2009 年度「計算数学1」 2009-07-13

§73 ゲームの必勝法(仮称:「ゲーム 31」)

(73.1) これは、2005 年八月に、 県内のある高等学校の生徒さん・先生から質問された、 あるゲームの必勝法に関する問題である。

(73.2) 次のゲームに必勝法はあるか? 「ルール: トランプのひと組から、A から 6 までのカード 24 枚を取り出して表を向けて並べておく。 これらから、二人のプレイヤーが交互に一枚ずつカードを選び出してゆく。 すでに選ばれたカードをもう一度選ぶことはできない。 選び出されたカードに書かれた数の和が初めて 31 を越えたとき、 そのとき選んだプレイヤーが負けとなる。 (二人のうちどちらが選んだカードかによらず、カードの数は足されてゆく。 また、A は 1 と数える。)」

(73.3) 0 から 444444 までの整数と、局面とを対応させよう。 たとえば、114432 は 「1 が 1 枚、2 が 1 枚、3 が 4 枚、4 が 4 枚、5 が 3 枚、6 が 2 枚」 残っている状況に対応させる。

(73.4) まず、0 から 444444 までの数 n を与えると、 その局面になったときの「選び出されたカードに書かれた数の和」を計算して返す関数 int val(int n) を書こう。 n が 0 から 4 まで以外の数字を含むとき、 たとえば 199999 などのときは -1 を返すことにする。 三山崩しで三ケタの数を三つの数に分解したときとほぼ同じ手法を使う。 (似たようなことを 6 回くりかえすのでループにしたくなるかもしれないが、 してもしなくてもよい。)

(73.5) 大きさ 444445 の int 型の配列 a[ ] を用意する。 a[n] の値は、局面 n で自分の番になったらどうするか、 を意味するものとする。

(73.6) ゲームが進むと、n の値は必ず減ってゆくことに注意しよう。 よって、n の値が小さいほうから、順番に見てゆくことになる。 具体的には、次のようになる。

(73.7) n が 0 から始めて 444444 まで、n を一つずつ増やしながら、 次のルールで配列の要素 a[n] の値を決めてゆく。

(73.8) あとは、a[n] を出力するのだが、 na[n] の値を printf() で出力させるには、 %06d と指定するのがよいだろう。これは、幅 6 ケタで、 それに満たないときは上位を 0 で埋めて出力せよ、という指定である。 すべての n について出力させるのは意味がない。 どういう n について出力させるかは、各自で考えること。 また、配列 a[ ] のすべての要素の値を決めてから出力させてもよいし、 a[n] が決まったらすぐに出力、としてもよい。

(73.9) 出力結果をファイルに収めるには、「./a.out > ファイル名」とする。 ただし、その名前のファイルがすでにあれば、上書きされるので注意。

(73.10) その出力を見てプレイすれば、先手か後手は必ず勝てる。 a[444444] が 0 なら後手必勝、そうでなければ先手必勝である。 その意味ではこの表全体が必勝法である。 では、世間でいうような、簡潔にまとめた「必勝法」はあるだろうか?

(73.11) ※ 上で述べたやりかたでは、a[199999] などは使われていない。 使われている要素の数を計算しよう。 1 の残り枚数は 0 枚、1 枚、2 枚、3 枚、4 枚の 5 通り。 ほかのカードについても同様だから、 全部で局面は 56 = 15625 通り、すなわち 15625 個である。 全体では 444445 個だったから、15625/444445 = 00351... となり、 わずか 3.5% ほどしか使われていない。 上の方法を改め、五進表記の 114432 に 「1 が 1 枚、2 が 1 枚、3 が 4 枚、4 が 4 枚、5 が 3 枚、6 が 2 枚」 残っている状況を対応させれば、15625 個の要素からなる配列で済む。 また、a[n] に代入される 001010 などを、五進表記でこう書ける数、と決めれば、 int 型が 16 ビットでも大丈夫である。 さらに、二進表記でこう書ける数と決めれば、int 型の代わりに char 型でもよい。

(73.12) あまり参考になりそうにないが、一応、“こんな感じになる”というものをあげておく。

#include <stdio.h>

int a[444444+1];    /* 上のケタから 1, 2, ..., 6 */

int val(int n);

main() {
    int n;

    for (n = 0; n <= 444444; n++) {
        ...
    }
}


/* 残ったカードがその状態のとき、選び出されたカードは何点? ありえないときは -1 */
int val(int n) {
    ...
}

(73.13) この種のゲームは、カードの種類や枚数、それと勝敗を決する数(上では 31)を変えることで、 いろいろなバリエーションができそうである。


岩瀬順一