$30 off During Our Annual Pro Sale. View Details »

メタヒューリスティクスで広がる「解けた!」の世界

 メタヒューリスティクスで広がる「解けた!」の世界

JOI夏季セミナー2023 全体講演会1の講演資料です。

【講義題目】
メタヒューリスティクスで広がる「解けた!」の世界

【講義概要】
世の中には,まだ効率的に解く方法が見つかっていない難しい問題がたくさんあります.こうした問題に立ち向かうときに頼りになるのが,メタヒューリスティクスと呼ばれるアルゴリズムたちです.本講義では,まずメタヒューリスティクスの基礎について,そしてメタヒューリスティクスが実社会にどのように応用されているかについて紹介します.

terry-u16

August 20, 2023
Tweet

More Decks by terry-u16

Other Decks in Programming

Transcript

  1. 松尾 充 株式会社ALGO ARTIS まつお あたる (世界 位) 2196 アルゴリズム

    ヒューリスティック 最高 2920 @terry_u16 terry_u16 1992年 福岡県生まれ 九州大学に入学して機械工学を勉強 九州大学大学院の修士課程に進学 2010年 2014年 株式会社IHIに入社し戦闘機用ジェットエンジンを開発 うっかり競技プログラミングにハマってしまう 株式会社ALGO ARTISに入社しアルゴリズムを仕事に 2016年 2020年 2022年
  2. なぜ多項式時間で解きたいの? 多項式時間で解けないと、計算時間が大爆発してしまうためです。 そのため、多項式時間で解けない問題を「解くのが難しい問題」としました。 𝑵𝟐 𝑵𝟓 𝟐𝑵 𝑵! N=10 一瞬 一瞬

    一瞬 0.003秒 N=20 一瞬 0.003秒 0.001秒 77年 N=50 一瞬 0.3秒 13日 1極年 N=100 一瞬 10秒 40兆年 3×10141年 以下の表は、1秒間に10億回の計算ができるコンピューターにおける入力サイズごとの計算時間のおおまかな目安です。 ※1極=1048乗 ※ 多項式時間 指数時間
  3. 巡回セールスマン問題 巡回セールスマン問題とは、一言で言えば 最小コストで全ての都市を回る経路を探す最適化問題です。 問題文 𝑁 個の都市があり、全ての都市同士の間は 道でつながっています。 都市 𝑖 から都市

    𝑗 に移動するときの コスト(≒距離)は𝑑𝑖,𝑗 です。 松尾くんは都市 1 から出発して、都市 1 以外の 都市を全て回り、最後に都市 1 に戻ってきたいです。 そのときの合計コストの最小値を求めてください。 都市 1 都市 2 都市 3 都市 4
  4. 巡回セールスマン問題 - 解法を考えてみよう 初めて聞いたよ!という人 N = 7 のときにどうやったら解くことができそうか、ちょっと考えてみてください! もう解いたことあるよ!という人 N

    = 20, 100, 1000 のときに高速に解く方法がないか、考えてみてください! ただし、松尾くんはヒマなので、少しくらい最適解より悪くても大丈夫なものとします。
  5. 巡回セールスマン問題 - 全探索 都市がN個あるとき、何通り試せばよいか考えてみましょう。 多すぎてコンピュータを使っても無理!何かいい方法はないでしょうか? 𝑁 × 𝑁 − 1

    × ⋯ × 2 × 1 のことを 階乗といい、 𝑁! と表します。 たとえば、 3! = 3 × 2 × 1 = 6 です。 詳しくは高校数学で学びますので、 お楽しみに! 4都市のとき、 3×2×1= 6通り 3! = 5都市のとき、 4×3×2×1= 24通り 4! = 6都市のとき、 5×4×3×2×1= 120通り 5! = 7都市のとき、 6×5×4×3×2×1= 720通り 6! = 8都市のとき、 7×6×5×4×3×2×1= 5,040通り 7! = 20都市のとき、 19×18×17×…×1= 121,645,100,408,832,000 通り 19! = ⋮ かいじょう
  6. 巡回セールスマン問題 - 動的計画法 実は全探索を行うとき、実質同じであるパターンがたくさん出てきます。 実質同じであるパターンをまとめて高速に計算する方法が動的計画法です。 1 2 3 4 5

    6 7 8 これらはすべて 「都市1, 2, 3, 4, 5を訪問済で、 今は都市5にいる状態」として まとめてしまうことができ、 無駄な計算を省くことができます。 訪問済 未訪問 1 2 3 4 5 6 7 8
  7. 巡回セールスマン問題 - 動的計画法 動的計画法ではNがどのくらいの大きさになるまで解けるでしょうか? Nを変化させたときの N22N の値を調べてみましょう。 ※1秒間に10億回の計算ができるコンピューターでのおおよその所要時間を示しています。 ※計算時間だけでなく、メモリの使用量も問題になってきます。32都市の巡回セールスマン問題を解く場合、工夫しないと1TBほどのメモリが必要になります。 10都市のとき、

    10×10×210= 102,400 → 一瞬 15都市のとき、 15×15×215 = 7,372,800 → 0.007秒 くらい 20都市のとき、 20×20×220 = 419,430,400 → 0.4秒 くらい 30都市のとき、 30×30×230 = 966,367,641,600 → 16分 くらい 50都市のとき、 50×50×250 = 2,814,749,767,106,560,000 → 89年 くらい 5都市のとき、 5×5×25= 800 → 一瞬 全探索より解ける幅は広がりましたが、まだまだNが大きくなると厳しい!
  8. 幅広い問題に適用することができる! (1/2) 実は巡回セールスマン問題に限って言えば、 もっと強力なアルゴリズムやソフトウェアが存在します。 Lin-Kernighan法 EAX Concorde 山登りベースだが、k-optという操作を効率的に行うアルゴリズム LKHという実装例が公開されている 遺伝的アルゴリズム系の比較的新しいアルゴリズム

    Kaggleの最適化コンテストで上位陣がみんな使っていたことで有名 厳密解法・近似解法いろいろ入ったすごいソフトウェア 85,900都市の巡回セールスマン問題の厳密解を出すことに成功 こんなに強い解法が既にあるなら、わざわざメタヒューリスティクスを勉強しなくても良いのでは? そんなことはありません!
  9. 幅広い問題に適用することができる! (2/2) メタヒューリスティクスの嬉しいところは、 研究者人生をかけて1から最強の解法を作らなくても 同じ枠組みに沿って考えればいろんな問題に対応できることです。 0 5 10 15 20

    その他 全探索系 推定系 モンテカルロ法 貪欲法・ビームサーチ 山登り・焼きなまし 過去21回のAHCにおける強かった手法 ※重複あり、選定は松尾の独断と偏見です。漏れもあるかもしれないので参考程度に……。 過去のAHCにおいても幅広い問題で メタヒューリスティクス解法が上位を取っています。 新しい問題に出会うたびに 数年かけて新しい解法を考えるのは大変です。 メタヒューリスティクスを使えば、 いろんな問題を同じ枠組みで解くことができます。 この2つが代表的な メタヒューリスティクス
  10. 貪欲法の適用例 巡回セールスマン問題に貪欲法を適用してみましょう。 「未訪問の都市の中で最も近い都市に移動する」という貪欲にしてみます。 1 2 3 5 4 6 都市1からスタート

    1 2 3 5 4 6 最寄りの都市6へ 1 2 3 5 4 6 最寄りの都市2へ 1 2 3 5 4 6 最寄りの都市3へ 1 2 3 5 4 6 最寄りの都市5へ 1 2 3 5 4 6 最寄りの都市4へ 1 2 3 5 4 6 都市1でゴール!
  11. 貪欲法の実装例 N = 4 cost = [[0, 5, 3, 8],

    [5, 0, 4, 6], [3, 4, 0, 9], [8, 6, 9, 0]] total_cost = 0 current_city = 0 visited = [True, False, False, False] order = [0] # 都市0以外のN-1都市を訪問するまでループ for _ in range(N - 1): # 最も近い次の都市を全探索 best_cost = 1000000000 best_city = -1 for next_city in range(N): if visited[next_city]: continue # コストが最小の場合は更新 next_cost = total_cost + cost[current_city][next_city] if next_cost < best_cost: best_cost = next_cost best_city = next_city # 次の都市に移動し、状態を更新する visited[best_city] = True order.append(best_city) total_cost = best_cost current_city = best_city # 最後に都市0に戻ってくる total_cost += cost[current_city][0] order.append(0) print(f"total_cost: {total_cost}") print(f"order: {order}") 入力の作成 合計コスト・現在位置・訪問済みフラグ・訪問順を初期化 都市0以外を全て訪問するまでループ まだ訪問していない都市を全探索し、 最もコストが小さい都市を次の都市として選ぶ 次の都市に移動する 合計コスト・現在位置・訪問済みフラグ・訪問順を更新 最後に都市0に戻る 合計コスト・訪問順を出力して終了
  12. 貪欲法の長所と短所 貪欲法の長所と短所を表にしてみました。 実装が簡単で動作が高速なため、初心者から上級者までオススメです。 実装が簡単 まずお試しで組んでみることがやりやすく、 より高度な手法を使う前の足がかりになる 長所 短所 動作が高速 今回のケースだと𝑂

    𝑁2 となり、とても高速 他の手法と組み合わせて使うこともやりやすい 「損して得取れ」が難しい 基本的に後先考えずに動くので、 後ろの方にしわ寄せが行きやすい 評価関数のチューニングが難しい 選び方の基準となる評価関数によって 性能が変わり、そのチューニングが難しい 1 2 3 5 4 6 ※各状態の「良さ」を決める関数のことを「評価関数」といいます。
  13. ビームサーチの適用例 こちらも巡回セールスマン問題に適用してみましょう。 貪欲法をベースに「総コストの小さい3つを保持する」という方針にしてみます。 都市1からスタート 1 2 3 4 0分 候補3つとも保持

    1 2 3 4 4分 1 2 3 4 6分 1 2 3 4 10分 1 2 3 4 9分 1 2 3 4 10分 1 2 3 4 10分 1 2 3 4 11分 ⋮ 1 2 3 4 13分 1 2 3 4 16分 1 2 3 4 14分 1 2 3 4 20分 1 2 3 4 20分 1 2 3 4 23分 上位3つまで保持 候補3つとも保持 解の完成!
  14. 貪欲法とビームサーチの比較 貪欲法では「その時点で一番良いもの」だけを考えていましたが、 ビームサーチでは「その時点で良い順にK番目まで」を考える点が異なります。 1 2 3 4 0分 1 2

    3 4 4分 1 2 3 4 6分 1 2 3 4 10分 1 2 3 4 9分 1 2 3 4 10分 1 2 3 4 10分 1 2 3 4 13分 1 2 3 4 16分 1 2 3 4 14分 1 2 3 4 20分 1 2 3 4 20分 1 2 3 4 23分 1 2 3 4 0分 1 2 3 4 4分 1 2 3 4 9分 1 2 3 4 13分 1 2 3 4 23分 1個 K個 貪 欲 法 ビ ー ム サ ー チ
  15. 山登り法の適用例 巡回セールスマン問題に山登り法を適用してみましょう。 巡回セールスマン問題では「ランダムな区間の訪問順を反転させる」※という2-optと呼ばれる変更が 有効なので、今回はその手法を試してみます。 1 3 2 4 52分 5

    6 1 3 2 4 58分 5 6 1 3 2 4 43分 5 6 ※「2辺をランダムに選び、辺をつなぎ替える」と説明されることが多いですが、やっていることは同じです。 [3, 4]を 反転 [3, 5]を 反転 [3, 5, 6]を 反転 1 3 2 4 65分 5 6 1 3 2 4 74分 5 6 1 3 2 4 68分 5 6 [5, 3]を 反転 [5, 1, 2]を 反転 1 3 2 4 61分 5 6 [2, 4]を 反転
  16. 山登り法の実装例 # 制限時間の定義 TIME_LIMIT = 1.9 # 初期解 (state) を作る

    # ランダムな解にしても、貪欲法で解を作ってもよい state = generate_initial_state() start_time = get_time() # 時間いっぱいループ while get_time() - start_time < TIME_LIMIT: # 解を少し変化させる # 1箇所の値を変化させたり、2つの値を交換したり new_state = change_state(state) # スコアを計算する old_score = calculate_score(state) new_score = calculate_score(state) # スコアが悪化しなければ採用 if new_score >= old_score: state = new_state # 解を出力する print(state) 制限時間の定義 初期解を作成 ランダムな解を作っても、貪欲法などで解を作ってもよい 解の出力 制限時間になるまでループ 解を少し変化させる 古い解のスコアと新しい解のスコアを計算する スコアが悪化していなければ採用 ※擬似コードなので、そのままコピペしても動きません。
  17. 山登り法の長所と短所 山登り法の長所と短所を表にしてみました。 貪欲法以上に強力な手法なので、適用できる問題にはかなり強いです。 全体を見て判断できる 途中段階ではなく最終状態で評価できるので 「損して得取れ」という動きがとてもやりやすい 長所 短所 性能が高い 比較的実装がシンプルでありながら、

    貪欲法よりも良い解を出しやすい 適用できる問題の幅が少し狭い テトリスのように「少し変えただけで最終状態が 大きく変わってしまう」ような問題は苦手 局所解にハマりやすい それ以上改善ができない局所解になりやすい (詳しくは次のページ)
  18. 焼きなまし法の実装例 詳細は割愛しますが、山登り法から解の採用判断だけ変更すればOKです。 # 制限時間の定義 TIME_LIMIT = 1.9 # 初期解 (state)

    を作る # ランダムな解にしても、貪欲法で解を作ってもよい state = generate_initial_state() start_time = get_time() # 時間いっぱいループ while get_time() - start_time < TIME_LIMIT: # 解を少し変化させる # 1箇所の値を変化させたり、2つの値を交換したり new_state = change_state(state) # スコアを計算する old_score = calculate_score(state) new_score = calculate_score(state) # スコアが悪化しなければ採用 if new_score >= old_score: state = new_state # 解を出力する print(state) ↑違いはここだけ! # 各種パラメータ定義 TIME_LIMIT = 1.9 START_TEMP = 10000 END_TEMP = 100 # 初期解 (state) を作る # ランダムな解にしても、貪欲法で解を作ってもよい state = generate_initial_state() start_time = get_time() # 時間いっぱいループ while get_time() - start_time < TIME_LIMIT: # 解を少し変化させる # 1箇所の値を変化させたり、2つの値を交換したり new_state = change_state(state) # スコアを計算する old_score = calculate_score(state) new_score = calculate_score(state) # 温度 (temperature) と遷移確率 (probability) を計算 time = (get_time() - start_time) / TIME_LIMIT temperature = pow(START_TEMP, 1 - time) * pow(END_TEMP, time) probatility = exp((new_score - old_score) / temperature) # スコアが悪化しなければ無条件で採用 # 悪くなっていても確率的に採用 if new_score >= old_score or random() < probatility: state = new_state # 解を出力する print(state) 山登り法 焼きなまし法 温度 温度パラメータ𝑇は、 開始温度を𝑇0 、終了温度を𝑇1 、 経過時間を𝑡 0 ≤ 𝑡 ≤ 1 として、 𝑇 𝑡 = 𝑇0 1−𝑡 × 𝑇1 𝑡 としています。 採用確率 採用確率𝑝は、スコア変化量をΔ𝑆、 温度を𝑇として、 𝑝 Δ𝑆, 𝑇 = ቊ 1 Δ𝑆 ≥ 0 𝑒 Τ Δ𝑆 𝑇 Δ𝑆 < 0 としています(最大化問題の場合)。 ※擬似コードなので、そのままコピペしても動きません。
  19. オススメの本 ゲームで学ぶ探索アルゴリズム実践入門 青木栄太 著/技術評論社 メタヒューリスティクスをはじめとする幅広い探索アルゴリズムがぎゅっと詰まった一冊! 丁寧な実装例付きで、各手法の基礎から一歩進んだテクニックまで学べます。 初心者から上級者まで、メタヒューリスティクスをじっくり学びたい人にオススメです! https://book.mynavi.jp/ec/products/detail/id=131288 https://gihyo.jp/book/2023/978-4-297-13360-3 競技プログラミングの鉄則

    アルゴリズム力と思考力を高める77の技術 米田優峻 著/マイナビ出版 メタヒューリスティクス関連の章は1章だけですが、とても分かりやすくまとまっています! AtCoder上での自動採点システムも提供されているので、定着度の確認もバッチリです。 アルゴリズムもメタヒューリスティクスも勉強してみたい初~中級者の方にオススメです!