たらい回し関数(以下の または tarai)の呼び出しが重なっている(呼び出しが終了する前に呼び出しが行われる)回数の最大値を「呼び出しの深さ」と呼ぶことにします(以下の depth)。
public static string Test() { int count = 0; int depth = 0; float tarai(float x, float y, float z, int d) { count++; depth = d + 1 > depth ? d + 1 : depth; if (x <= y) { return y; } else { return tarai(tarai(x - 1, y, z, d + 1), tarai(y - 1, z, x, d + 1), tarai(z - 1, x, y, d + 1), d + 1); } } return $"{tarai(12, 6, 0, 0)}, count = {count}, depth = {depth}"; }
を実数全体の集合、 を自然数(負ではない整数)全体の集合)とします。 に対して とおきます。 に対して の「幅」 を と定義します。 の「幅」と「呼び出し深さ」(depth)、「呼び出しの回数」(count)の関係を考えます。
上記の C# のコードを while を使って書き直します。
public static string Test() { int count = 0; int depth = 0; float tarai(float x, float y, float z, int d) { count++; d++; depth = d > depth ? d : depth; while (x > y) { float a = tarai(x - 1, y, z, d); float b = tarai(y - 1, z, x, d); float c = tarai(z - 1, x, y, d); (x, y, z) = (a, b, c); count++; d++; depth = d > depth ? d : depth; } return y; } return $"{tarai(12, 6, 0, 0)}, count = {count}, depth = {depth}"; }
while の中の繰り返しの回数を考えます。
とします。
「幅」が 以下のときの「呼び出しの深さ」は 以下、「呼び出しの回数」は 以下であるとします。
「幅」が 以下である に対して
が成り立っているとします。
(1) のとき
(*)より で、 となるのは の場合となります。このとき
となります。よって のとき 、 は のどれかとなります。(*)より の引数の「呼び出しの深さ」は 以下、「呼び出しの回数」は 以下となります。
(2) のとき
(*)より で、 となるのは の場合となりますが、 なのでこれは起こりません。
(3) のとき
(*)より で、 となるのは の場合となります。このとき
となります。よって のとき 、 は のどれかとなります。(*)より の引数の「呼び出しの深さ」は 以下、「呼び出しの回数」は 以下となります。
(4) それ以外のとき
(1)(2)(3)以外のとき、、、 は のどれかとなります。
(4-1) 、 のとき
となるので となります。
(4-2) 、 のとき
となるので となります。
(4-3) 、 のとき
であり、よって となるので となります。
(4-4) 、、 のとき
となるので となります。
(4-5) 、、 のとき
while の繰り返しを抜けずに次の段階に進むことになります。しかし なので次の繰り返しの段階では かつ となり、(4-5)の状態は起こりません。よって繰り返しの回数は最大2回となります。
よって「幅」が ときの「呼び出しの深さ」は 以下、「呼び出しの回数」は 以下となります。
帰納法によって「幅」が ときの「呼び出しの深さ」は 以下、「呼び出しの回数」は 以下となります。