아래 튜토리얼에서는 그래프에서의 매칭(matching), 특히 최대 매칭(maximum cardinality matching) 및 최대 가중치 매칭(maximum weighted matching)을 다룹니다. 이는 Boost C++ Libraries(C++에서 제공되는 고성능 그래프 라이브러리) 문서 일부로, graph-tool 등 파이썬 라이브러리에서도 이 개념을 차용하여 구현합니다.
튜토리얼에서는 다음을 중점적으로 설명합니다:
매칭(Matching):
최대 매칭(Maximum Cardinality Matching, MCM):
최대 매칭 vs. 극대 매칭(Maximal Matching):
증대 경로(augmenting path)
matched, unmatched alternating) 경로. 기본 구조:
문제: 일반 그래프에서 증대 경로 찾기 시, “블로섬(Blossom)” 등 복잡한 구조가 있음.
edmonds_maximum_cardinality_matching:
checked_edmonds_maximum_cardinality_matching:
라이브러리 내부에는
matching<Graph, MateMap, VertexIndexMap,
AugmentingPathFinder, InitialMatchingFinder, MatchingVerifier>
(const Graph& g, MateMap mate, VertexIndexMap vm)
와 같은 제네릭 함수가 있으며,
edmonds_maximum_cardinality_matching 등은 이를 적절히 특수화해서 사용하는 inlined 버전.
예)
edmonds_maximum_cardinality_matching = edmonds_augmenting_path_finder + extra_greedy_matching + no_matching_verifier checked_edmonds_maximum_cardinality_matching = edmonds_augmenting_path_finder + extra_greedy_matching + maximum_cardinality_matching_verifier.에드몬즈 최대 매칭(cardinality)
에드몬즈 최대 가중치 매칭(weighted)
mateMap에 매칭 결과로 각 정점의 짝을 저장:
mate[v] = w, mate[w] = v if ((v,w))가 매칭에 포함.#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/max_cardinality_matching.hpp>
#include <iostream>
int main() {
using namespace boost;
typedef adjacency_list<vecS, vecS, undirectedS> Graph;
Graph g(6);
// 예시: 간선 추가
add_edge(0,1,g);
add_edge(1,2,g);
add_edge(2,3,g);
add_edge(2,4,g);
add_edge(4,5,g);
// mate 배열(정점→정점)
std::vector<graph_traits<Graph>::vertex_descriptor> mate(num_vertices(g),
graph_traits<Graph>::null_vertex());
// 최대 매칭
edmonds_maximum_cardinality_matching(g,
make_iterator_property_map(mate.begin(), get(vertex_index,g)));
// mate 정보 출력
for (int v=0; v<6; v++){
if (mate[v] == graph_traits<Graph>::null_vertex())
std::cout << v << " is unmatched\n";
else
std::cout << v << " matched with " << mate[v] << "\n";
}
return 0;
}
이와 비슷한 방식으로 가중치가 있을 경우에는 maximum_weighted_matching() 함수에 edge_weight_t 프로퍼티가 있어야 하며, 그에 따라 mateMap을 구할 수 있습니다.
edmonds_maximum_cardinality_matching + optional ‘검증(checked_)’maximum_weighted_matching / brute_force_maximum_weighted_matching이로써 Edmonds 알고리즘(Blossom) 계열을 이용해 일반 그래프에서 최대 매칭/가중치 매칭을 효율적으로 구할 수 있으며, graph-tool 등 다양한 그래프 라이브러리에서도 유사한 구조로 제공되는 함수를 사용할 수 있습니다.
아래 튜토리얼은 graph-tool 라이브러리에서 제공하는 max_cardinality_matching() 함수를 중심으로, 최대 매칭(최대 수의 간선으로 구성된 매칭) 혹은 “가중치가 주어졌을 경우 최대 가중치 매칭”을 어떻게 구하는지 설명합니다. 또한, 다양한 파라미터(bipartite, init_match, weight, heuristic, edges, 등)가 실제로 무엇을 의미하고, 어떤 상황에서 쓰이는지, 그리고 실용적인 예제 코드를 통해 알아봅니다.
튜토리얼의 흐름:
graph-tool 라이브러리의 max_cardinality_matching() 함수는, 기본적으로 최대 (수)의 매칭을 찾지만, weight 파라미터가 주어지면 최대 가중치 + 최대 수를 동시에 만족하는 매칭을 찾습니다. (동점일 때는 최대 간선을 고르는지, 최대 가중을 우선하는지 등에 대한 구현 세부는 Boost 라이브러리 문서 참고)
max_cardinality_matching() 함수 시그니처와 파라미터match = graph_tool.topology.max_cardinality_matching(
g,
weight=None,
bipartite=False,
init_match='extra_greedy',
heuristic=False,
minimize=False,
edges=False,
brute_force=False
)
g: 사용될 그래프(Graph 객체).
weight=None:
None이면, 간단히 최대 간선 수(카디널리티) 매칭을 찾음. EdgePropertyMap 객체를 넣으면, “최대 매칭이면서 동시에 가중치 합을 최대(또는 최소)로” 하는 매칭을 찾음.bipartite=False:
True로 설정 or 정점 속성(VertexPropertyMap)으로 이분 분할을 지정. init_match='extra_greedy':
'empty', 'greedy', 'extra_greedy' 중 선택 가능. weight=None), heuristic=False일 때만 유효.heuristic=False:
True면, 빠른(선형 시간) 휴리스틱이 수행되어 근사 매칭(정확히 최대가 아닐 수도!)을 구함. minimize=False:
weight가 주어졌을 때, 최대 혹은 최소 가중치 매칭을 결정하는 플래그. True면 “최소 가중치 매칭”, False면 “최대 가중치 매칭”.edges=False:
VertexPropertyMap. True라면, 매칭에 속한 간선 여부를 표시하는 EdgePropertyMap(bool)이 반환.brute_force=False:
weight가 있고, heuristic=False인 경우, True로 두면 느린 무차별 대입(브루트 포스) 방법을 씀(아주 작은 그래프 테스트용).복잡도:
- 일반적으로 (O(m \cdot n \cdot \alpha(m,n))) (단, (\alpha)는 매우 느리게 증가하는 함수)
- 가중치 매칭의 경우 (O(n^3)).
- 휴리스틱(
heuristic=True) 시, (O(n+m)) 선형 시간.- 브루트포스(
brute_force=True)는 지수 시간.
무가중 그래프 (기본)
가중치 그래프 (weight != None)
이분 그래프 (bipartite=True or bipartite=vertex_prop)
Heuristic 모드 (heuristic=True)
리턴 형태
edges=False: VertexPropertyMap (각 정점이 매칭된 상대 정점 or null_vertex) edges=True: EdgePropertyMap(bool) (매칭에 속한 간선만 True)다음 예시는 graph-tool 라이브러리에서의 max_cardinality_matching() 함수를 사용해본 스크립트입니다. (단, 실제 사용 전 import graph_tool.all as gt, import numpy as np 같은 import가 필요합니다.)
import graph_tool.all as gt
import numpy as np
def example_unweighted_matching():
# 1) 임의 그래프 생성(Price network 300노드) -> 무방향 View
g_orig = gt.price_network(300)
g = gt.GraphView(g_orig, directed=False) # 무방향으로 보기
# 2) max_cardinality_matching 실행 (edges=True로 간선 property map 리턴)
matching_eprop = gt.max_cardinality_matching(g, edges=True)
# 매칭된 간선 수 카운트
num_matched_edges = sum(1 for e in g.edges() if matching_eprop[e])
print(f"Found matching with {num_matched_edges} edges.")
# (A) EdgePropertyMap(float) 생성
penwidth_ep = g.new_edge_property("float")
# matching_eprop[a] -> True/False이므로, True면 3.0, False면 1.0으로 설정
# (원한다면 더 복잡한 수식 가능)
for e in g.edges():
if matching_eprop[e]:
penwidth_ep[e] = 3.0 # 매칭 간선 굵게
else:
penwidth_ep[e] = 1.0 # 비매칭 간선 얇게
# 3) 그래프 시각화
pos = gt.sfdp_layout(g)
gt.graph_draw(
g,
pos=pos,
edge_color=matching_eprop, # True->기본 colormap을 통해 다른색
edge_pen_width=penwidth_ep, # EdgePropertyMap(float)
vertex_fill_color="lightgrey",
output="max_match_example1.pdf"
)
if __name__=="__main__":
example_unweighted_matching()
주요 포인트:
edges=True로 설정했으므로, 반환값이 EdgePropertyMap.matching_eprop[e] == True 면 해당 간선이 매칭에 속함.import graph_tool.all as gt
import numpy as np
def example_weighted_matching():
# 1) 무작위 그래프 생성
g = gt.random_graph(50, lambda: np.random.poisson(3), directed=False)
# 2) 간선 weight 속성 만들기
weight_ep = g.new_edge_property("double")
import random
for e in g.edges():
weight_ep[e] = random.uniform(1,10)
# 3) 최대 가중치 매칭 구하기
match_vprop = gt.max_cardinality_matching(
g,
weight=weight_ep,
minimize=False, # 최대화
edges=False # 정점기준 매칭정보 반환
)
# 4) 매칭된 정점 수
# match_vprop[v] 가 None이 아니면 매칭된 것
matched_count = sum(1 for v in g.vertices() if match_vprop[v] is not None)
print(f"Matched {matched_count} vertices (out of {g.num_vertices()}).")
# 5) 매칭된 에지들의 weight 합계
total_weight = 0
used = set()
for v in g.vertices():
mate = match_vprop[v]
if mate is not None:
# v와 mate 사이의 edge 찾기:
e = None
for e_out in v.out_edges():
# 무방향 그래프이므로 source/target 관계 유의
if e_out.target() == mate or e_out.source() == mate:
e = e_out
break
if e is not None:
# 중복 방지
if e not in used:
total_weight += weight_ep[e]
used.add(e)
print(f"Total matching weight = {total_weight}")
if __name__=="__main__":
example_weighted_matching()
이분 그래프(bipartite)일 경우, 조금 더 효율적인 알고리즘(예: Hopkroft-Karp 유사)이 적용될 수 있습니다. 이때, bipartite=True 또는 bipartite=vertex_color_prop 같이 설정합니다.
def example_bipartite():
# bipartite 예시: 두 그룹(A,B)
# - A = 0~19, B=20~39
# - 임의 edges: A->B 만 존재
g = gt.Graph(directed=False)
g.add_vertex(40)
bip_prop = g.new_vertex_property("bool") # True/False로 구분
for v in g.vertices():
idx = int(v)
if idx<20:
bip_prop[v] = True
else:
bip_prop[v] = False
import random
for i in range(20):
for j in range(20,40):
# p=0.2 확률로 간선 생성
if random.random()<0.2:
g.add_edge(g.vertex(i), g.vertex(j))
# 이제 bipartite 모드
match_ep = gt.max_cardinality_matching(
g,
bipartite=bip_prop,
edges=True
)
matched_edges = sum(1 for e in g.edges() if match_ep[e])
print(f"Bipartite matching edges: {matched_edges}")
init_match 옵션 ('empty', 'greedy', 'extra_greedy'): weight가 없고, heuristic=False일 때만 유효. 'empty'는 초기 매칭 없이 시작. 'greedy'는 에지 순회하며 갈 수 있으면 매칭에 추가. 'extra_greedy'는 간선을 “정점 차수” 기반 정렬 후 그리디 식으로 매칭해 초기화 -> 품질이 나아질 수 있음.heuristic=True weight != None이면, “간선 개수 최대” AND “가중치 합 최대”를 동시에 달성하려 시도(Boost 문서상). minimize=True이면 “가중치 합 최소” 쪽으로.bipartite edges=True일 경우: EdgePropertyMap (bool). True=매칭에 포함된 간선. edges=False일 경우: VertexPropertyMap (각 정점이 매칭된 상대 정점 or gt.INVALID).max_cardinality_matching()는 그래프에서 최대 매칭(혹은 최대/최소 가중치 매칭)을 손쉽게 구하는 강력한 함수입니다.
실제 도시 교통 네트워크, 대규모 bipartite matching(예: job-person 매칭), 병렬 분산처리 로드매칭, 공유자원 할당 등 다양한 응용에서 활용 가능하니, 상황에 맞춰 파라미터를 조정하면 됩니다.
이상으로, max_cardinality_matching() 함수의 주요 개념과 사용 예시를 살펴봤습니다.
아래 내용은 Michael Luby의 논문 "A Simple Parallel Algorithm for the Maximal Independent Set Problem" (이하 MIS 문제) 관련 일부 발췌입니다. 여기서 소개된 알고리즘들은 그래프의 최대 독립 집합(Maximal Independent Set, 이하 MIS)을 빠른 병렬 알고리즘으로 구하는 방법을 다룹니다. 다음 설명에서는 해당 논문의 핵심 수식, 개념, 알고리즘 구조, 분석 원리 등을 최대한 상세하게 정리해보겠습니다.
Luby의 핵심 공헌:
1. 무작위(Monte Carlo) 기반의 지역적(local) 알고리즘 제시
2. 해당 무작위 알고리즘을 쌍wise 독립(pairwise independence) 만으로도 충분하다는 점을 보여, 보다 적은 난수로도 동일한 분석이 가능함을 증명
3. 이러한 기법을 통해 최종적으로 결정적(deterministic) NC(^2) 알고리즘 구성.
논문에서 제시된 1차 알고리즘(가장 간단 버전)은 다음과 같이 요약된다 (구체화):
Luby 논문(5장)에서 정점 개수 (n)에 대해 (O(n^2))개의 이진 벡터만 택하는 “작은 샘플 공간(small sample space)”을 구성하고, 그 중에서 “쌍wise 독립” 사건을 만족하는 방식으로 난수를 생성한다:
요약하면:
정리하자면, Luby 최종 결정적 알고리즘 개요:
(논문에서는 조금 더 정교한 병렬화를 통해 프로세서 수를 줄일 수 있다고도 언급.)
Michael Luby 논문에서 제안된 단순 병렬 알고리즘의 핵심은 다음과 같다:
기본 구조:
상호 독립(mutual independence)이 아니어도, 쌍wise 독립(pairwise)만 보장되면 동일 분석이 유지됨을 보임.
쌍wise 독립 난수를 만드는 기법:
결정적 알고리즘:
이를 통해, MIS 문제를 NC(^2) 클래스에서 풀 수 있음을 보인다. 이 결과는 기존에 Karp&Wigderson 등이 (O((\log n)^2)) 병렬 시간에 비해 알고리즘 구조가 단순하고, 쌍wise 독립성만 필요하다는 점에서 이론적·실용적 의의를 지닌다.
(이상, Luby(1985) 논문 내용을 정리.)
아래는 graph-tool 라이브러리에 포함된 max_independent_vertex_set() 함수를 사용하여 그래프에서 맥시멀 독립 정점 집합(MIVS, Maximal Independent Vertex Set)을 효율적으로 구하는 방법을 설명합니다. 이 함수는 Michael Luby가 제안한 유명한 병렬 MIS(Maximal Independent Set) 알고리즘 아이디어 ([mivs-luby])를 기반으로 구현되어 있으며, 시간복잡도는 그래프 (G)의 정점 수를 (n), 간선 수를 (m)라 할 때 (O(m)) 또는 (O(n + m)) 수준으로 동작하도록 되어 있습니다. 아래 튜토리얼에서는 이 함수의 내부 작동원리, 파라미터 옵션, 활용 예시 파이썬 코드 등을 단계별로 정성껏 소개해봅니다.
독립 정점 집합
맥시멀 독립 집합(MIVS)
맥시멀 독립 집합의 활용
graph_tool.topology.max_independent_vertex_set()도 유사한 아이디어로 구현.max_independent_vertex_set() 함수 설명mivs_property_map = gt.max_independent_vertex_set(
g,
high_deg=False,
mivs=None
)
인자 설명:
g: 사용될 그래프 객체(Graph 또는 GraphView).high_deg (bool, default: False):True면 높은 차수(degree)의 정점을 먼저 집합에 포함하려 시도.False면 낮은 차수 정점을 먼저 포함하려 시도.mivs (VertexPropertyMap, 옵션):반환:
mivs: Boolean 타입의 정점 property map. 각 정점에 대해 True이면 그 정점이 맥시멀 독립 집합에 속함.이는 Luby ([mivs-luby])가 제안한 병렬 알고리즘과 유사한 구조입니다. graph-tool에서는 이 과정을 단일 스레드/순차로도 충분히 빠르게 구현하지만, 원리적으로는 병렬성(EREW PRAM 분석 시 (\log n) 라운드 등)도 활용 가능합니다.
아래는 max_independent_vertex_set() 함수를 간단히 써보는 예시입니다.
import graph_tool.all as gt
def example_mivs():
# 1) 예시 그래프 생성: Price network 300개 노드 (방향성 제거)
g_orig = gt.price_network(300)
g = gt.GraphView(g_orig, directed=False) # 무방향 그래프로 보기
# 2) 맥시멀 독립 정점 집합 구하기
# high_deg = False -> 낮은 차수 정점 우선 버전
mivs_map = gt.max_independent_vertex_set(g, high_deg=False)
# mivs_map[v] == True 면, v가 독립 집합에 속함
mivs_size = sum(1 for v in g.vertices() if mivs_map[v])
print(f"Found a maximal independent set of size {mivs_size}")
# 3) 시각화
# - 독립 집합에 속한 정점은 파랑 / 속하지 않은 정점은 회색
# - edges 그대로
pos = gt.sfdp_layout(g)
# Boolean값 -> color로 매핑
# True -> 노란색, False -> 회색
color_map = g.new_vertex_property("string")
for v in g.vertices():
color_map[v] = "yellow" if mivs_map[v] else "gray"
gt.graph_draw(
g,
pos=pos,
vertex_fill_color=color_map,
vertex_color=color_map,
output="mivs_example.pdf"
)
if __name__ == "__main__":
example_mivs()
price_network(300) : Price model 그래프(= 무방향 Preferential Attachment 유사).GraphView로 directed=False 하여 무방향으로 취급.gt.max_independent_vertex_set(g, high_deg=False) 호출 → Boolean vertex map 반환.mivs_size = sum(mivs_map[v] for v in g.vertices())high_deg=True vs Falsehigh_deg=True:high_deg=False (기본값):일반적으로 두 옵션 중 큰 차이가 없을 수도 있으니, 필요에 따라 테스트를 권장합니다.
graph_tool.topology.max_independent_vertex_set() 함수는 단일 함수 호출로 그래프의 맥시멀 독립 정점 집합을 구할 수 있는 매우 편리한 기능입니다. 내부적으로 Luby 방식(혹은 유사한 방식)의 무작위/순차 알고리즘이 최적화되어 있고, high_deg 같은 옵션으로 약간의 변형을 줄 수 있습니다. 결과물은 Boolean형 vertex property map이므로, 이를 활용하여 시각화나 후처리를 손쉽게 할 수 있습니다.
맥시멀 독립 집합은 그래프 Coloring, Matching, 분산 알고리즘(네트워크 토폴로지 제어), 인공신경망(에너지 최소화), 스케줄링 등의 여러 문제에서 핵심 도구로 쓰이므로, 본 함수를 유용하게 쓸 수 있습니다.
이상으로 max_independent_vertex_set()의 사용법과 예시 코드를 마칩니다. 필요시 더 다양한 그래프에 적용하며 성능/결과를 살펴보시길 권장합니다.