์ต์ ๋น์ฉ ์ ์ฅ ํธ๋ฆฌ (Minimum Cost Spanning Tree)
- ์ฌ์ดํด์ด ์๋ ์ฐ๊ฒฐ๋ ๊ทธ๋ํ๋ผ๋ฉด ์ด๋ฅผ ์ ์ฅ ํธ๋ฆฌ(spanning tree)๋ผ๊ณ ํ๋ค.
- ์ต์ ๋น์ฉ ์ ์ฅํธ๋ฆฌ (Minimum Spanning Tree)๋ ๊ฐ์ค์น ๋ฌด๋ฐฉํฅ ๊ทธ๋ํ์์ ๋ชจ๋ ์ ์ ์ ์ฐ๊ฒฐํ ๋ ์ต์์ ๋น์ฉ์ผ๋ก ์ฐ๊ฒฐํ ์ ์๋ ๋ฐฉ๋ฒ์ ์ฐพ๋ ์๊ณ ๋ฆฌ์ฆ์ด๋ค.
- ์ต์๋น์ฉ์ ์ฅ ์๊ณ ๋ฆฌ์ฆ์๋ ํฌ๋ฃจ์ค์นผ ์๊ณ ๋ฆฌ์ฆ๊ณผ ํ๋ฆผ ์๊ณ ๋ฆฌ์ฆ์ด ์กด์ฌํ๋ค.
ํ์ ์๊ณ ๋ฆฌ์ฆ(Greedy algorithm)
- ํฌ๋ฃจ์ค์นผ ์๊ณ ๋ฆฌ์ฆ์ด๋ ํ๋ฆผ์๊ณ ๋ฆฌ์ฆ ๋ชจ๋ ํ์ ์๊ณ ๋ฆฌ์ฆ์ ํ ์ข ๋ฅ
- ํ์ ์๊ณ ๋ฆฌ์ฆ์ ๋งค ๋จ๊ณ๋ง๋ค ํญ์ ์ต์ ์ผ ๊ฒ ๊ฐ์ ์ ํ์ ํ๋ฉด ์ ์ฒด์ ์ผ๋ก๋ ์ต์ ์ ์ ํ์ ํ๊ฒ ๋๋ค๋ ์๊ณ ๋ฆฌ์ฆ์ด๋ค.
- ์ฆ, ์ฌ๋ฌ ๊ฒฝ์ฐ ์ค ํ๋๋ฅผ ๊ฒฐ์ ํด์ผ ํ ๋๋ง๋ค ๊ทธ ์๊ฐ์ ์ต์ ์ด๋ผ๊ณ ์๊ฐ๋๋ ๊ฒ์ ์ ํํด ๋๊ฐ๋ ๋ฐฉ์์ผ๋ก ์งํํ์ฌ ์ต์ข ์ ์ธ ํด๋ต์ ๋๋ฌํ๋ค.
- ํ์ ์๊ณ ๋ฆฌ์ฆ์ ์ ์ฉํ๋ ค๋ฉด ๋๊ฐ์ง ์ ์ฝ์กฐ๊ฑด์ ๋ง์กฑํด์ผ ์ฌ์ฉํ ์ ์๋ค. ์ด ๋๊ฐ์ง ์ ์ฝ ์กฐ๊ฑด์ ํ์ ํน์ฑ(greedy properties)๋ผ๊ณ ํ๋ค.
์ต์ ๋ถ๋ถ ๊ตฌ์กฐ(optional substructure) : ์ด๋ค ์ ์ฒด ๋ฌธ์ ์ ๋ํ ์ต์ ํด๋ ๋ถ๋ถ๋ฌธ์ ์ ๋ํ ์ต์ ํด๋ฅผ ํฌํจํ๊ณ ์๋ค.
ํ์ ์ ํ ํน์ฑ(greedy choice property) : ๋ถ๋ถ์ ์ผ๋ก ์ต์ ์ ์ ํ์ ๊ณ์ํ๋ฉด ์ ์ญ์ ์ผ๋ก ์ต์ ํด์ ๋๋ฌํ ์ ์์ต๋๋ค.
- ํ์ ์ ํ ํน์ฑ์ธ ์ปท ํ๋กํผํฐ(cut property)
- V(G) ์งํฉ์ ๊ณต์งํฉ์ด ์๋ ๋ ์งํฉ์ผ๋ก ๋๋ ๊ฒ
- Crossing edge : ์ปท์ผ๋ก ๋๋ ํ ์งํฉ์ ์ ์ ๊ณผ ๋ค๋ฅธ ์งํฉ์ ์ ์ ์ ์ฐ๊ฒฐ
- Cut property : Crossing edge ์ค ๊ฐ์ฅ ์์ ์์ง๋ก, MST์ ํ ์์ง๊ฐ ๋๋ค.
- Kruskal๊ณผ Prim ๋ชจ๋ Cut property ๊ธฐ๋ฐ์ผ๋ก ๋ง๋ค์ด์ก์ผ๋ฉฐ, ๋ค๋ฅธ ์ ์ ์ด๋ป๊ฒ ์๋ฅผ ๊ฒ์ธ๊ฐ์ ์ฐจ์ด์ด๋ค.
โ ์์์ ์์ ์ธ์ ํ ์ ์ ์ ์ฃผ๋ณ์ผ๋ก ์ปท์ ์ฐพ๋๋ค. crossing edge ์ค์์ ๊ฐ์ค์น๊ฐ ๊ฐ์ฅ ์์ ์์ง์ธ 2๋ฅผ ์ ํํ๋ค.
โก crossing edge ์ค์์ ๊ฐ์ค์น๊ฐ ๊ฐ์ฅ ์์ ์์ง์ธ 7์ ์ ํํ๋ค. ์ ํ๋ crossing edge๋ฅผ ํฌํจํ์ง ์๋ ์ปท์ ์ฐพ๋๋ค.
โข crossing edge ์ค์์ ๊ฐ์ค์น๊ฐ ๊ฐ์ฅ ์์ ์์ง์ธ 5๋ฅผ ์ ํํ๋ค.
โฃ crossing edge ์ค์์ ๊ฐ์ค์น๊ฐ ๊ฐ์ฅ ์์ ์์ง์ธ 4๋ฅผ ์ ํํ๋ค. โค
ํฌ๋ฃจ์ค์นผ ์๊ณ ๋ฆฌ์ฆ(Kruskal algorithm)
- ๊ฐ์ฅ ์ ์ ๋น์ฉ์ผ๋ก ๋ชจ๋ ๋ ธ๋๋ฅผ ์ฐ๊ฒฐํ๊ธฐ ์ํด ์ฌ์ฉํ๋ ์๊ณ ๋ฆฌ์ฆ
- ์ฌ๋ฌ ๊ฐ์ ๋์๊ฐ ์์ ๋, ๊ฐ ๋์ ์ฌ์ด๋ฅผ ์ฐ๊ฒฐํ๋ ๋๋ก๋ฅผ ์ต์ํ์ ๋น์ฉ์ผ๋ก ๊ฑด์คํ๊ณ ํ ๋ ์ ์ฉ๋๋ ์๊ณ ๋ฆฌ์ฆ
ํฌ๋ฃจ์ค์นผ ์๊ณ ๋ฆฌ์ฆ ๋์
- ๊ทธ๋ํ์ ๊ฐ์ ๋ค์ ๊ฐ์ค์น์ ์ค๋ฆ์ฐจ์์ผ๋ก ์ ๋ ฌํ๋ค.
- ์ ๋ ฌ๋ ๊ฐ์ ๋ฆฌ์คํธ์์ ์์๋๋ก ์ฌ์ดํด์ ํ์ฑํ์ง ์๋ ๊ฐ์ ์ ์ ํํ๋ค. ์ฆ, ๊ฐ์ฅ ๋ฎ์ ๊ฐ์ค์น๋ฅผ ๋จผ์ ์ ํํ๋ค.
- ์ฌ์ดํด์ ํ์ฑํ๋ ๊ฐ์ ์ ์ ์ธํ๋ค.
- ํด๋น ๊ฐ์ ์ ํ์ฌ์ MST(์ต์ ๋น์ฉ ์ ์ฅ ํธ๋ฆฌ)์ ์งํฉ์ ์ถ๊ฐํ๋ค.
์ฌ์ดํด์ด ํ์ฑ๋๋์ง ์ด๋ป๊ฒ ํ์ธํ์ง?
- ์๋ก ๋ค๋ฅธ ๋ ์งํฉ์ ๋ณํฉํ๋ Union ์ฐ์ฐ, ์งํฉ ์์๊ฐ ์ด๋ค ์งํฉ์ ์ํด์๋์ง ์ฐพ๋ Find ์ฐ์ฐ์ ํ์ฉํ๋ค.
- parent ๋ฐฐ์ด์ ๊ฐ ์ ์ ์ root node(๋ถ๋ชจ)๋ฅผ ํํํ ๋ฐฐ์ด
- ๊ฐ์ ์ ํ ์ , ์ด๊ธฐ์๋ ์๊ธฐ ์์ ์ด ๋ฃจํธ ๋ ธ๋๊ฐ ๋๊ฒ ์ด๊ธฐํ ๋์ด ์๋ ์ํ (parent[i] = i)
- find ์ฐ์ฐ์ ์ด์ฉํ์ฌ ์์ง(0,1)์ ์ ์ 0๊ณผ ์ ์ 1๊ฐ ์ฌ์ดํด์ด ํ์ฑํ๋์ง ํ์ธํ๋ค.
- ์ฌ์ดํด์ด ํ์ฑ๋์ง ์์๋ค๋ฉด union์ฐ์ฐ์ ์ด์ฉํ์ฌ ๋์งํฉ์ ํ๋๋ก ํฉ์ณ์ค๋ค.
ํฌ๋ฃจ์ค์นผ ์๊ณ ๋ฆฌ์ฆ ๋์๊ณผ์
from disjoint_set import DisjointSet
class Edge:
# ์์์ ์ , ๋์ ์ , ๊ฐ์ค์น ์ด๊ธฐํ
def __init__(self, u, v, w):
self.u=u
self.v=v
self.w=w
class Graph:
def __init__(self, vertex_num):
self.adj_list=[[] for _ in range(vertex_num)]
self.edge_list=[]
self.vertex_num=vertex_num
def add_edge(self, u, v, weight):
self.adj_list[u].append(v)
self.adj_list[v].append(u)
self.edge_list.append(Edge(u, v, weight))
def MST_kruskal(self):
mst=Graph(self.vertex_num) ## ์ต์ข
์ ์ผ๋ก ๋ง๋ค ์ต์๋น์ฉ ์ ์ฅ ํธ๋ฆฌ
ds=DisjointSet(self.vertex_num) ## ์ฌ์ดํด ํ์ฑ ๊ฒ์ฌ๋ฅผ ํ ์ ์ ์งํฉ
self.edge_list.sort(key=lambda e: e.w) ## ๊ฐ์ค์น์ ๋ฐ๋ผ ์์ง ์ ๋ ฌ
mst_edge_num=0
edge_idx=0 ## ์ ๋ ฌ๋ ์์ง ๋ฆฌ์คํธ์์ ์ธ๋ฑ์ค
while mst_edge_num < self.vertex_num-1:
edge=self.edge_list[edge_idx]
if ds.collapsing_find(edge.u) != ds.collapsing_find(edge.v):
## find ์ฐ์ฐ์ ํ์ฉํ์ฌ ์งํฉ ์์๊ฐ ์ด๋ค ์งํฉ์ ์ํด์๋์ง ํ์ธ
mst.add_edge(edge.u, edge.v, edge.w)
ds.weighted_union(ds.collapsing_find(edge.u), ds.collapsing_find(edge.v))
# union ์ฐ์ฐ์ ํ์ฉํ์ฌ ๋์งํฉ ๋ณํฉ
mst_edge_num+=1
edge_idx+=1
return mst
def print_edges(self):
for edge in self.edge_list:
print("({}, {}) : {}".format(edge.u, edge.v, edge.w))
if __name__=="__main__":
g=Graph(6)
g.add_edge(0, 1, 10)
g.add_edge(0, 2, 2)
g.add_edge(0, 3, 8)
g.add_edge(1, 2, 5)
g.add_edge(1, 4, 12)
g.add_edge(2, 3, 7)
g.add_edge(2, 4, 17)
g.add_edge(3, 4, 4)
g.add_edge(3, 5, 14)
mst=g.MST_kruskal()
mst.print_edges()
class DisjointSet:
def __init__(self, vnum):
self.parent=[-1 for _ in range(vnum)]
def simple_find(self, i):
while self.parent[i] >= 0:
i=self.parent[i]
return i
def simple_union(self, i, j):
self.parent[i]=j
def collapsing_find(self, i):
root=trail=lead=None
# ๋ฃจํธ๋ฅผ ์ฐพ๋๋ค.
root=i
while self.parent[root] >= 0:
root=self.parent[root]
# ๋ชจ๋ ๋
ธ๋๋ฅผ ๋ฃจํธ์ ์์์ผ๋ก ๋ง๋ ๋ค
trail=i
while trail != root:
lead=self.parent[trail]
self.parent[trail]=root
trail=lead
return root
def weighted_union(self, i, j):
"""
paremeters i, j must be roots!
if size[i] < size[j] then parent[i]=j
"""
#abs(parent[i])=size[i]
#temp_cnt๋ ์์์ด๊ณ size[i] + size[j]
temp_cnt=self.parent[i]+self.parent[j]
# ๊ฐ ์งํฉ์ด ๊ฐ์ง ๋
ธ๋ ๊ฐ์๋ฅผ ๋น๊ตํ์ฌ ๋
ธ๋๊ฐ์๊ฐ ๋ง์ ์งํฉ์ ๋
ธ๋ ๊ฐ์๊ฐ ์ ์ ์งํฉ์ ์์์ผ๋ก ๋ณํฉ
#size[i] < size[j]
if self.parent[i] > self.parent[j]:
self.parent[i]=j
self.parent[j]=temp_cnt
#size[i] > size[j]
else:
self.parent[j]=i
self.parent[i]=temp_cnt
if __name__=="__main__":
ds=DisjointSet(5)
# ds.simple_union(1, 2)
# ds.simple_union(4, 2)
# ds.simple_union(3, 0)
# print(ds.parent)
# for i in range(5):
# print("parent[{}] : {}".format(i, ds.simple_find(i)))
ds=DisjointSet(5)
ds.parent[2]=-5
ds.parent[4]=2
ds.parent[0]=4
ds.parent[1]=0
ds.parent[3]=1
print(ds.parent)
print("the root is {}".format(ds.collapsing_find(3)))
print(ds.parent)
ํ๋ฆผ ์๊ณ ๋ฆฌ์ฆ
- ์์ ์ ์ ์ ์ ํํ ํ, ์ ์ ์ ์ธ์ ํ ๊ฐ์ ์ค ์ต์ ๊ฐ์ ์ผ๋ก ์ฐ๊ฒฐ๋ ์ ์ ์ ์ ํํ๊ณ , ํด๋น ์ ์ ์์ ๋ค์ ์ต์ ๊ฐ์ ์ผ๋ก ์ฐ๊ฒฐ๋ ์ ์ ์ ์ ํํ๋ ๋ฐฉ์์ผ๋ก ์ต์ ์ ์ฅ ํธ๋ฆฌ๋ฅผ ํ์ฅํด๊ฐ๋ ๋ฐฉ์
Kruskalโs algorithm ๊ณผ Primโs algorithm ๋น๊ต
๋๋ค, ํ์ ์๊ณ ๋ฆฌ์ฆ์ ๊ธฐ์ด๋ก ํ๊ณ ์์ (๋น์ฅ ๋ ์์ ์ต์ ๋น์ฉ์ ์ ํํด์, ๊ฒฐ๊ณผ์ ์ผ๋ก ์ต์ ์ ์๋ฃจ์ ์ ์ฐพ์)
Kruskalโs algorithm์ ๊ฐ์ฅ ๊ฐ์ค์น๊ฐ ์์ ๊ฐ์ ๋ถํฐ ์ ํํ๋ฉด์ MST๋ฅผ ๊ตฌํจ
Primโs algorithm์ ํน์ ์ ์ ์์ ์์, ํด๋น ์ ์ ์ ์ฐ๊ฒฐ๋ ๊ฐ์ฅ ๊ฐ์ค์น๊ฐ ์์ ๊ฐ์ ์ ์ ํ, ๊ฐ์ ์ผ๋ก ์ฐ๊ฒฐ๋ ์ ์ ๋ค์ ์ฐ๊ฒฐ๋ ๊ฐ์ ์ค์์ ๊ฐ์ฅ ๊ฐ์ค์น๊ฐ ์์ ๊ฐ์ ์ ํํ๋ ๋ฐฉ์์ผ๋ก MST๋ฅผ ๊ตฌํจ
ํ๋ฆผ ์๊ณ ๋ฆฌ์ฆ ๋์๊ณผ์
import math
from min_heap import Element, MinHeap
class Edge:
def __init__(self, u, v, w):
self.u=u
self.v=v
self.w=w
class Graph:
def __init__(self, vertex_num):
self.adj_list=[[] for _ in range(vertex_num)]
self.edge_list=[]
self.vertex_num=vertex_num
def add_edge(self, u, v, w):
# (์ ์ , ์์ง์ ๊ฐ์ค์น)๋ฅผ ์ธ์ ๋ฆฌ์คํธ์ ์ถ๊ฐ
self.adj_list[u].append((v, w))
self.adj_list[v].append((u, w))
self.edge_list.append(Edge(u, v, w))
def MST_prim(self):
mst=Graph(self.vertex_num)
w_list=[math.inf for _ in range(self.vertex_num)]
# w_list = [inf,inf,inf,inf,inf,inf]
TV=set()
h=MinHeap()
# Element ๊ฐ์ฒด: ๊ฐ์ค์น๋ฅผ ํค๋ก ์ฌ์ฉํ๋ค.
for i in range(1, self.vertex_num):
h.push(Element(i, math.inf, None))
w_list[0]=0
h.push(Element(0, 0, None))
while not h.is_empty():
elem_v=h.pop()
v=elem_v.v
w=elem_v.w
_from=elem_v._from
TV.add(v)
if _from != None:
mst.add_edge(v, _from, w)
adj_v=self.adj_list[v]
# ๋
ธ๋ 0์ผ ๊ฒฝ์ฐ
# adj_v = [(1,10),(2,2),(3,8)]
for u, w_u_v in adj_v:
if u not in TV and w_u_v < w_list[u]:
# TV ์ด์ ์ ๋
ธ๋๋ฅผ ๋ฐฉ๋ฌธํ๋์ง ์ฒดํฌ ํ๊ธฐ ์ํ ์ฉ๋
w_list[u]=w_u_v
h.decrease_weight(Element(u, w_u_v, v))
# u : ๋์ ์ , w_u_v : ๊ฐ์ค์น, v : ์์์ ์
'''
# ์๋ก์ด ๊ฐ์ค์น๊ฐ ๊ธฐ์กด ๊ฐ์ค์น๋ณด๋ค ์๋ค๋ฉด decrease_weight ์ฐ์ฐ
def decrease_weight(self, new_elem):
cur=self.pos[new_elem.v]
while cur!= 1 and new_elem.w < self.arr[self.parent(cur)].w:
self.arr[cur]=self.arr[self.parent(cur)]
self.pos[self.arr[cur].v]=cur
cur=self.parent(cur)
self.arr[cur]=new_elem
self.pos[new_elem.v]=cur
'''
'''
๊ฒฐ๊ณผ๊ฐ
(2, 0) : 2
(1, 2) : 5
(3, 2) : 7
(4, 3) : 4
(5, 3) : 14
'''
return mst
def print_edges(self):
for edge in self.edge_list:
print("({}, {}) : {}".format(edge.u, edge.v, edge.w))
if __name__=="__main__":
g=Graph(6)
g.add_edge(0, 1, 10)
g.add_edge(0, 2, 2)
g.add_edge(0, 3, 8)
g.add_edge(1, 2, 5)
g.add_edge(1, 4, 12)
g.add_edge(2, 3, 7)
g.add_edge(2, 4, 17)
g.add_edge(3, 4, 4)
g.add_edge(3, 5, 14)
mst=g.MST_prim()
mst.print_edges()