昨日の続き。
今日は、昨日の自己言及パズルを数式に落とし込んでみる。
自己言及パズル(再掲)
ということで、とりあえずは昨日の問題の再掲から。
次の枠内に 数字がいくつずつあるかを 括弧内に数字で記入しなさい。 +--------------------+ | 2015/07/15 | | 0 ( ) 5 ( ) | | 1 ( ) 6 ( ) | | 2 ( ) 7 ( ) | | 3 ( ) 8 ( ) | | 4 ( ) 9 ( ) | +--------------------+
解けただろうか・・・?
数式へ落とし込む
GLPKを使って問題を解けるようにするためには、まず数式に落とし込んでやらないといけない。
問題は、この自己言及パズルをどのように数式に落とし込むのか、という話だ。
このときにポイントとなるのが、変数の置き方。
単純に考えると、0の個数を、1の個数を、・・・としてしまいそうだけど、そう考えてしまうとなかなか解けない。
というのも、そのような変数の置き方をすると、その変数の中身の情報にアクセス出来ないからだ。
例えば、が3のとき、「の3の個数は1個」という情報が欲しいわけだけれども、その情報を取得する(線形の)関数を考えようとすると、非常に頭が痛い。
じゃあ、どうするのかというと、発想を逆にする。
数の中身の情報を変数として用意しておいて、そこから逆に数を構築するようにする。
具体的には、以下。
まず、「の個数の数字の桁目はか?」という変数を とする。
なお、1なら真と解釈して、0なら偽と解釈する。
このように、真か偽かを表す0-1変数のことを、バイナリ変数と呼んだりもする。
例えば、0の個数の数字が3だとすれば、 かつ となる。
逆に、 かつ なら、0の個数の数字は3だと確定する(※0の個数の数字が1桁だと仮定した場合)。
こうしてやると、の個数の数字というのは、次のような数式で表せることになる。(は最大の桁数)
そして、枠内にあるの個数というのは、最初から枠内にあるの数(とする)と、が1になっているものの数の合計なので、次のような数式で表せる。
あとは、これらが一致すればいいので、次のような条件として書けることになる。
一番重要な条件はこれでOKなんだけど、他にもちょこちょこと細かい条件が必要。
具体的には、以下のとおり。
- の各桁に入る数字は高々1つ
- の桁目に数字が入っていないなら、桁目にも数字が入っていない
- の数字が入っている桁で一番大きい桁の数字は0でない
- の桁目には数字が入っていない(※一つ上の条件を書くときに必要になる)
- の1桁目には数字が絶対入っている
これらの条件を書くためには、「の個数の数字の桁目は空か?」という変数を として用意してあげるといい。
そうすると、それぞれの条件は以下のようになる。
の各桁に入る数字は高々1つ
の桁目に数字が入っていないなら、桁目にも数字が入っていない
の数字が入っている桁で一番大きい桁の数字は0でない
の桁目には数字が入っていない
の1桁目には数字が絶対入っている
ちょっと数式を読み解くのは大変かもだけど、別に難しいことをやっているわけではないので、根気よく読めば分かるはず・・・
あらためて書くと、重要なのは、バイナリ変数を使うというところ。
バイナリ変数を使うというのは最適化問題(特に組合せ最適化問題)の定式化にはよく出てくるので、覚えておきたい。
何にせよ、これで問題が数式に落とし込めたことになる。
この問題は、条件が(変数に関して)すべて一次式になっていて、変数が0か1の整数となっているので、整数計画問題(IP)という問題に分類される。
あとは、これをモデル記述言語で記述して、GLPKに解かせるだけ。
今日はここまで!