# 問題解決力を鍛える!アルゴリズムとデータ構造 ## 第11章~ - 任意参加 - ファシリテーター: hsu ### 話す・書く順番決めルーレット in Ruby ```ruby ['hsu', 'chen', 'teruya', 'kanazawa', 'okura', 'tsujita'].shuffle.each { |v| puts ">[name=#{v}]\n\n" } ``` in JavaScript ```javascript const l = ['hsu', 'chen', 'teruya', 'kanazawa', 'okura', 'tsujita'] l[Math.floor(Math.random()*l.length)] ``` ### ファシリテーター決め方 - `%w(hsu chen teruya kanazawa okura kanno)`の順番でやっていく ### 参加者がやること - 事前に決められた章を読む - 学んだことやわからなかったことを書き込む(任意) 当日 - 最初に感想や質問を書く時間を設ける(10分) - 時間枠 60分 - 延長枠 30分 ### 勉強会時にやること 各自学びや気付き、疑問を発表していく ファシリテーターがよしなに議論を進める 終了前に勉強会を振り返る?(KPTなどで)← 60分のときに仮締め ## 各自が書き込む場所 >[name=hsu] https://www.runoob.com/data-structures/union-find-size.html 文字を読んでも理解できずJavaの例を参考しにJSに 効率は木の高さに依存するなので時間複雑度はO(h)です ```javascript= class UnionFind { constructor(n) { // parent[i] 表示第 i 個元素所指向的父節點 this.parent = new Array(n); // 數據個數 this.count = n; // 初始化, 每一個 parent[i] 指向自己, 表示每一個元素自己自成一個集合 for (let i = 0; i < n; i++) { this.parent[i] = i; } } find(p) { if (p < 0 || p >= this.count) { throw new Error("Index out of range"); } // Search self-parent node until achieving root node // root node: parent[p] === p while (p !== this.parent[p]) { p = this.parent[p]; } return p; } // 要素pとqが同じ集合に属するかどうかを判断し、それらのルートノードが同じかどうかを確認します isConnected(p, q) { return this.find(p) === this.find(q); } // 要素pとqが属する2つの集合をマージする unionElements(p, q) { const pRoot = this.find(p); const qRoot = this.find(q); if (pRoot === qRoot) { return; } this.parent[pRoot] = qRoot; } } ``` size化にすると効率が上がる ```javascript= class UnionFindWithSizeOptimization { constructor(n) { // parent[i] 表示第 i 個元素所指向的父節點 this.parent = new Array(n); // sz[i] 表示以 i 為根的集合中元素個数 this.sz = new Array(n); // 數據個數 this.count = n; // 初始化, 每一個 parent[i] 指向自己, 表示每一個元素自己自成一個集合 for (let i = 0; i < n; i++) { this.parent[i] = i; this.sz[i] = 1; } } find(p) { if (p < 0 || p >= this.count) { throw new Error("Index out of range"); } // Search self-parent node until achieving root node // root node: parent[p] === p while (p !== this.parent[p]) { p = this.parent[p]; } return p; } // 要素pとqが同じ集合に属するかどうかを判断し、それらのルートノードが同じかどうかを確認します isConnected(p, q) { return this.find(p) === this.find(q); } // 要素pとqが属する2つの集合をマージする unionElements(p, q) { const pRoot = this.find(p); const qRoot = this.find(q); if (pRoot === qRoot) { return; } // 少ない集合は多いの方にマージする if (this.sz[pRoot] < this.sz[qRoot]) { this.parent[pRoot] = qRoot; this.sz[qRoot] += this.sz[pRoot]; } else { this.parent[qRoot] = pRoot; this.sz[pRoot] += this.sz[qRoot]; } } } ``` テストしたBeforeとAfter ```javascript! function testUF(uf, m) { const startTime = new Date().getTime(); const n = uf.count; for (let i = 0; i < m; i++) { const a = Math.floor(Math.random() * n); const b = Math.floor(Math.random() * n); uf.unionElements(a, b); } for (let i = 0; i < m; i++) { const a = Math.floor(Math.random() * n); const b = Math.floor(Math.random() * n); uf.isConnected(a, b); } const endTime = new Date().getTime(); return (endTime - startTime) / 1000; } const n = 100000; const m = 100000; const uf1 = new UnionFind(n); console.log(`Without optimization: ${testUF(uf1, m)} s`); const uf2 = new UnionFindWithSizeOptimization(n); console.log(`With optimization: ${testUF(uf2, m)} s`); ``` ```bash Without optimization: 0.380 s With optimization: 0.064 s ``` > [name=tsujita] - 特段語れることがないのですが・・・・ - こちらのサイト内のスライドがわかりやすかったです https://atcoder.jp/contests/atc001/tasks/unionfind_a - まとめて→同じグループにいるか判定するプロセス の中で計算をより効率的にできる手法と理解しました > [name=teruya] https://atcoder.jp/ranking https://atcoder.jp/contests/arc032/tasks/arc032_2 ```ruby= # 上位ノードほど @rank が大きい数字となる class Node attr_accessor :parent, :rank # 親は自分自身、ランクは 0 として初期化する def initialize(n) @parent = n @rank = 0 end end class UnionFind # 0 から n までのノード(つまりn + 1個)を初期化する def initialize(n) @nodes = (0..n).map { |i| Node.new(i) } end # 再帰で最も上位の親を探して更新し、その値を返す def parent(x) return x if @nodes[x].parent == x return @nodes[x].parent = parent(@nodes[x].parent) end # a と b を仲間にする def unite(a, b) a_parent = parent(a) b_parent = parent(b) # a 及び b の親が同じ場合は何もしない return if a_parent == b_parent # a親ランク < b親ランク の場合は、b親をa親の親とする # なお、ランクをいじる必要はなし if @nodes[a_parent].rank < @nodes[b_parent].rank @nodes[a_parent].parent = b_parent # その他の場合は、a親をb親の親とする else @nodes[b_parent].parent = a_parent # a親、及びb親のランクが同じ場合は、a親のランクを +1 する @nodes[a_parent].rank += 1 if @nodes[a_parent].rank == @nodes[b_parent].rank end end # a と b の親が同じかどうかを返す def same?(a, b) parent(a) == parent(b) end # 各ノードの親を全て出力する確認用 def parents @nodes.map(&:parent) end end ``` ## 振り返り > [name=tsujita] たまーにatcoderで照屋さんの過去の解答例を見つけてます > [name=chen] 都市の例は面白い... それで説明すれば理解しやすいですねw > [name=hsu] atcoder見てみます > [name=teruya] たまにアルゴリズムで殴って問題解決してます https://github.com/Catal/Nozomi/pull/8263 など ## 終了後ファシリテーターがやること - 次回のファシリテーターを決める - chenさん - 読む範囲を決める - 第12.1~12.4章
{"metaMigratedAt":"2023-06-18T01:44:39.273Z","metaMigratedFrom":"Content","title":"問題解決力を鍛える!アルゴリズムとデータ構造","breaks":true,"contributors":"[{\"id\":null,\"add\":3263,\"del\":203},{\"id\":\"b770ae0c-395b-4a9f-800b-749b1d5e6f28\",\"add\":1268,\"del\":0},{\"id\":\"ac592d01-fc7b-40cf-a7af-9fbd482fe074\",\"add\":227,\"del\":2},{\"id\":\"bff1be86-f568-4ded-8625-e23387557205\",\"add\":51,\"del\":0},{\"id\":\"71fff5c8-808f-46f2-8fdf-9c81b7ea2fc0\",\"add\":719,\"del\":0}]"}
Expand menu