Upload
kazuma-mikami
View
728
Download
0
Embed Size (px)
Citation preview
マラソンマッチ89日記kyuridenamida
使った環境
• C++11
• Sublime Text(mac) / notepad++(win)
• gprof (プロファイラ,一回だけ使った)
• Valgrind (極稀にどうしてもバグが取れないとき
• Git (最近こういうのには使うようになった)
• Python, bash, Excel (統計情報取るために)
• さくらのクラウド(new! 知り合い伝にさくらのクーポン頂きました感謝)• 結局下手すぎてうまくパラメータ調整には使えた気がしない• ただ,40スレッド並列でテストケースを食わして統計情報作れるのは最高(3分くらい)
Excelは便利
参加前
•高専プロコンで@colunさんと@mecha_g3に居酒屋でいろいろマラソンマッチについて話してもらったこと
•高専プロコンの部門内容,解法が最後まで検討もつかなくて何もできなかったし,OB戦も一行もコードを書くことなく壇上に上がってしまったことですごく不甲斐なかったこと
• こんな感じでモチベが少し上がってました
目標
• マラソンは改善→思考のサイクルをいつも放棄しがちだった→統計とか取って,何かに気づける環境を面倒くさがらず整えることを意識
• いつも自分が何やってるかわからなくなると手詰まり起こしてた• 基本的にはシンプルな方針,アドホックな改善はその後でというふうにした
• いろいろ入り組むとアルゴリズムの評価がしにくくなるので切り分けできるよう気をつけた
•最適化もできるだけ最初はしないことにした• 必要になった部位だけするように心がけた
• いつも思考ゼロで書き始めちゃうので思考を少しするようにした
1日目
•寝る前に問題読む.
•題意理解したけどさっぱり方針が立たん.• ていうか脳内でやるのキツい系だこれ.
• とりあえず少し考える.• 基本的にUは(端にある時以外)障害物と化す
• Eは便利.
• 行き止まりになってるのがどういうケースかビジュアライズするように改造
• とりあえずコード書く前にseed1~50の統計取るスクリプト書く.
•書き終わって暇だったので少しだけコード書く.
1日目の提出 39.27点
• Sはそんなに迷惑じゃない.• Uを外側のものからSに貪欲に変えていくコードを書く(端からの距離minで)
• 提出 → 39.27点
•出した後,とりあえず左上からfor文で’S’にするコードを書く.• これを90度,180度,270度回転させた感じで4通り試す.
• それとUを外から埋めていくやつのmaxを取る.
• 手元スコアが平均43点くらいになる.
• とりあえず出すのは明日にする
2日目 45.43点
•死んだパスビジュアライズしてると死んでる長いパスを頑張ってあとすこしだけいじって外に連れてってあげればハッピーということは分かる.
• とりあえず以下のような怪しい方針を思いついたので実装• 全てのE点間をできるだけSで繋ぐ.
• 勝手にパス断片を無理やりEに繋ぐような貪欲が部分問題になってそうで嬉しそう.
• とりあえずクソ怪しい適当最小全域木をやる.遅い上に重い.嘘だし.
•愚直にやるよりスコアが低い場合がある Ω\ζ°)チーン• しゃーないので昨日書いた43点くらいのアルゴリズムとmax取る
• 提出 → 45.43
• ああ^~
3-4日目
• いい方法が思いつかないので飽きる.• 学校で適当に考えたりする.
•死んでるパスをなんとかしたいなあと思う.
• ここでさっき適当に構築してた全域木のコストを探索で求めてえなあとなる.• ただパスが交差しちゃだめだ….
• コスト非負の最短経路ならパスに含まれる頂点の重複はないし,最短経路かつ最も長いパスみたいなのを得るとヨサゲとなる.これを求めるのを「適当探索」と呼ぶ.
5日目 70.78点
• とりあえずさっきの「適当探索」でEの間を適当に繋いでいくコードを書く.ただし変更のときに今まで構築したパスを破壊するような移動は認めないようにする(つまり確定マスを保存する二次元配列みたいなのができる).• ちなみに「適当探索」ではUを踏むとコスト0,コストを変更するとコスト1みたいな感じにして,0/1BFSをデックで高速化とかいう謎のことをしていた
•途中で,端っこから伸びてる長い死んだパスをどこかの’E’もしくは端にくっつける怪しいコードを書いたほうが良いことに気づく.• そのために,採点関数を少し弄って端っこから伸びてる長い死んだパスを列挙するようにする.
5日目(2) 70.78点
• Eをその「適当探索」で全域木みたいに繋いだ後,さらに余った手数で死んだパスを「適当探索」で伸ばす.• ローカルスコアよくない.
• 「適当探索」で長いパスを貪欲にEか外に繋いだらローカルスコアが劇的改善!!「Eを予め繋ぐ方針廃止しよう」と決意する.
• そして提出• 提出 → 70.78点 めちゃ伸び!
6日目
•方針があまりにも適当だということには気づいているので,また考察フェーズに戻る.この頃には既に80点台が現れそうになってた気がする.てか現れてた?
• ただ,少なくとも死んだ辺の中で一番長いやつを伸ばすという方針は根拠不明
• あと探索も根拠不明
•正しい探索を考えるとして,以前の行動が影響するからパスを効率的に探す方法がどうにも思いつかない.
6日目(2)
>>>正しい探索を考えるとして,以前の行動が影響するからパスを効率的に探す方法がどうにも思いつかない.<<<
• ↑ここで前の行動が影響するタイプの無茶振りにはビームサーチが強かったことを思い出す(高専プロコン!!!!!!!!!!!)
•無茶振りに対するビームサーチの有用さは高専プロコンのときに@mecha_g3のコードを見て感じてた
• とりあえず適当探索をビームサーチに変えていくことにする
• この日は実装しない
7日目 77.41
• ビームサーチの評価関数は• 変更したら減点, Uを変更したら加点,まだ踏んでないマスを得たら加点
• 実際に盤面のスコアを計算するのは困難だと確信してたのでしない
• というふうに決めた.重みは適当で書き換えてみる.
• ローカルで6点くらい増えた.
•提出 → 77.41点
• コード1000行超えてて汚い.悩んでたら「既に伸びてるが死んでるパスを伸ばす」というのよくないのではと気づく.
•別に端から端まで最初から探索してもよくない?w
8日目(2) 77.41
• とりあえず伸ばすんじゃなくて,端から端までで一番得する経路をビームサーチで貪欲に見つけていくようにする.• 今まで書いたコード全て破壊し1000行→370行くらいに
•!!ローカルで劇的スコア改善 80点超えたっぽい?すげえ• 提出→73.28点・・・あれ?
• Exampleテスト出してみたらMLEしてるあらら
• ビームサーチのためのメモリ節約のコード書く.
• でもその日の提出は諦める
9日目 →80.33→83.65
•昨日のコードのMLEが取れるよううまく改善
•提出 → 80.33
•やったーーーー
•改善方法を考える.まず良い経路を貪欲に取っていく部分も含め二重ビームサーチにして,評価関数のチューニングかな.
• とりあえず探索の中でコストを適当に足したり引いたりしていたのはよくない.
•式を立てやすくするために別々にカウントして,それらのベクトルに対しての評価を評価関数で行えるよう整備する.
(ちなみに)ビームサーチで得た解
•一回探索するだけでこんな長い経路
• しかも90/265!
• テンション上がる!!
9日目 → 80.33→83.65
•経路決めるビームサーチの多様性が絶望的なことに気づく.• 類似性を判定するコード書いてみたら探索の後半ほぼ100%類似してしまっている…
• とりあえず同じEを同じ順番で辿ってるようなパスは同じものとして,20個までしか採用しないことにする.
• このハッシュにはZobrist hasing(前知って感動した)を用いたけど,正直性質的にローリングハッシュで良かった(最後に変えました)
• その後評価関数の係数について考える(次ページ)
9日目 → 80.33→83.65
•以下の3つのパラメータで評価してるからよさそうな係数を探す.• 使った手数usedF
• 実際に得点に結びつくマスの遷移数certainProfit(そこが確定マスかどうかで近似)
• 変えたUの数changed
•経験則的に大体のケースで -20 : +10 : +5 くらいに設定すると良いということに気づく.
•実際それでスコアが平均3%くらい伸びた.• 提出 → 83.65 うおおおお!!!!!!!
• テンション上がってくる.
10日目 86.28
•今clock()で計測してて,時間制限怖くて8秒しか回してない上になんか精度が悪いし,komakiさんの記事(https://topcoder.g.hatena.ne.jp/CKomaki/)を見て時間計測の方法を変える.これは昔のマラソンマッチでもやった経験がある
cycle_per_sec=2500000000とすると大体良いらしい
•時間を9.4sに増やしてから,時間の限りパラメータを変えて試しまくってたところを弄り,ハッシュが衝突しているやつは最後に選ぶところで追加しないようにする.
•提出 → 86.28
• ここで一位取れちゃったせいかマラソンにクソハマりしました.
10日目(2) 86.28
• とりあえずこのへんで今まで未到だった「パラメータのチューニング」に強い憧れを持つ
• さくらのクラウドに惹かれ人生で初めて借りる(直前にあったCodeChefの埋め込みが通る問題のせいも影響してそう)
• クラウドのお手軽さとすごさに感動.知見が広がった.
• 40コア並列のすごさに感動する.
• マラソンというよりクラウドが楽しくて時間を溶かす
• とりあえず入力の F / N(空きじゃないセル数) に対して適切なパラメータを乱数で見つけるスクリプトを書く.
11日目 86.29→ 86.60
•学校に行ってから1コマ分,8コアのクラウド2台ぶん回す.
• このへんからクラウドもマラソンも楽しくて大学のプログラミング演習時間をサボリ出したくなる.やばい.
• なんかよさげな値が見つかっている.練習含め1000円分くらい使ったかいがあったな.提出してみる.
提出 → 86.29
•は?(威圧)
11日目(2) 86.29→ 86.60
• もう少しぶん回したらもう少し良い値が出たので提出
提出 → 86.60
• しょぼい….しょぼい…
•悲しくなってクラウドで別のことして遊ぶ.
12日目
• KUPCでの失敗もあってか悲しくなったので何もせず
•何がネックになっているか・何をごまかしていたかを考えてみる
13日目 88.12
• いろいろ怪しいところを定数倍改善したらよくなるかなと思って弄るが効果なし.もうマラソンいいかなあとなる.
• と思っていたら突然,「得点に結びつくセルの数」を計算するときに「確定セルの数で代替するのは平均的によくないよ!小さいケースでもっと精度よく得点取るべき」という天啓が降ってくる
•天啓にしたがって配られたテスターのコードのsearch関数を10万回呼び出しに制限して呼び出す関数を書く.
•提出 → 88.12
• これマジ?ブレイクスルーすぎる
14日目 →88.32→88.40
• なんかF/Nが0.15以下の盤面についてのパラメータを相変わらずクラウドパワーで探索して提出したら88.32点くらいになった.
• あと提出回数は高々2回となった
•後になって見たときに楽しめるよう,半ばあきらめ気味でコードをリファクタリングして提出しようとしていて,TLEを9.5sにしていたのを9.6sにしてみようとなる.
• ExampleTestの最悪ケースが9750msくらいで通ったので提出.
•提出 → 88.4点になって一瞬だけ3位に(また抜かされた)
良かった点と反省•良かった点
• いつもよりは適当にぐだぐだコードを書くことで手詰まりを起こさなかった
• とにかく思いついたのを思考だけで終わらせずいろいろ試すようにした
(実装時間と改善量のトレードオフとかも意識しつつ)
• 新しいものを使ったり学んだ/ビームサーチ/意図的に衝突させるハッシュ手法/クラウド/
• 統計を取ったおかげで「実は悪い改善なのに良い」と思い込むことがなかった
• クラウドをテストに使うと40コア並列で行うことができ時間を金で買えて良い.
•悪かった点• ビジュアライザを自前で用意せず,貧弱な付属ビジュアライザで頑張ってしまった
• 方針がたまたま軌道に乗っただけで調子に乗りすぎたかもしれない
• 大学の実験・演習を犠牲にしすぎた(あまりにも犠牲にしすぎて自分でもびっくり)
• クラウド力が低すぎて,パラメータのチューニングに完全失敗した
アルゴリズムで重要なとこkyuridenamida
メインルーチン
•基本的には盤面をいくつか持ったビームサーチ
• ただしビーム幅は1,2,…と時間が許す限りパラメータを変更しつつ広げていく.
•盤面の遷移は,今の盤面における,「嬉しい経路」を見つけてそれを採用.使った嬉しい経路に含まれるセルは今後変更できないようにする.
(次のスライドで説明する)
• このビームサーチの評価値は面倒くさかったので経路のビームサーチで使った評価値の和(あんまりよくないのかなあと思ったけどこれで行った)
• いろんなパラメータで試して時間も経って,手数が余ってたら,めいっぱいUのセルをSのセルに書き換えて解を出力
経路発見ビームサーチ
• “嬉しい経路”を見つけるビームサーチを繰り返して使える数が無くなるまで繰り返す.
•嬉しい経路: 基本できるだけ元のマスを変えずに動くイメージ(※具体的な評価関数はスライドにたくさん載ってる通りです)
•嬉しい経路の始点は端点となる点全てをキューに入れて,その「初期点の数」をビーム幅にビームサーチを行う.• 多様性の確保のために,同じEの集合を同じ順序で辿った構築中のパスは同一のハッシュになるよう,ローリングハッシュを用いてハッシュ付け.同一ハッシュのものは各ステップ20個までしか入らないようにする
•探索の途中で外に出たパスは全て解候補に入れ,ビームサーチが終わった後,それらをソートした後,ハッシュが衝突しない上位100個を嬉しい経路候補として採用する.
既に得点に結びついたセルの特定
• ビームサーチで移動先がセルかどうかの判定が必要
•確定していったマスが得点に影響してるのは明らかだが,そうでないセルも多分に得点に結びついているかもしれない
•組み合わせ爆発しない盤面だけでもこれを厳密に計算しておきたい• search()を10万ステップに制限してとりあえずぶん回して,それで新たに見つかった得点に結びついたセルも考慮してあげたらよい.10万で爆発する場合はまあいいや.
ちなみに
•多様性の確保はあまり効果がなかったかもしれません
•統計見ると得点にそんなに結びついてないような気がしました
•久しぶりのマラソンとても楽しかったです.
最終提出のやつのexampleはこれです
Seed=1 1.0Seed=2 0.9846153846153847Seed=3 0.8786549707602339Seed=4 0.8942522321428571Seed=5 0.9643347050754458Seed=6 0.8584254143646409Seed=7 0.9801980198019802Seed=8 0.8917248790972595Seed=9 0.7448453608247423Seed10 0.950948212983224
Seed=9 F/N=323/2328つらいですね…