Upgrade to Pro — share decks privately, control downloads, hide ads and more …

AHC045_解説

Shun_PI
April 10, 2025

 AHC045_解説

Shun_PI

April 10, 2025
Tweet

More Decks by Shun_PI

Other Decks in Programming

Transcript

  1. 目次 1. 作問について 2. サンプルプログラムからの改善 3. 木の構築パートの改善 【部分木上書き処理】 4. グループ分けパートの改善【部分破壊再構築焼きなまし】

    5. 推定パートの改善 【不等式・違反量・ギブスサンプリング】 ※最上位解法には触れず、サンプル~橙パフォ到達程度までを 解説します 2025/4/10 3
  2. 問題概要 • 𝑁個の点があるが、座標が曖昧である • 以下のようなクエリを𝑄回までできる • 2個以上𝐿個以下の点の集合を渡す • 最小全域木(MST)を構成する辺が返ってくる •

    クエリが終わったら以下の問題に回答する • サイズ𝑀・総和𝑁の配列𝐺があるので、 その通りに𝑵個の点をグループに分ける • 各グループで全域木を作る • この時に使用した全ての辺の (真の)長さの総和をできるだけ短くしたい 2025/4/10 4 回答 𝐺 = [1,2,3]のとき クエリ
  3. 問題の面白いポイント① クエリの使い道が多様 • 「点の集合を与えてMSTを返す」という クエリが大きく2種類の方法で活用できる • 木の構築パートでクエリの応答で解を 直接上書きすることで改善 • クエリで返ってきた木が最小全域木であると

    いう性質を利用して不等式を立てて 座標推定を行うことで改善 2025/4/10 7 木の一部でクエリして 応答で上書き! 3 2 1 クエリで得られた MSTから 不等式が成立! 𝑑12 ≤ 𝑑13 𝑑23 ≤ 𝑑13
  4. 作問の反省ポイント • 「何をすればいいかわからない」「クエリがまともに使えない」 という方多数 • 今回の発表で頑張って解説します! • ビジュアライザを活用しづらい • ある程度改善ができるまでは回答が線ですごいごちゃごちゃする

    • ごちゃごちゃなのが無くなっていく過程は楽しいかも? • クエリをアニメーションしてもあまり考察できることがない • 絵が地味 • MSTを辺の長さ順に返してしまうバグを埋め込んでいた • Clarのおかげですぐに直せました(ありがとうございます) 2025/4/10 9
  5. 複雑な問題を部分問題に分ける 2025/4/10 12 1. 座標の長方形範囲から 座標を推定する 2. 推定した座標を使い 点をグループ分けする 3.

    各グループについて 全域木を作る • サンプルプログラムはこの問題が 3つの部分問題から成り、 それぞれ独立に改善できる ことを示唆している • 複雑な問題を部分問題に 落としこみ、それぞれの小問題に ついて独立に考察することで、 「何をすればいいかわからない」 状態から脱却しやすい!
  6. 余談:部分問題をメソッド化する • ある処理を部分処理に細分化できそうな場合、 対応するメソッドを作る習慣があるとよい • 長期ヒューリスティック問題だけでなく 業務プログラミングでもよくやる • メリット 1.

    コードの可読性・検索性向上 2. 各処理の入出力が明確になり、 その処理が何をする処理で 何の情報に依存しているかが分かる 3. 同じ機能を持つ別のメソッドと交換したり、 パラメータによってどちらを使うかを 制御することが簡単になる • 上記1~3より、整理に多少の時間が かかったとしても長期コンでは 実装や考察の効率化が見込める 2025/4/10 13 before after
  7. サンプルの改善 2025/4/10 14 1. 座標の長方形範囲から 座標を推定する 2. 推定した座標を使い 点をグループ分けする 3.

    各グループについて 全域木を作る • 3つの部分問題のうち、 「座標推定」の改善は難しいが、 他2つは比較的簡単に改善できる • 「グループ分け」「全域木を 作る」の2処理について、 簡単にできる改善方法を 紹介していく
  8. サンプルの改善(グループ分けパート) • ビジュアライザを見ると、 各グループが異常に縦に長い! • これはグループを「(𝑥, 𝑦)で ソートした順」に決めているため • 𝑥座標が同じになる点は少ないので、

    実質𝑥座標のみでソートしていることになる • グループ内で𝑦座標が極端に異なる点が存在 してしまうので、非常に長い縦線ができる 2025/4/10 15
  9. サンプルの改善(グループ分けパート) ソート関数の改善① • ソート順を𝑥 + 𝑦/1000 ∗ 1𝑒9 にする •

    𝑦軸を細かく短冊に区切り、 各短冊上の点を𝑥座標順に結ぶイメージ • ビジュアライザはかなりすっきりしたが、 短冊が切り替わるタイミングで長い斜め辺 ができてしまう 2025/4/10 16 19.8G 737位 1093perf
  10. サンプルの改善(グループ分けパート) ソート関数の改善② • ソート順を以下の関数にする • 𝑥 + 𝑦/600 ∗ 1𝑒9

    ( 𝑦/600 が偶数の時) • −𝑥 + 𝑦/600 ∗ 1𝑒9 ( 𝑦/600 が奇数の時) • 短冊ごとに「右から左」「左から右」が 交互になるので、 長い斜め辺が登場しなくなる • 斜め辺を気にしなくてよくなったので、 短冊はさらに細くできるようになった 2025/4/10 17 24.6G 618位 1217perf
  11. 以降の改善手順 2025/4/10 19 1. 座標の長方形範囲から 座標を推定する 2. 推定した座標を使い 点をグループ分けする 3.

    各グループについて 全域木を作る • 以降は各部分問題について さらに改善をしていく • 改善が簡単な順に解説する 1. 木の構築パート 2. グループ分けパート 3. 座標推定パート
  12. クラスカル法やプリム法による木の構築 各グループでMSTを作る • サンプルは(なぜか)クエリで得た辺を 回答に使っているが、クエリを使わず、 各グループでMSTを作って回答してみる • MSTを作る方法はクラスカル法とプリム法 がある •

    クエリを使わない分スコアが悪化してしまう • が、一先ずクエリ無しでも木が構築できた • ここからクエリを使って改善することを考える 2025/4/10 21 23.5G 684位 1148perf
  13. 単純な焼きなまし 2点swap焼きなまし • ランダムに2頂点を選び、異なるグループ に属していたら割り当てをswapする • 全グループのMSTの辺の長さの合計値を 評価値とする • swapした2グループのみMSTを再計算

    • MSTの計算は辺をソートしないプリム法で やると高速(割と重要) • MSTの差分計算は困難なのでやらない 2025/4/10 28 MSTの再計算 2点swap 33.7G 267位 1663perf
  14. 部分破壊再構築焼きなまし 3グループの部分破壊再構築 • 2つの距離が近いグループと、 サイズがその2グループのサイズの和である 1つのグループの合計3つをランダムに選び 破壊再構築 • サイズが{𝐺1 ,

    𝐺2 , 𝐺1 + 𝐺2 }の3グループを 選ぶと、グループを 離れた場所に1手で移動させられる 可能性が生じる 2025/4/10 31 37.7G 119位 1990perf 𝐺1 𝐺2 𝐺1 + 𝐺2 a b a+b 𝐺1 + 𝐺2 𝐺1 𝐺2
  15. 参考:全頂点MSTの切断を元にしたグループ分け • 別の方針として、全頂点のMSTについて辺を 切り分けてグループを作る方法がある • 以下のアルゴリズムを実装 • 木について長い順に辺を見て、切断後の部分木が残っているグ ループのサイズと同じであれば 部分木にグループを割り当てる

    • バックトラック法で再帰的に一定時間処理する • 切断した辺の長さの総和が最大のものを採用 • 1~2割のケースでは全グループの割り当てに失敗する • この場合はあきらめて初期解を使用した • 3グループ近傍を入れた部分破壊再構築焼きなましより少し 悪いスコアだが、2グループ近傍のみの焼きなまし より良いスコアになる • この方針を元に「辺の切断」「辺の結合」 「木の辺の組み換え」といった近傍で焼きなましができると さらに改善できる 32 36.9G 140位 1929perf 大きさ3のグループを切り出し 大きさ2のグループを切り出し
  16. 座標推定の重要性 • 今まで、「木の構築パート」ではクエリを使うことで 座標の曖昧さに対抗してきたが、「グループ分け」パートでは 一切クエリの情報を使えていない • クエリを用いた「座標推定」ができないかを考察する 2025/4/10 34 1.

    座標の長方形範囲から 座標を推定する 2. 推定した座標を使い 点をグループ分けする 3. 各グループについて 全域木を作る before after クエリ 座標の曖昧さの 影響を大きく受ける 1. 座標の長方形範囲から 座標を推定する 2. 推定した座標を使い 点をグループ分けする 3. 各グループについて 全域木を作る クエリ 座標の曖昧さの 影響を抑えられる クエリ (長方形の中心にするだけ)
  17. 参考:不等式を立てない方法 MSTに出現する辺の距離を縮めるだけ • クエリ結果について不等式を立てなくても、 「MSTにある辺は無い辺より短そう」 程度の考察だけでも座標の補正ができる • クエリの基点を、長方形範囲の大きい順に選び、 その頂点から距離が近い順に集合を作る •

    応答に出てきた辺は20%縮める • 応答に出てこなった辺は2%伸ばす • 座標補正パートと木の構築パートでクエリを 半分ずつ使用 • これだけでも結構改善する 2025/4/10 35 クエリ結果 クエリに出てきた 辺は縮める 39.1G 89位 2096perf クエリに出てこなかった 辺は伸ばす
  18. MSTに対する考察 • MSTの定義は、「他のどんな全域木よりも辺の総長が短い全域木」 • 適当に全域木を作ると、辺の総長に関する不等式が作れる • 全域木の個数は𝐿𝐿−2(プリューファーコード)なので、列挙が大変 2025/4/10 36 3

    2 5 1 4 3 2 5 1 4 MST ≤ 3 2 5 1 4 3 2 5 1 4 MST ≤ 𝑑12 + 𝑑23 + 𝑑34 + 𝑑35 ≤ 𝑑12 + 𝑑24 + 𝑑34 + 𝑑25 𝑑12 + 𝑑23 + 𝑑34 + 𝑑35 ≤ 𝑑12 + 𝑑13 + 𝑑14 + 𝑑35 𝑑23 + 𝑑35 ≤ 𝑑24 + 𝑑25 𝑑23 + 𝑑34 ≤ 𝑑13 + 𝑑14
  19. MSTに対する考察 • 不等式の両辺はなるべく差が小さい方が制約が厳しくなって嬉しそう • MSTから1辺組み替えただけの全域木だけ考えれば十分そう • 2辺の大小関係に関する不等式をたくさん作れる 2025/4/10 37 3

    2 5 1 4 3 2 5 1 4 MST ≤ 3 2 5 1 4 3 2 5 1 4 MST ≤ 𝑑12 + 𝑑23 + 𝑑34 + 𝑑35 ≤ 𝑑12 + 𝑑23 + 𝑑24 + 𝑑35 𝑑12 + 𝑑23 + 𝑑34 + 𝑑35 ≤ 𝑑12 + 𝑑13 + 𝑑34 + 𝑑35 𝑑34 ≤ 𝑑24 𝑑23 ≤ 𝑑12
  20. 不等式の列挙法①:辺を切ってつなぎなおす • MSTから1辺組み替えただけの全域木を列挙すれば、不等式を列挙できる • MSTの辺を1辺切って、残った2つの連結成分に対してあり得る辺の つなぎ方を全列挙 • この例だと合計で 3+5+3+3 =

    14個の不等式ができる 2025/4/10 38 3 2 5 1 4 3 2 5 1 4 MSTの辺を1つ切る 𝑑23 ≤ 𝑑13 𝑑23 ≤ 𝑑14 𝑑23 ≤ 𝑑15 𝑑23 ≤ 𝑑24 𝑑23 ≤ 𝑑25 (2つの連結成分の積 - 1)個の 不等式ができる 連結成分のつなぎ方を 全列挙
  21. 不等式の列挙法②:辺を追加して閉路を作る • MSTに含まれない辺を1つつなぐと、閉路ができる • 閉路内において(MSTに含まれる辺) ≤ (MSTに含まれない辺) が成り立つ • この方法でも作れる不等式の個数はたぶん同じ

    • やってることもたぶん同じ 2025/4/10 39 3 2 5 1 4 MSTに含まれない 辺を1つつなぐ 𝑑12 ≤ 𝑑14 𝑑23 ≤ 𝑑14 𝑑34 ≤ 𝑑14 閉路内で (MSTに含まれる辺) ≤ (MSTに含まれない辺) の不等式を立てる
  22. 不等式から座標推定する方法 • 辺の長さの不等式から、どのように座標推定すればよいか? • なるべく不等式に違反しないような座標の組み合わせを求めたい • 適当に座標を決めたとき、不等式制約の(違反数 or 違反量)に着目する と、評価関数として使えるので、山登りができる!

    2025/4/10 40 違反数を山登り 𝑑23 ≤ 𝑑13 𝑑23 ≤ 𝑑15 𝑑23 ≤ 𝑑25 𝑑23 ≤ 𝑑43 𝑑23 ≤ 𝑑45 𝑓 𝒙, 𝒚 = (𝑑23 > 𝑑13 ) +(𝑑23 > 𝑑15 ) +(𝑑23 > 𝑑25 ) +(𝑑23 > 𝑑43 ) +(𝑑23 > 𝑑45 ) 違反量を山登り 𝑑23 ≤ 𝑑13 𝑑23 ≤ 𝑑15 𝑑23 ≤ 𝑑25 𝑑23 ≤ 𝑑43 𝑑23 ≤ 𝑑45 𝑓 𝒙, 𝒚 = max(0, 𝑑23 − 𝑑13 ) +max(0, 𝑑23 −𝑑15 ) +max(0, 𝑑23 −𝑑25 ) +max(0, 𝑑23 − 𝑑43 ) +max(0, 𝑑23 − 𝑑45 )
  23. 違反数の焼きなましによる座標推定 • 制約違反数を評価値とした焼きなまし • 近傍は1つの点を長方形範囲から ランダムに選びなおす • 近傍で選びなおした点に関係する不等式のみ 評価しなおすようにして高速化 •

    クエリは座標推定と木の構築で半々で使用 • これだけでもほぼ全制約を満たした 座標が得られる 2025/4/10 41 𝑑23 ≤ 𝑑13 𝑑23 ≤ 𝑑15 𝑑23 ≤ 𝑑25 𝑑23 ≤ 𝑑43 𝑑23 ≤ 𝑑45 𝑓 𝒙, 𝒚 = (𝑑23 > 𝑑13 ) +(𝑑23 > 𝑑15 ) +(𝑑23 > 𝑑25 ) +(𝑑23 > 𝑑43 ) +(𝑑23 > 𝑑45 ) 39.4G 78位 2142perf
  24. 違反量の焼きなましによる座標推定 • 制約違反量を評価値とした焼きなまし • 単に違反量で焼くだけだと 違反数の時より良くない • 違反量に「マージン」を入れると少し改善 • 単に制約を満たすだけでなく、余裕をもって満たす

    配置の方が、より座標期待値に近い値で推定できる • それでも違反量の場合と大差ない • 焼きなましだと山を登り切れない • 近傍をうまくとることでもより改善できるが… 2025/4/10 42 𝑑23 ≤ 𝑑13 𝑑23 ≤ 𝑑15 𝑑23 ≤ 𝑑25 𝑑23 ≤ 𝑑43 𝑑23 ≤ 𝑑45 39.4G 79位 2138perf 𝑓 𝒙, 𝒚 = max(0, 𝑑23 − 𝑑13 ) +max(0, 𝑑23 −𝑑15 ) +max(0, 𝑑23 −𝑑25 ) +max(0, 𝑑23 − 𝑑43 ) +max(0, 𝑑23 − 𝑑45 ) 通常の違反量 マージンを持たせた違反量 𝑑23 ≤ 𝑑13 𝑑23 ≤ 𝑑15 𝑑23 ≤ 𝑑25 𝑑23 ≤ 𝑑43 𝑑23 ≤ 𝑑45 𝑓 𝒙, 𝒚 = max(0, 𝑑23 − 𝑑13 + 𝑚) +max(0, 𝑑23 −𝑑15 + 𝑚) +max(0, 𝑑23 −𝑑25 + 𝑚) +max(0, 𝑑23 − 𝑑43 + 𝑚) +max(0, 𝑑23 − 𝑑45 + 𝑚)
  25. 勾配降下法による座標推定 • 違反量ベースの評価関数は、 (各不等式の違反量が0になる点を除いて) 微分可能であるため、勾配が求まる • 例えば𝑑12 の𝑥1 に関する勾配は以下のようになる •

    𝜕𝑑12 𝜕𝑥1 = 𝜕 𝜕𝑥1 𝑥1 − 𝑥2 2 + 𝑦1 − 𝑦2 2 = 𝜕 𝜕𝑥1 𝑥1 − 𝑥2 2 + 𝑦1 − 𝑦2 2 2𝑑12 = 𝑥1 − 𝑥2 𝑑12 • (マージン込みの)違反不等式について全点で勾配 を計算し、全ての点を勾配の方向に同時に動かす • 長方形範囲からはみ出したら押し戻す • 学習率は線形で時間減衰 2025/4/10 43 41.4G 46位 2319perf マージンを持たせた違反量 𝑑23 ≤ 𝑑13 𝑑23 ≤ 𝑑15 𝑑23 ≤ 𝑑25 𝑑23 ≤ 𝑑43 𝑑23 ≤ 𝑑45 𝑓 𝒙, 𝒚 = max(0, 𝑑23 − 𝑑13 + 𝑚) +max(0, 𝑑23 −𝑑15 + 𝑚) +max(0, 𝑑23 −𝑑25 + 𝑚) +max(0, 𝑑23 − 𝑑43 + 𝑚) +max(0, 𝑑23 − 𝑑45 + 𝑚)
  26. ギブスサンプリング • Lが小さいケースだと簡単に全制約を満たす 座標セットが得られるが、今まではそのうち 1セットしか使っていない • 座標セットを複数取って平均値を推定値とすれば、 より良い近似値ができそう • 焼きなましや勾配降下法の終盤での

    座標の平均値を取るという方法もできるが… • (ほぼ)全制約を満たすような座標セットを 効率よくサンプルできる手法がギブスサンプリング 2025/4/10 44 Wikipediaにも 焼きなましは サンプリングに不向きで あることが書いてある
  27. ギブスサンプリング • 勾配降下法で得た座標をもとに、以下の処理を繰り返す • 点1から𝑁までの順に、 • 長方形範囲からランダムに点を選びなおす • その点に関する不等式に違反しているかをチェック •

    全ての不等式に違反していなければ採択 • 1つでも違反していたら棄却 • 300回選びなおしても採択できなければあきらめる • ループの後半で得られた座標セットの 平均を取り推定値とする 2025/4/10 45 43.3G 30位 2453perf
  28. クエリ配分の調整 • 今までは「座標推定」「木の構築」でのクエリ数を半々にしていた • 座標推定パートが十分強くなったので、「木の構築」パートで 上書き処理をするメリット下がった • 「座標推定」にクエリの2/3を使用するように修正 • さらに改善が進むと座標推定全振りでよくなったりする

    43.9G 27位 2485perf 46 before after 1. 座標の長方形範囲から 座標を推定する 2. 推定した座標を使い 点をグループ分けする 3. 各グループについて 全域木を作る クエリ クエリ 1. 座標の長方形範囲から 座標を推定する 2. 推定した座標を使い 点をグループ分けする 3. 各グループについて 全域木を作る クエリ クエリ
  29. コンテスト中のwriter最終提出 コンテスト中のwriter最終提出 • グループ分けパートで触れた「全域木の切断・結合による焼きなまし」 を実装し、特に𝑀が大きいケースのグループ分けで活用 • クエリ毎に各点の想定誤差を更新し、 クエリを決める際に活用している 最上位到達へのアプローチ •

    推定パートのさらなる改善が重要となる • 特に「クエリの決め方」が最重要で、より情報が取れるような クエリをデザインすることが求められる • 座標セットを複数用意し、各座標セットで結果が異なるように (=結果のエントロピーが最大となるように)クエリを決める • 𝐿=3のとき、なるべく正三角形に近い3点を選ぶことで得られる情報量を向上 2025/4/10 47 46.9G 9位 2799perf