« 2011年6月 | メイン | 2012年1月 »

2011年9月13日

GDD2011JP DevQuiz 私の書いた回答コードその2

GDD DevQuiz、今年の本命はスライドパズル。拡張された15パズルです。まずはJavaで解いて、最後にgo言語で上積みしました。

Java実装
Go実装

問題文は以下のとおり。

幅が 3 マスから 6 マスで、高さが 3 マスから 6 マスのボードが与えられます。 各マスは、パネルが置かれているか、壁があるか、空白であるかのいずれかです。 パネルには 1 から 9 あるいは A から Z のいずれかの文字が書かれており、同じ文字の書かれたパネルは存在しません。 壁は 0 個以上存在し、空白のマスはただ 1 つだけ存在します。 例えば、次のようなボードが与えられます。ここで、壁は = で、空白は 0 で表されています。

40=
215
=86

空白は、上下左右のマスのパネルと入れ替えることができます。上のマスのパネルと入れ替えることを U とよび、同様に、下左右のマスのパネルと入れ替えることをそれぞれ D, L, R とよぶものとします。壁を空白やパネルと入れ替えることはできません。
パズルを解くというのは、与えられたボードの各マスを操作して、ゴール状態に持っていくことです。
ゴール状態とは、上の行から各行順番に、左から右に 1, 2, 3, 4, ..., 9, A, ..., Z という順にパネルが並び、最も右下のマスに空白が配置された状態のことです。壁のあるマスに対応するパネルは存在しません。例えば、左上のマスが壁であれば、ボード上に 1 のパネルは存在しません。
(中略)
いま、使うことができる L, R, U, D それぞれの総数があたえられます。 この総数は全パズルで共有されています。 例えばあるパズルを解くために L を使い切ってしまった場合、 他のパズルでは L を使うことはできません。 この総数を超えないようにしながら、なるべくたくさんのパズルを解いてください。

このパズル、5000問あるのです。そして一問が0.01点。100問といてやっと1点。手で解くことをある程度防ぐ設問ですね。

15パズルでの評価関数は各駒のゴールに対するマンハッタン距離合計を使うのがポピュラーな模様。その方向で、A*探索をRubyで試しに実装してみてあまりの遅さに音をあげてすぐにJavaにうつりました。

まずはメモリをじゃぶじゃぶつかって試みてみました。この時点で一晩で1000問程度回答。かなり厳しいです。

次はゴール状態から初期状態に向かってのA*探索を同時に走らせる双方向A*探索してみました。これでもくろみ通り、探索空間が小さくなってかなり成績があがり、3300問程度。その後はパラメータを少し調整し、メモリを増やすなどして、4300問まで頑張りました。

メモリ使いすぎで探索中にMacBook使えないのが辛くて、こんどはメモリをあんまり使わない反復深化深さ優先探索(IDDFS)を実装してみました。が、新たな解はほとんどみつけてくれません。

このあたりで飽きたのでgoでIDDFSを実装しなおして遊んでいたら「探索したノード数が一定数を超えたらその時点で評価値最良のノードひとつだけ残してそこから再度探索する」という乱暴な枝狩りをおもいつきました。これがそれなりに効果的で、最終的には4818問で終了しました。

この問題、30人近くが5000問すべてを解いています。そのひとたちは、そもそもメモリの使い方を最初からすごくしぼっていて、私の無駄遣い富豪プログラミングとは一線を画している様です。評価関数もいろいろ工夫されています。コード公開しているひと多数なので、後でじっくりみてみようと思ってます。

GDD2011JP DevQuiz 私の書いた回答コードその1

今年の11月に、Google Developer's Dayというイベントがあります。
一昨年までは先着順で瞬間的にチケットがなくなっていたらしいです(参加してないので知りませんが)

昨年から参加者には基本的に「開発者向けクイズを解く」ことが課せられるようになりました。その得点上位のひとから順に、入場の権利が与えられるのです。開発者向けイベントなのだから、合理的ですね。

昨年にひきつづき、今年も挑戦しました。各問題のために書いたコードをここにだしておきます。まずは、簡単なほうの問題5つのうち、私がといた3つから。

(この5つの問題は、2つだけ点数にカウントされるのです)

Web Game

シンプルな神経衰弱ゲームです。カードはクリックすることでめくることができます。全 64 セットを解くことで問題クリアとなります。

色が違うだけのカードが、最大1024枚出てくる神経衰弱。これを手でやろうなんていうプログラマはほとんどいないでしょう。

親切なことに、「ヒント」としてGoogle Chromeの機能拡張の例が出ています。これをベースに使えばとても簡単。JavaScriptでDOMさわってクリックして色チェックして覚えて、というだけOKです。

manifestを含むコードはここに置いています。
https://gist.github.com/1213056

Go!

Go 言語で、PNG 画像を入力として受け取り、その画像が何色使っているかを返す関数func CountColor(png io.Reader) intを実装してください。PNG 画像は io.Reader 型で与えられます。なお、入力の画像は R G B の各色の値が 0 から 255 までの 256 段階のいずれかであり、不透明(アルファチャンネルの値が常に 255)であることが保証されています。

これも、goのimage/pngライブラリを使えば実に簡単です。

一人ゲーム

数がいくつか与えられます。なるべく少ない手数で数を全て取り除いてください。あなたは 1 手で、
- 全ての数を半分にする(端数は切り捨て)
- 5 の倍数 (0 を含む) を全て取り除く
のどちらかの操作をすることができます。

簡単簡単、とおもったらちょっとだけひっかかりました。

「5で割りきれてかつ2で割りきれない数が残っている」ときに、その数を含めて5の倍数を取り除くのが良い場合と、良くない場合があるのです。

結局、こんなコード書きました。幅優先探索。

  1. キューから取り出した数セットの要素すべて5でわりきれれば終了
  2. 要素のなかに5で割り切れるが2で割りきれないものがあったら、5の倍数を取り除いたものをキューに入れる
  3. 全要素を2で割ったものをキューに入れる


クリエイティブ・コモンズ・ライセンス
このブログは、次のライセンスで保護されています。 クリエイティブ・コモンズ・ライセンス.