お久しぶりです。まぁじです。
Juliaねこ
まえおき
今回はDataStructures.jlにあるDisjointSetsについてです。
競技プログラマー ならUnion-Find法 の方が聞き覚えがあると思います。
日本語でいうと「素集合データ構造」で、木構造 を用いて
が高速にできます。
詳しいアルゴリズム や応用はこのスライド やこのページ を読むとわかりやすいと思います。
実行環境
Juliaのインストールは既に済んでいるものとします。
ちなみに使ってるエディタはVSCode で、Julia Extensionを入れて使ってます。
julia> versioninfo()
Julia Version 1.5.3
Commit 788 b2c77c1 (2020 - 11 - 09 13 : 37 UTC)
Platform Info:
OS: Windows (x86_64- w64- mingw32)
CPU: Intel(R) Core(TM) i3- 8130 U CPU @ 2.20 GHz
WORD_SIZE: 64
LIBM: libopenlibm
LLVM: libLLVM- 9.0.1 (ORCJIT, skylake)
自力実装したUnionFind法
あまり複雑なアルゴリズム ではないので、20行ほどで書けます。
名前
説明
par
各要素の親を表す(初期値は自分が親)。
siz
各要素を親とするグループの大きさを表す(初期値は1)。
root(x)
xの親を探す。
unite!(x,y)
xのノードとyのノードを結合。小さい方が親 となる。
issame(x,y)
xのノードとyのノードの親が同じかを返す。
※ちなみに、慣例上Juliaにおいて破壊的処理は!
を付ける ことになっているので、結合処理はunite!
になってます。
あとはこれをmain関数内にペタリして処理をするだけです。
par= Vector {Int }([i for i= 1 : n])
siz= ones(Int ,n)
function root (x:: Int )
index= par[x]
while par[index]!= index
index= par[index]
end
return index
end
function unite! (x:: Int ,y:: Int )
rx,ry= root(x),root(y)
if rx== ry
return 0
elseif rx> ry
rx,ry= ry,rx
end
par[ry]= rx
siz[rx]+= siz[ry]
end
function issame (x:: Int ,y:: Int )
isequal(root(x),root(y))
end
例:ACL Beginner Contest 「C - Connect Cities」
(Submission #20614886 - ACL Beginner Contest )
自力実装の利点・欠点
DataStructures.jlを使うよりもパッケージの読み込み時間がないので早い
このコード内ですべて完結しているのでバグの原因を見つけやすい
コードが比較的長くなる
サイズMのUnion-Find木を作らなければならないところを、コピペミスしてサイズNでWA (実体験)
IntDisjointSetsを使ったUnionFind法
DataStructures.jlのインストール
DataStructures.jlがインストールされていない場合、先にPkgを開いてインストールしてください。
コマンド入力時に]
を押す とPkgのコマンドを入力できます。
(@v1 .5 ) Pkg> add "DataStructures"
(Int)DisjointSetsの説明
DataStructures.jl内にはDisjointSetsとIntDisjointSetsが存在します。
DisjointSetsはIntDisjointSetsのラッパーであり、辞書を用いてマッピング しているのでChar型とか数字以外でもUnionFindができるらしいです(必要か?)。
※d=DisjointSets(1:N)
と宣言することで同様に使えます。
名前
説明
IntDisjointSets(N)
要素数 NのUnionFind木を宣言。
union!(d,x,y)
xのノードとyのノードを結合。xが親になる。
root_union!(d,x,y)
xのノードとyのノードのそれぞれ親を結合。
find_root!(d,x)
xのノードの親を返す。
in_same_set(d,x,y)
xのノードとyのノードが同じグループにあるかを返す。
d.ngroups
木構造 内のグループ数を返す。
num_groups(d)
おそらくd.ngroupsと同様。
using DataStructures
uf= IntDisjointSets(5 )
union!(uf,2 ,3 )
@show uf
root_union!(uf,1 ,3 )
@show uf
@show find_root!(uf,3 )
@show in_same_set(uf,1 ,4 )
@show uf.ngroups
@show num_groups(uf)
uf = IntDisjointSets {Int64 }([1 , 2 , 2 , 4 , 5 ], [0 , 1 , 0 , 0 , 0 ], 4 )
uf = IntDisjointSets {Int64 }([1 , 2 , 1 , 4 , 5 ], [1 , 1 , 0 , 0 , 0 ], 3 )
find_root!(uf, 3 ) = 1
in_same_set(uf, 1 , 4 ) = false
uf.ngroups = 3
num_groups(uf) = 3
IntDisjointSetsを用いた提出例
https://atcoder.jp/contests/abl/submissions/20614444
DisjointSetsを用いた提出例(こちらではngroupsは使えないっぽいです)
https://atcoder.jp/contests/abl/submissions/20614009
IntDisjointSetsを用いる利点・欠点
自力実装よりIntDisjointSetsの方が早い事例(2023/7/12追記)
i=1,2,…,N について、頂点 i まで消した時にグラフはいくつの連結成分に分かれていますか? という問題。逆から見てUnion-Findの典型。
自力実装の提出(TLE):
https://atcoder.jp/contests/abc229/submissions/43509722
IntDisjointSetsを使った提出(AC, 943ms):
https://atcoder.jp/contests/abc229/submissions/43510042
IntDisjoinSetsを使おう(戒め)
参考資料
https://juliacollections.github.io/DataStructures.jl/latest/disjoint_set/
Union find(素集合データ構造)
競プロ覚書:Union-Find まとめ - pyてよn日記
https://ei1333.github.io/algorithm/union-find.html
素集合データ構造 - Wikipedia
応用
重み付き Union-Find 木とそれが使える問題のまとめ、および、牛ゲーについて - Qiita