今さら森博嗣「笑わない数学者」のビリヤードの問題を解く(13)～総括編

# 0. はじめに

森博嗣氏の小説「笑わない数学者」に登場するビリヤード問題について下記のシリーズ記事で取り扱ってきた。

- [今さら森博嗣「笑わない数学者」のビリヤードの問題を解く(1)～導入編](https://qiita.com/TETSURO1999/items/fa1db203d48858c607f1)
- [今さら森博嗣「笑わない数学者」のビリヤードの問題を解く(2)～計算機を使う](https://qiita.com/TETSURO1999/items/be4a8b460a690a48236b)
- [今さら森博嗣「笑わない数学者」のビリヤードの問題を解く(3)～計算量オーダーを考える](https://qiita.com/TETSURO1999/items/bcd0cceb66a3b6d5964f)
- [今さら森博嗣「笑わない数学者」のビリヤードの問題を解く(4)～世界記録に挑む](https://qiita.com/TETSURO1999/items/eefbec683a26cc221a57)
- [今さら森博嗣「笑わない数学者」のビリヤードの問題を解く(5)～未解決問題に挑む](https://qiita.com/TETSURO1999/items/78345dadd089c6c90c76)
- [今さら森博嗣「笑わない数学者」のビリヤードの問題を解く(6)～答え合わせ編](https://qiita.com/TETSURO1999/items/5211735a34921a73d1e1)
- [今さら森博嗣「笑わない数学者」のビリヤードの問題を解く(7)～解法の謎に迫る](https://qiita.com/TETSURO1999/items/f6d0839aec49692a03e0)
- [今さら森博嗣「笑わない数学者」のビリヤードの問題を解く(8)～謎は全て解けた](https://qiita.com/TETSURO1999/items/051bb652fb5c9b6c8b6e)
- [今さら森博嗣「笑わない数学者」のビリヤードの問題を解く(9)～巡回平面凄過ぎる](https://qiita.com/TETSURO1999/items/3a91419f2c2e483eb854)
- [今さら森博嗣「笑わない数学者」のビリヤードの問題を解く(10)～40年前のBASICプログラムに敗北する](https://qiita.com/TETSURO1999/items/a88fd15eb1a6f88b20d9)
- [今さら森博嗣「笑わない数学者」のビリヤードの問題を解く(11)～40年越しの回答](https://qiita.com/TETSURO1999/items/4470bedaf3b55d9e5e85)
- [今さら森博嗣「笑わない数学者」のビリヤードの問題を解く(12)～作図のヒミツ](https://qiita.com/TETSURO1999/items/de5bd02c7510ddd6212a)

一般化したビリヤード問題については，魔円陣（Magic Circle）もしくは周期ゴロム定規と呼ばれる数学の問題であり，古くから研究が行われてきたようである。このような先行研究を発見するたびに記事を書いてきたので，今となってはまとまりのない内容になってしまった。このため最新の状況を反映させながら本記事を総括編としてまとめたい。

# 1. 五つの玉の問題：手計算で解く場合

[シリーズ第１回の記事](https://qiita.com/TETSURO1999/items/fa1db203d48858c607f1)では，その後のプログラム化を想定した非人間的なアルゴリズムを採用したが，おそらく一般的な（人間的な）計算手順は下記のようになる。

- ①と②の数は組み合わせで作れないので必ず必要となる
- 回転対称形を排除すると①の玉の位置を固定してよい
- 左右対称形を排除すると②の玉の位置は①の隣と一つ間を空けた場所の二つのみ
- ３個目の玉は，これまで表されていない数の中で最小値とし，場所は残り三つから選ぶ
- ４個目の玉も，これまで表されていない数の中で最小値とし，場所は残り二つから選ぶ
- 合計が２１となることから５個目の玉の数は自動的に決まり，場所も決まる
- 重複する数をチェックして排除する　※４個目を置くときエラーになる場合もある

上記の手順により，実際に場合分けを行った結果を表１に示す。なお<font color=red>赤色</font>は新たに置く玉を示す。(…)内の数字は表すことのできる数字である。

<table>
<caption>表１　手計算による場合分け</caption>
<thead>
<tr><th style="text-align:center">１個目</th>
    <th style="text-align:center">２個目</th>
    <th style="text-align:center">３個目</th>
    <th style="text-align:center">４個目</th>
    <th style="text-align:center">５個目</th>
    <th style="text-align:center">判定</th></tr>
</thead>
<tbody>
<tr></tr>
<tr><td rowspan=23><font color=red>①</font>－－－－<br>(1)</td>
    <td rowspan=11>①<font color=red>②</font>－－－<br>(1,2,3)</td>
    <td rowspan=3>①②<font color=red>④</font>－－<br>(1,2,3,4,6,7)</td>
    <td>①②④<font color=red>⑤</font>－</td>
    <td>①②④⑤<font color=red>⑨</font></td>
    <td>×④+⑤=⑨</td></tr>
<tr></tr>
<tr><td>①②④－<font color=red>⑤</font></td>
    <td>①②④<font color=red>⑨</font>⑤</td>
    <td>×⑤+①=②+④</td></tr>
<tr></tr>
<tr><td rowspan=3>①②－<font color=red>④</font>－<br>(1,2,3,4)</td>
    <td>①②<font color=red>⑤</font>④－</td>
    <td>①②⑤④<font color=red>⑨</font></td>
    <td>×⑤+④=⑨</td></tr>
<tr></tr>
<tr><td>①②－④<font color=red>⑤</font></td>
    <td>①②<font color=red>⑨</font>④⑤</td>
    <td>×⑨=④+⑤</td></tr>
<tr></tr>
<tr><td rowspan=3>①②－－<font color=red>④</font><br>(1,2,3,4,5,7)</td>
    <td>①②<font color=red>⑥</font>－④</td>
    <td>①②⑥<font color=red>⑧</font>④</td>
    <td>×②+⑥=⑧</td></tr>
<tr></tr>
<tr><td>①②－<font color=red>⑥</font>④</td>
    <td>①②<font color=red>⑧</font>⑥④</td>
    <td>×②+⑧=⑥+④</td></tr>
<tr></tr>
<tr><td rowspan=11>①－<font color=red>②</font>－－<br>(1,2)</td>
    <td rowspan=3>①<font color=red>③</font>②－－<br>(1,2,3,4,5,6)</td>
    <td>①③②<font color=red>⑦</font>－</td>
    <td>①③②⑦<font color=red>⑧</font></td>
    <td>×⑧+①=②+⑦</td></tr>
<tr></tr>
<tr><td>①③②－<font color=red>⑦</font></td>
    <td>①③②<font color=red>⑧</font>⑦</td>
    <td>×⑧=⑦+①</td></tr>
<tr></tr>
<tr><td rowspan=3>①－②<font color=red>③</font>－<br>(1,2,3,5)</td>
    <td>①<font color=red>④</font>②③－</td>
    <td></td>
    <td>×①+④=②+③</td></tr>
<tr></tr>
<tr><td>①－②③<font color=red>④</font></td>
    <td></td>
    <td>×④+①=②+③</td></tr>
<tr></tr>
<tr><td rowspan=3>①－②－<font color=red>③</font><br>(1,2,3,4)</td>
    <td>①<font color=red>⑤</font>②－③</td>
    <td>①⑤②<font color=red>⑩</font>③</td>
    <td>○</td></tr>
<tr></tr>
<tr><td>①－②<font color=red>⑤</font>③</td>
    <td>①<font color=red>⑩</font>②⑤③</td>
    <td>×⑩＝②+⑤+③</td></tr>
</tbody>
</table>

こうやってしらみ潰しに調べても高々１２通りである。唯一の解は ①⑩②⑤③ となるが，左右反転させた ①③⑤②⑩ のほうが分かり易いだろう。

一見非効率的に見える方法だが，実は次項で述べる計算機手法はこの手順に沿ったアルゴリズムを採用しており，有限体理論を用いた手法を除き，全探索する方法としては最も探索効率が良いと考えている。

# 2. 一般的な問題：力技で全探索する方法

[シリーズ第２回の記事](https://qiita.com/TETSURO1999/items/be4a8b460a690a48236b)で紹介したC言語版プログラムよりも[第１１回](https://qiita.com/TETSURO1999/items/4470bedaf3b55d9e5e85)の Visual Basic 版プログラムほうが格段に優れたアルゴリズムを採用している。下記はそれをC言語に移植したものであるが，Visual Basic（というより.NET環境）からC言語に移植しただけで四割ほどスピードがアップした。


実装コードを以下に示す。

<details><summary><font color=blue><b>C言語版プログラム billiard2.c のソースはコチラ</b></font></summary>

```c:billiard2.c
#include <stdio.h>
#include <stdlib.h>
#define	FALSE			(0)									// 偽の値
#define	TRUE			(!FALSE)							// 真の値
#define	MIN_BALL_NUM	(3)									// ボールの個数の最大値
#define	MAX_BALL_NUM	(20)								// ボールの個数の最大値
#define	MAX_BALL_SUM	(1+MAX_BALL_NUM*(MAX_BALL_NUM-1))	// ボールの総和の最大値
static	int		count;										// 解の個数
static	int		ball_num;									// ボールの個数
static	int		ball_sum;									// ボールの総和
static	int		ball[1 + MAX_BALL_NUM];						// ボールの配置
static	int		flag[1 + MAX_BALL_SUM];						// 重複チェック用配列
//------------------------------------------------------------------------------
// 解の出力
//------------------------------------------------------------------------------
static	void	output( void ) {
	fprintf( stdout, "(%d) %d", ++count, ball[1] );
	if( ball[2] < ball[ball_num] ) {
		for( int i = 2; i <= ball_num; i++ )
			fprintf( stdout, " %d", ball[i] );
	} else {
		for( int i = ball_num; i >= 2; i-- )
			fprintf( stdout, " %d", ball[i] );
	}
	fprintf( stdout, "\n" );
}
//------------------------------------------------------------------------------
// インクリメンタル加算チェック，未配置の最小数の位置 k とする
//------------------------------------------------------------------------------
static	int		icheck( int k ) {
	int	j, r, t;
	for( j = 1, r = k, t = 0; j < ball_num; j++ ) {
		if( ball[r] == 0 ) break;
		if( ( t += ball[r] ) >= ball_sum || flag[t] ) goto SKIP;
		flag[t] = TRUE;
		for( int i = j + 1, l = k - 1, u = t; i < ball_num; i++ ) {
			if( ball[l] == 0 ) break;
			if( ( u += ball[l] ) >= ball_sum || flag[u] ) {
				for(;;) {
					u -= ball[l]; flag[u] = FALSE;
					if( u == t ) break;
					if( ++l > ball_num ) l = 1;
				}
				goto SKIP;
			}
			flag[u] = TRUE;
			if( --l == 0 ) l = ball_num;
		}
		if( ++r > ball_num ) r = 1;
	}
	return TRUE;
SKIP:
	while( j > 1 ) {
		j--;
		t -= ball[r]; flag[t] = FALSE;
		for( int i = j + 1, l = k - 1, u = t; i < ball_num; i++ ) {
			if( ball[l] == 0 ) break;
			u += ball[l]; flag[u] = FALSE;
			if( --l == 0 ) l = ball_num;
		}
		if( --r == 0 ) r = ball_num;
	}
	return FALSE;
}
//------------------------------------------------------------------------------
// インクリメンタル加算クリア，未配置の最小数の位置 k とする
//------------------------------------------------------------------------------
static	void	iclear( int k ) {
	for( int j = 1, r = k, t = 0; j < ball_num; j++ ) {
		if( ball[r] == 0 ) break;
		t += ball[r]; flag[t] = FALSE;
		for( int i = j + 1, l = k - 1, u = t; i < ball_num; i++ ) {
			if( ball[l] == 0 ) break;
			u += ball[l]; flag[u] = FALSE;
			if( --l == 0 ) l = ball_num;
		}
		if( ++r > ball_num ) r = 1;
	}
}
//------------------------------------------------------------------------------
// 再帰チェック関数，未配置の最小数 m，探索範囲の上限 range とする
//------------------------------------------------------------------------------
static	void	rcheck( int m, int range ) {
	for( int k = 2; k <= range; k++ ) {
		if( ball[k] != 0 ) continue;
		ball[k] = m;
		//----------------------------------------------------------------------
		// インクリメンタル加算チェック
		//----------------------------------------------------------------------
		if( icheck( k ) ) {
			//------------------------------------------------------------------
			// 次の未配置の最小数を探す
			//------------------------------------------------------------------
			int		s;
			for( s = m + 1; s <= ball_sum; s++ )
				if( !flag[s] ) break;
			if( s >= ball_sum ) {
				//--------------------------------------------------------------
				// 解の表示
				//--------------------------------------------------------------
				output();
			} else {
				//--------------------------------------------------------------
				// 再帰的に次の数をチェックする
				//--------------------------------------------------------------
				rcheck( s, ball_num );
			}
			//------------------------------------------------------------------
			// インクリメンタル加算クリア
			//------------------------------------------------------------------
			iclear( k );
		}
		ball[k] = 0;
	}
}
//------------------------------------------------------------------------------
// メイン関数
//------------------------------------------------------------------------------
int		main( int argc, char *argv[] ) {
	//--------------------------------------------------------------------------
	// ヘルプメッセージ
	//--------------------------------------------------------------------------
	if( argc < 2 ) {
		fprintf( stderr, "Usage: billiard2(.exe) [ball-num]\n" );
		return( -1 );
	}
	//--------------------------------------------------------------------------
	// 準備
	//--------------------------------------------------------------------------
	ball_num = atoi( argv[1] );
	if( ball_num < MIN_BALL_NUM || ball_num > MAX_BALL_NUM ) {
		fprintf( stderr, "ボールの個数 %d が不正です!!\n", ball_num );
		return( -1 );
	}
	ball_sum = 1 + ball_num * ( ball_num - 1 );
	//--------------------------------------------------------------------------
	// 1 のボールは先頭に固定する
	// 2 のボールは二番目から中央まで移動する
	//--------------------------------------------------------------------------
	ball[1] = 1; flag[1] = TRUE;
	rcheck( 2, ( 1 + ball_num ) / 2 );
	//--------------------------------------------------------------------------
	// ボール個数が偶数かつ 2 のボールが中央のとき
	// 3 のボールは二番目から 2 のボールの手前まで移動する
	//--------------------------------------------------------------------------
	if( ( ball_num & 1 ) == 0 ) {
		int		k = 1 + ball_num / 2;
		ball[k] = 2; flag[2] = TRUE;
		rcheck( 3, k - 1 );
	}
	return( 0 );
}
```

</details>

あまりにも速くなったので，これまで未到達であった玉の数 $n = 18$ の場合でも三時間強で計算できるようになった。実行結果を以下に示す。
※Core i7-13700（ベース2.1GHz，ブースト5.2GHz）マシンの場合

```plantuml
@startuml
<style>
root {
  margin: 0;
  padding: 0;
}
caption {
  fontSize: 17;
}
</style>
title <img:https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3490655/7576a2db-a43a-0c8c-2495-b015b47dbceb.png>
caption 図１　ボールの数と計算時間
@enduml
```
<details><summary><font color=blue><b>C言語版プログラム billiard2.exe の実行結果はコチラ</b></font></summary>

<table>
<caption>表２　C言語版プログラムの実行結果</caption>
<thead>
<tr><th style="text-align:center">玉の数</th>
    <th style="text-align:center">計算時間[s]</th>
    <th style="text-align:center">解となる玉の配置</th></tr>
</thead>
<tbody>
<tr><td style="text-align:right">3</td>
    <td style="text-align:right">0.01</td>
    <td style="text-align:left">(1) 1 2 4</td></tr>
<tr><td style="text-align:right">4</td>
    <td style="text-align:right">0.01</td>
    <td style="text-align:left">(1) 1 2 6 4<br>
(2) 1 3 2 7</td></tr>
<tr><td style="text-align:right">5</td>
    <td style="text-align:right">0.01</td>
    <td style="text-align:left">(1) 1 3 10 2 5</td></tr>
<tr><td style="text-align:right">6</td>
    <td style="text-align:right">0.01</td>
    <td style="text-align:left">(1) 1 2 5 4 6 13<br>
(2) 1 2 7 4 12 5<br>
(3) 1 3 2 7 8 10<br>
(4) 1 3 6 2 5 14<br>
(5) 1 7 3 2 4 14</td></tr>
<tr><td style="text-align:right">7</td>
    <td style="text-align:right">0.01</td>
    <td style="text-align:left">なし</td></tr>
<tr><td style="text-align:right">8</td>
    <td style="text-align:right">0.01</td>
    <td style="text-align:left">(1) 1 2 10 19 4 7 9 5<br>
(2) 1 6 12 4 21 3 2 8<br>
(3) 1 4 22 7 3 6 2 12<br>
(4) 1 4 2 10 18 3 11 8<br>
(5) 1 3 8 2 16 7 15 5<br>
(6) 1 3 5 11 2 12 17 6</td></tr>
<tr><td style="text-align:right">9</td>
    <td style="text-align:right">0.01</td>
    <td style="text-align:left">(1) 1 2 4 8 16 5 18 9 10<br>
(2) 1 11 8 6 4 3 2 22 16<br>
(3) 1 6 4 24 13 3 2 12 8<br>
(4) 1 4 7 6 3 28 2 8 14</td></tr>
<tr><td style="text-align:right">10</td>
    <td style="text-align:right">0.01</td>
    <td style="text-align:left">(1) 1 2 6 18 22 7 5 16 4 10<br>
(2) 1 5 4 13 3 8 7 12 2 36<br>
(3) 1 4 2 20 8 9 23 10 3 11<br>
(4) 1 6 9 11 29 4 8 2 3 18<br>
(5) 1 4 3 10 2 9 14 16 6 26<br>
(6) 1 3 9 11 6 8 2 5 28 18</td></tr>
<tr><td style="text-align:right">11</td>
    <td style="text-align:right">0.02</td>
    <td style="text-align:left">なし</td></tr>
<tr><td style="text-align:right">12</td>
    <td style="text-align:right">0.07</td>
    <td style="text-align:left">(1) 1 2 14 4 37 7 8 27 5 6 13 9<br>
(2) 1 2 9 8 14 4 43 7 6 10 5 24<br>
(3) 1 2 12 31 25 4 9 10 7 11 16 5<br>
(4) 1 2 14 12 32 19 6 5 4 18 13 7<br>
(5) 1 5 12 21 29 11 3 16 4 22 2 7<br>
(6) 1 4 19 20 27 3 6 25 7 8 2 11<br>
(7) 1 4 16 3 15 10 12 14 17 33 2 6<br>
(8) 1 14 3 2 4 7 21 8 25 10 12 26<br>
(9) 1 22 14 20 5 13 8 3 4 2 10 31<br>
(10) 1 7 13 12 3 11 5 18 4 2 48 9<br>
(11) 1 3 23 24 6 22 10 11 18 2 5 8<br>
(12) 1 3 8 9 5 19 23 16 13 2 28 6<br>
(13) 1 3 12 34 21 2 8 9 5 6 7 25<br>
(14) 1 4 7 3 16 2 6 17 20 9 13 35<br>
(15) 1 8 10 5 7 21 4 2 11 3 26 35<br>
(16) 1 15 5 3 25 2 7 4 6 12 14 39<br>
(17) 1 14 10 20 7 6 3 2 17 4 8 41<br>
(18) 1 4 20 3 40 10 9 2 15 16 6 7</td></tr>
<tr><td style="text-align:right">13</td>
    <td style="text-align:right">0.40</td>
    <td style="text-align:left">なし</td></tr>
<tr><td style="text-align:right">14</td>
    <td style="text-align:right">2.72</td>
    <td style="text-align:left">(1) 1 2 21 17 11 5 9 4 26 6 47 15 12 7<br>
(2) 1 2 13 7 5 14 34 6 4 33 18 17 21 8<br>
(3) 1 2 28 14 5 6 9 12 48 18 4 13 16 7<br>
(4) 1 6 8 22 4 5 33 21 3 20 32 16 2 10<br>
(5) 1 5 2 24 15 29 14 21 13 4 33 3 9 10<br>
(6) 1 10 48 9 19 4 8 6 7 17 3 2 34 15<br>
(7) 1 4 20 2 12 3 6 7 33 11 8 10 35 31<br>
(8) 1 12 48 6 2 38 3 22 7 10 11 5 4 14<br>
(9) 1 9 21 25 3 4 8 5 6 16 2 36 14 33<br>
(10) 1 9 5 40 3 4 21 35 16 18 2 6 11 12<br>
(11) 1 3 12 7 20 14 44 6 5 24 2 28 8 9<br>
(12) 1 8 21 45 6 7 11 17 3 2 10 4 23 25<br>
(13) 1 9 14 26 4 2 11 5 3 12 27 34 7 28<br>
(14) 1 3 24 6 12 14 11 55 7 2 8 5 16 19<br>
(15) 1 3 5 6 25 32 23 10 18 2 17 7 22 12<br>
(16) 1 4 6 31 3 13 2 7 14 12 17 46 8 19<br>
(17) 1 10 22 34 27 12 3 4 2 14 24 5 8 17<br>
(18) 1 5 23 27 42 3 4 11 2 19 12 10 16 8<br>
(19) 1 8 3 10 23 5 56 4 2 14 15 17 7 18<br>
(20) 1 4 8 52 3 25 18 2 9 24 6 10 7 14</td></tr>
<tr><td style="text-align:right">15</td>
    <td style="text-align:right">20.33</td>
    <td style="text-align:left">なし</td></tr>
<tr><td style="text-align:right">16</td>
    <td style="text-align:right">163.20</td>
    <td style="text-align:left">なし</td></tr>
<tr><td style="text-align:right">17</td>
    <td style="text-align:right">1,323.52</td>
    <td style="text-align:left">(1) 1 2 4 8 16 32 27 26 11 9 45 13 10 29 5 17 18<br>
(2) 1 7 31 2 11 3 9 36 17 4 22 6 18 72 5 10 19<br>
(3) 1 7 12 44 25 41 9 17 4 6 22 33 13 2 3 11 23<br>
(4) 1 21 11 50 39 13 6 4 14 16 25 26 3 2 7 8 27<br>
(5) 1 3 12 10 31 7 27 2 6 5 19 20 62 14 9 28 17<br>
(6) 1 7 3 15 33 5 24 68 2 14 6 17 4 9 19 12 34</td></tr>
<tr><td style="text-align:right">18</td>
    <td style="text-align:right">11,314.37</td>
    <td style="text-align:left">(1) 1 2 18 4 6 37 9 14 79 7 8 11 16 13 32 12 5 33<br>
(2) 1 2 24 15 4 16 12 25 38 50 14 17 5 8 10 11 49 6<br>
(3) 1 2 42 13 4 11 23 14 12 20 7 29 18 71 10 9 16 5<br>
(4) 1 2 41 12 5 6 8 7 9 4 29 36 18 22 10 27 25 45<br>
(5) 1 2 27 7 13 5 21 22 19 12 4 24 32 10 23 71 6 8<br>
(6) 1 2 20 6 12 47 15 30 27 21 4 39 7 10 34 19 5 8<br>
(7) 1 9 2 3 23 7 43 4 20 16 6 19 34 51 13 8 31 17<br>
(8) 1 12 34 5 16 60 36 14 6 3 15 4 7 37 17 8 2 30<br>
(9) 1 5 2 25 4 12 44 34 18 20 19 26 21 3 11 40 13 9<br>
(10) 1 9 2 4 43 36 5 26 8 22 46 28 7 17 3 18 19 13<br>
(11) 1 17 2 10 4 7 38 5 8 24 15 42 6 22 3 68 9 26<br>
(12) 1 4 2 16 12 13 8 24 38 44 15 27 10 9 17 3 11 53<br>
(13) 1 3 34 2 45 7 18 5 48 16 10 57 6 13 14 8 9 11<br>
(14) 1 37 11 24 26 16 29 23 18 33 6 8 13 4 3 2 10 43<br>
(15) 1 5 24 4 32 17 54 15 7 9 26 52 3 8 10 2 25 13<br>
(16) 1 27 3 13 2 6 50 5 9 11 12 10 7 19 41 53 4 34<br>
(17) 1 5 4 45 11 27 41 7 24 26 13 8 43 16 2 12 3 19<br>
(18) 1 6 11 3 2 28 34 35 4 38 10 27 19 24 12 13 32 8<br>
(19) 1 22 20 7 9 10 5 29 3 8 17 13 48 4 2 12 21 76<br>
(20) 1 14 39 19 2 10 28 8 35 6 3 13 4 7 18 5 32 63<br>
(21) 1 4 36 35 32 49 7 6 3 14 8 12 27 19 2 24 18 10<br>
(22) 1 3 25 7 20 30 46 15 38 10 6 5 13 47 2 17 14 8<br>
(23) 1 3 11 13 5 20 10 7 51 6 16 34 26 19 2 44 31 8<br>
(24) 1 23 26 5 11 9 18 21 13 6 22 35 71 2 8 4 3 29<br>
(25) 1 17 9 31 6 2 12 16 5 47 3 4 15 10 13 11 62 43<br>
(26) 1 6 52 16 50 3 31 5 9 15 4 13 8 2 20 35 26 11<br>
(27) 1 3 20 9 58 19 2 25 36 13 22 31 12 5 11 26 8 6<br>
(28) 1 6 9 32 18 5 14 21 4 13 11 91 2 8 12 31 3 26<br>
(29) 1 7 15 24 20 9 2 3 38 13 6 26 4 12 21 79 10 17<br>
(30) 1 4 27 43 6 11 2 7 3 18 16 8 14 39 25 33 15 35<br>
(31) 1 12 36 19 25 7 11 4 5 30 3 21 2 8 6 72 17 28<br>
(32) 1 5 14 4 22 12 9 50 27 51 3 8 2 15 16 32 7 29<br>
(33) 1 4 16 7 17 25 8 26 3 6 13 39 2 12 18 64 36 10<br>
(34) 1 12 3 5 9 27 4 2 66 10 24 25 28 11 7 19 32 22<br>
(35) 1 7 35 32 36 44 4 12 17 5 23 2 24 15 31 6 3 10<br>
(36) 1 6 36 9 16 13 26 2 3 15 4 8 65 21 35 14 23 10<br>
(37) 1 39 18 4 6 76 15 11 9 3 31 2 14 5 8 17 7 41<br>
(38) 1 20 14 59 19 5 3 9 33 7 4 2 16 10 15 23 37 30<br>
(39) 1 9 12 16 14 3 15 8 27 31 59 2 4 7 36 39 5 19<br>
(40) 1 3 11 20 7 33 10 9 17 22 6 2 16 5 32 12 13 88<br>
(41) 1 5 22 24 12 13 8 26 9 30 2 29 19 53 37 3 4 10<br>
(42) 1 15 28 6 12 11 3 5 2 20 36 35 13 4 38 9 45 24<br>
(43) 1 12 9 5 38 16 28 3 32 37 2 18 58 8 7 4 6 23<br>
(44) 1 3 8 45 9 18 10 6 7 17 2 29 15 5 59 38 14 21<br>
(45) 1 4 3 36 23 6 18 22 9 2 19 63 14 34 16 10 15 12<br>
(46) 1 7 3 27 6 12 23 5 19 2 51 9 4 16 34 57 17 14<br>
(47) 1 12 23 3 7 21 16 18 22 2 6 11 32 20 5 4 90 14<br>
(48) 1 4 6 17 13 8 16 9 20 2 12 72 15 3 32 7 19 51<br>
(49) 1 5 9 8 4 7 32 24 18 2 35 3 10 49 29 25 16 30<br>
(50) 1 4 27 13 70 17 6 29 22 2 9 3 7 18 30 8 26 15<br>
(51) 1 9 29 4 20 11 45 16 65 2 3 12 6 7 19 8 14 36</td></tr>
</tbody>
</table>

</details>

# 3. 有限体理論を用いた方法

## 3.1 解の個数公式（予想）

[シリーズ第６回の記事](https://qiita.com/TETSURO1999/items/5211735a34921a73d1e1)より引用する。

素数 $p$，自然数 $m$ として，ビリヤードの玉の数 $n = 1 + p^m$ 個と表されるとき，

```math
\textsf{解の個数} = \frac{\phi(1 + n(n - 1))}{6m}
```

と予想されている。ここで $\phi(x)$ はオイラーの <i>&#x03D5;</i> 関数といい，$x$ 以下で $x$ と互いに素な自然数の個数を返す。自然数 $x$ を素数 $p_1,\hspace1px p_2,\hspace1px p_3,\hspace1px\cdots,\hspace1px p_q$ のべき乗数の積として以下のように定義する。

```math
\begin{aligned}
x &= p_1^{k_1} \times p_2^{k_2} \times p_3^{k_3} \times \cdots \times p_q^{k_q} \\
&= \prod_{i = 1}^{q} p_i^{k_i}
\end{aligned}
```

このとき $\phi(x)$ は次のように定義される。

```math
\begin{aligned}
\phi(x) &= x \times \left( 1 - \frac{1}{p_1} \right) \times \left( 1 - \frac{1}{p_2} \right) \times \left( 1 - \frac{1}{p_3} \right) \times \cdots \times \left( 1 - \frac{1}{p_q} \right) \\
&= x \prod_{i = 1}^{q} \left( 1 - \frac{1}{p_i} \right)
\end{aligned}
```

とくに $x$ が素数の場合は $\phi(x) = x - 1$ となる。

JavaScript によるサンプルコードを以下に示す。

```javascript:オイラーの <i>&#x03D5;</i> 関数
function euler_totient(n) {
	var	r = n;
	for(var i = 2; i * i <= n; i++) {
		if(n % i != 0) continue;
		while(n % i == 0) n /= i;
		r -= r / i;
	}
	if(n > 1) r -= r / n;
	return r;
}
```

## 3.2 玉の数が「１＋素数」個の場合

[シリーズ第４回の記事](https://qiita.com/TETSURO1999/items/eefbec683a26cc221a57)で紹介したプログラムを少し改良した。主な改良点を以下に示す。

- 数列のパラメータの順を入れ替えた。これは参考文献の順とうっかり逆にしてしまっていたものを今回を機に修正したものだ。
- 解の個数の公式に従い，全ての解を得られたら直ちに終了するようにした。これで一桁以上速くなった。
- 最初の解を得たら直ちに終了するオプション `/F` を追加した。これは玉の数が多くなると全ての解を保持しておくことがメモリの制約によって難しくなるため。

実装コードを以下に示す。

<details><summary><font color=blue><b>JavaScript プログラム GaloisField.js のソースはコチラ</b></font></summary>

```javascript:GaloisField.js
var	args = WScript.Arguments.Unnamed;
var	opts = WScript.Arguments.Named;
var	ret = main(args, opts)
try {
	WScript.Quit(ret);
} catch(e) {
	/* 何もしない */
}
//------------------------------------------------------------------------------
// メイン関数
//------------------------------------------------------------------------------
function main(args, opts) {
	//--------------------------------------------------------------------------
	// ヘルプメッセージ
	//--------------------------------------------------------------------------
	if(args.Count == 0) {
		WScript.Echo("ボールの個数が「１＋素数」の場合の解を求めます。");
		WScript.Echo("");
		WScript.Echo("GaloisField(.js) (オプション) [素数]");
		WScript.Echo("");
		WScript.Echo("/F　最初の解を発見したら終了します。");
		return false;
	}
	//--------------------------------------------------------------------------
	// コマンドライン解析
	//--------------------------------------------------------------------------
	var	p = parseInt(args(0));
	var	f_flag = opts.Exists("F") ? true : false;
	//--------------------------------------------------------------------------
	// 線形再帰剰余数列のパラメータ (a,b,c) を全探索する
	//--------------------------------------------------------------------------
	var	num = 1 + p;						// ボールの個数
	var	sum = 1 + num * (num - 1);			// ボールの数値の総和
	var	limit = euler_totient(sum) / 6;		// 解の個数（予想値）
	var	count = 0;							// 解の個数
	var	d = [];								// 解（ボール配置）
BREAK:
	for( var a = 0; a < p; a++ ) {
		for( var b = 0; b < p; b++ ) {
			for( var c = 1; c < p; c++ ) {
				//--------------------------------------------------------------
				// 完全差集合を作成する
				//--------------------------------------------------------------
				var	s = [0, 0, 1];
				for(i = 3; i < sum + 2; i++) {
					var	v = (a * s[i - 1] + b * s[i - 2] + c * s[i - 3]) % p;
					s.push(v);
					if(v == 0 && s[i - 1] == 0) break;
				}
				if(i != sum + 1) continue;
				var	t = [];
				for(var i = 0; i < s.length; i++)
					if(s[i] == 0) t.push(i);
				//--------------------------------------------------------------
				// 完全差集合をボール配置に変換する
				//--------------------------------------------------------------
				var	u = [];
				for(var i = 1; i < t.length; i++)
					u.push(t[i] - t[i - 1]);
				//--------------------------------------------------------------
				// ボール配置を正規化（左右反転）
				//--------------------------------------------------------------
				if(u[1] > u[u.length - 2]) u.reverse();
				u.pop();
				//--------------------------------------------------------------
				// ボール配置のチェック
				//--------------------------------------------------------------
				if(!check_ball(u)) continue;
				//--------------------------------------------------------------
				// ボール配置の出力
				//--------------------------------------------------------------
				var	k = [a, b, c].join(" ")
				var	v = u.join(" ");
				if(f_flag) {
					WScript.Echo([num, k, v].join("\t"));
					return true;
				} else if(d[v] == null) {
					d[v] = k;
					if(++count >= limit) break BREAK;
				}
			}
		}
	}
	//--------------------------------------------------------------------------
	// ボール配置をソートして出力する
	//--------------------------------------------------------------------------
	var	e = [];
	for(var v in d)
		e.push({ key:d[v], val:v, arr:v.split(" ") });
	e.sort(function(a, b) {
		for(var i = 0; i < a.arr.length; i++)
			if(a.arr[i] != b.arr[i]) return a.arr[i] - b.arr[i];
		return 0;
	});
	for(var i = 0; i < e.length; i++)
		WScript.Echo([num, e[i].key, e[i].val].join("\t"));
	//--------------------------------------------------------------------------
	// 解の個数を出力する
	//--------------------------------------------------------------------------
	WScript.Echo(e.length);
	return true;
}
//------------------------------------------------------------------------------
// ボール配置のチェック
//------------------------------------------------------------------------------
function check_ball(ball) {
	var	num = ball.length;
	var	sum = 1 + num * (num -  1);
	for(var i = 0, s = 0; i < ball.length; i++)
		s += ball[i];
	if(sum != s) return false;
	var	flag = [];
	for(var i = 0; i <= sum; i++)
		flag.push(false);
	for(var i = 0; i < ball.length - 1; i++) {
		for(var j = 0, s = 0; i + j >= 0; j--) {
			s += ball[i + j];
			if(flag[s] || flag[sum - s]) return false;
			flag[s] = true;
			flag[sum - s] = true;
		}
	}
	return true;
}
//------------------------------------------------------------------------------
// オイラーのφ関数
//------------------------------------------------------------------------------
function euler_totient(n) {
	var	r = n;
	for(var i = 2; i * i <= n; i++) {
		if(n % i != 0) continue;
		while(n % i == 0) n /= i;
		r -= r / i;
	}
	if(n > 1) r -= r / n;
	return r;
}
```

</details>

<details><summary><font color=blue><b>高速 JavaScript エンジンを用いるバッチファイル Chakra.cmd のソースはコチラ</b></font></summary>

```:Chakra.cmd
@echo off
setlocal
if "%1"=="" goto USAGE
rem ----------------------------------------------------------------------------
rem スクリプトファイルの拡張子が省略された場合 .JS とする
rem ----------------------------------------------------------------------------
if "%~x1"=="" (
  set SCRIPTFILE=%~1.JS
) else (
  set SCRIPTFILE=%~1
)
rem ----------------------------------------------------------------------------
rem スクリプトファイルが存在する場合
rem ----------------------------------------------------------------------------
if exist %SCRIPTFILE% (
  set SCRIPTPATH=%SCRIPTFILE%
  goto SKIP
)
rem ----------------------------------------------------------------------------
rem スクリプトファイルをパスから検索する
rem ----------------------------------------------------------------------------
for %%I in ( %SCRIPTFILE% ) do set SCRIPTPATH=%%~$PATH:I
if "%SCRIPTPATH%"=="" (
  echo %SCRIPTFILE% が見つかりません!!
  exit /b
)
:SKIP
rem ----------------------------------------------------------------------------
rem スクリプトファイルのオプション解析
rem ----------------------------------------------------------------------------
set SCRIPTARGS=%SCRIPTPATH%
:LOOP
  if "%~2"=="" goto BREAK
  set SCRIPTARGS=%SCRIPTARGS% %2
  shift /2
goto LOOP
:BREAK
rem ----------------------------------------------------------------------------
rem スクリプトの実行
rem ----------------------------------------------------------------------------
cscript //E:{1B7CD997-E5FF-4932-A7A6-2A9E636DA385} %SCRIPTARGS%
exit  /b
:USAGE
rem ----------------------------------------------------------------------------
rem ヘルプメッセージ
rem ----------------------------------------------------------------------------
echo Microsoft Edge の chakra エンジンを用いて Javascript を高速実行します。
echo.
echo CHAKRA(.CMD) [スクリプトファイル名(.JS)]  [スクリプトオプション] ...
echo.
echo スクリプトファイルの拡張子を省略すると .JS とします。
exit /b
```

</details>

実行例を以下に示す。引数を何も指定しないとヘルプメッセージを表示する。

```:引数を何も指定しない場合
Chakra GaloisField.JS
ボールの個数が「１＋素数」の場合の解を求めます。

GaloisField(.js) (オプション) [素数]

/F　最初の解を発見したら終了します。
```

素数のみを指定した場合，解をソートして出力する。重複する解は出力しない。なお出力行において最初の 4 はボールの個数（１＋与えた素数）であり，続く三つの数値は数列のパラメータ，その次の数列がボールの配置となる。最後の行の 2 は得られた解の個数である。

```:素数のみを指定した場合
Chakra GaloisField.JS 3
4       1 0 2   1 2 6 4
4       1 1 1   1 3 2 7
2
```

オプション `/F` を指定すると最初の解を発見すると終了する。

```:オプション /F を指定した場合
Chakra GaloisField.JS 3 /F
4       1 0 2   1 2 6 4
```

指定可能な最大素数は JavaScript を実行する仮想マシンの使用可能なメモリ（おそらく2GB以下）によって決まる。当方の環境で確認できた上限値は以下の通り。

- オプションを指定しないとき素数 953（玉の数としては 954 個）解の個数は 150,660 個，計算時間は9時間弱要した。なお計算時間は概ね玉の数の四乗に比例する傾向がみられる。なお素数 967（玉の数としては 968 個）はメモリ不足で失敗した。
- オプション `/F` を使用したとき素数 15,583（玉の数としては 15,584 個）計算時間は 13 秒あまり。なお素数 15,601（玉の数としては 15,602 個）はメモリ不足で失敗した。

```plantuml
@startuml
<style>
root {
  margin: 0;
  padding: 0;
}
caption {
  fontSize: 17;
}
</style>
title <img:https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3490655/b9d00fc3-9e00-4092-7ca4-22328a9efe42.png>
caption 図２　ボールの数と計算時間
@enduml
```

<details><summary><font color=blue><b>JavaScript プログラム GaloisField.js の実行結果はコチラ</b></font></summary>

<table>
<caption>表３　ボールの個数 $n = 1 + p$ と計算時間</caption>
<thead>
<tr><th style="text-align:center">素数<br>$p$</th>
    <th style="text-align:center">玉の数<br>$n$</th>
    <th style="text-align:center">解の数</th>
    <th style="text-align:center">計算時間<br>[s]</th>
    <th></th>
    <th style="text-align:center">素数<br>$p$</th>
    <th style="text-align:center">玉の数<br>$n$</th>
    <th style="text-align:center">解の数</th>
    <th style="text-align:center">計算時間<br>[s]</th></tr>
</thead>
<tbody>
<tr><td style="text-align:right">2</td>
    <td style="text-align:right">3</td>
    <td style="text-align:right">1</td>
    <td style="text-align:right">0.03</td>
    <td></td>
    <td style="text-align:right">421</td>
    <td style="text-align:right">422</td>
    <td style="text-align:right">19,740</td>
    <td style="text-align:right">534.46</td></tr>
<tr><td style="text-align:right">3</td>
    <td style="text-align:right">4</td>
    <td style="text-align:right">2</td>
    <td style="text-align:right">0.03</td>
    <td></td>
    <td style="text-align:right">431</td>
    <td style="text-align:right">432</td>
    <td style="text-align:right">26,136</td>
    <td style="text-align:right">784.45</td></tr>
<tr><td style="text-align:right">5</td>
    <td style="text-align:right">6</td>
    <td style="text-align:right">5</td>
    <td style="text-align:right">0.03</td>
    <td></td>
    <td style="text-align:right">433</td>
    <td style="text-align:right">434</td>
    <td style="text-align:right">20,304</td>
    <td style="text-align:right">661.32</td></tr>
<tr><td style="text-align:right">7</td>
    <td style="text-align:right">8</td>
    <td style="text-align:right">6</td>
    <td style="text-align:right">0.03</td>
    <td></td>
    <td style="text-align:right">439</td>
    <td style="text-align:right">440</td>
    <td style="text-align:right">20,460</td>
    <td style="text-align:right">804.50</td></tr>
<tr><td style="text-align:right">11</td>
    <td style="text-align:right">12</td>
    <td style="text-align:right">18</td>
    <td style="text-align:right">0.03</td>
    <td></td>
    <td style="text-align:right">443</td>
    <td style="text-align:right">444</td>
    <td style="text-align:right">28,098</td>
    <td style="text-align:right">1,024.48</td></tr>
<tr><td style="text-align:right">13</td>
    <td style="text-align:right">14</td>
    <td style="text-align:right">20</td>
    <td style="text-align:right">0.03</td>
    <td></td>
    <td style="text-align:right">449</td>
    <td style="text-align:right">450</td>
    <td style="text-align:right">33,312</td>
    <td style="text-align:right">1,159.52</td></tr>
<tr><td style="text-align:right">17</td>
    <td style="text-align:right">18</td>
    <td style="text-align:right">51</td>
    <td style="text-align:right">0.04</td>
    <td></td>
    <td style="text-align:right">457</td>
    <td style="text-align:right">458</td>
    <td style="text-align:right">19,932</td>
    <td style="text-align:right">882.99</td></tr>
<tr><td style="text-align:right">19</td>
    <td style="text-align:right">20</td>
    <td style="text-align:right">42</td>
    <td style="text-align:right">0.04</td>
    <td></td>
    <td style="text-align:right">461</td>
    <td style="text-align:right">462</td>
    <td style="text-align:right">35,340</td>
    <td style="text-align:right">1,262.74</td></tr>
<tr><td style="text-align:right">23</td>
    <td style="text-align:right">24</td>
    <td style="text-align:right">78</td>
    <td style="text-align:right">0.05</td>
    <td></td>
    <td style="text-align:right">463</td>
    <td style="text-align:right">464</td>
    <td style="text-align:right">22,608</td>
    <td style="text-align:right">929.43</td></tr>
<tr><td style="text-align:right">29</td>
    <td style="text-align:right">30</td>
    <td style="text-align:right">132</td>
    <td style="text-align:right">0.05</td>
    <td></td>
    <td style="text-align:right">467</td>
    <td style="text-align:right">468</td>
    <td style="text-align:right">34,506</td>
    <td style="text-align:right">1,331.14</td></tr>
<tr><td style="text-align:right">31</td>
    <td style="text-align:right">32</td>
    <td style="text-align:right">110</td>
    <td style="text-align:right">0.05</td>
    <td></td>
    <td style="text-align:right">479</td>
    <td style="text-align:right">480</td>
    <td style="text-align:right">37,422</td>
    <td style="text-align:right">1,455.57</td></tr>
<tr><td style="text-align:right">37</td>
    <td style="text-align:right">38</td>
    <td style="text-align:right">132</td>
    <td style="text-align:right">0.07</td>
    <td></td>
    <td style="text-align:right">487</td>
    <td style="text-align:right">488</td>
    <td style="text-align:right">22,632</td>
    <td style="text-align:right">1,048.69</td></tr>
<tr><td style="text-align:right">41</td>
    <td style="text-align:right">42</td>
    <td style="text-align:right">287</td>
    <td style="text-align:right">0.10</td>
    <td></td>
    <td style="text-align:right">491</td>
    <td style="text-align:right">492</td>
    <td style="text-align:right">39,168</td>
    <td style="text-align:right">1,521.43</td></tr>
<tr><td style="text-align:right">43</td>
    <td style="text-align:right">44</td>
    <td style="text-align:right">210</td>
    <td style="text-align:right">0.11</td>
    <td></td>
    <td style="text-align:right">499</td>
    <td style="text-align:right">500</td>
    <td style="text-align:right">23,544</td>
    <td style="text-align:right">1,101.62</td></tr>
<tr><td style="text-align:right">47</td>
    <td style="text-align:right">48</td>
    <td style="text-align:right">360</td>
    <td style="text-align:right">0.17</td>
    <td></td>
    <td style="text-align:right">503</td>
    <td style="text-align:right">504</td>
    <td style="text-align:right">39,000</td>
    <td style="text-align:right">1,788.04</td></tr>
<tr><td style="text-align:right">53</td>
    <td style="text-align:right">54</td>
    <td style="text-align:right">408</td>
    <td style="text-align:right">0.22</td>
    <td></td>
    <td style="text-align:right">509</td>
    <td style="text-align:right">510</td>
    <td style="text-align:right">42,252</td>
    <td style="text-align:right">1,940.20</td></tr>
<tr><td style="text-align:right">59</td>
    <td style="text-align:right">60</td>
    <td style="text-align:right">590</td>
    <td style="text-align:right">0.44</td>
    <td></td>
    <td style="text-align:right">521</td>
    <td style="text-align:right">522</td>
    <td style="text-align:right">43,710</td>
    <td style="text-align:right">2,064.59</td></tr>
<tr><td style="text-align:right">61</td>
    <td style="text-align:right">62</td>
    <td style="text-align:right">384</td>
    <td style="text-align:right">0.33</td>
    <td></td>
    <td style="text-align:right">523</td>
    <td style="text-align:right">524</td>
    <td style="text-align:right">28,104</td>
    <td style="text-align:right">1,578.65</td></tr>
<tr><td style="text-align:right">67</td>
    <td style="text-align:right">68</td>
    <td style="text-align:right">420</td>
    <td style="text-align:right">0.40</td>
    <td></td>
    <td style="text-align:right">541</td>
    <td style="text-align:right">542</td>
    <td style="text-align:right">27,924</td>
    <td style="text-align:right">1,607.36</td></tr>
<tr><td style="text-align:right">71</td>
    <td style="text-align:right">72</td>
    <td style="text-align:right">852</td>
    <td style="text-align:right">0.72</td>
    <td></td>
    <td style="text-align:right">547</td>
    <td style="text-align:right">548</td>
    <td style="text-align:right">33,048</td>
    <td style="text-align:right">2,097.60</td></tr>
<tr><td style="text-align:right">73</td>
    <td style="text-align:right">74</td>
    <td style="text-align:right">600</td>
    <td style="text-align:right">0.67</td>
    <td></td>
    <td style="text-align:right">557</td>
    <td style="text-align:right">558</td>
    <td style="text-align:right">44,394</td>
    <td style="text-align:right">2,651.46</td></tr>
<tr><td style="text-align:right">79</td>
    <td style="text-align:right">80</td>
    <td style="text-align:right">588</td>
    <td style="text-align:right">0.74</td>
    <td></td>
    <td style="text-align:right">563</td>
    <td style="text-align:right">564</td>
    <td style="text-align:right">51,210</td>
    <td style="text-align:right">3,576.32</td></tr>
<tr><td style="text-align:right">83</td>
    <td style="text-align:right">84</td>
    <td style="text-align:right">1,098</td>
    <td style="text-align:right">1.09</td>
    <td></td>
    <td style="text-align:right">569</td>
    <td style="text-align:right">570</td>
    <td style="text-align:right">46,326</td>
    <td style="text-align:right">3,223.27</td></tr>
<tr><td style="text-align:right">89</td>
    <td style="text-align:right">90</td>
    <td style="text-align:right">1,335</td>
    <td style="text-align:right">1.38</td>
    <td></td>
    <td style="text-align:right">571</td>
    <td style="text-align:right">572</td>
    <td style="text-align:right">30,600</td>
    <td style="text-align:right">2,269.32</td></tr>
<tr><td style="text-align:right">97</td>
    <td style="text-align:right">98</td>
    <td style="text-align:right">1,056</td>
    <td style="text-align:right">1.57</td>
    <td></td>
    <td style="text-align:right">577</td>
    <td style="text-align:right">578</td>
    <td style="text-align:right">35,100</td>
    <td style="text-align:right">2,829.29</td></tr>
<tr><td style="text-align:right">101</td>
    <td style="text-align:right">102</td>
    <td style="text-align:right">1,717</td>
    <td style="text-align:right">2.27</td>
    <td></td>
    <td style="text-align:right">587</td>
    <td style="text-align:right">588</td>
    <td style="text-align:right">57,330</td>
    <td style="text-align:right">4,443.94</td></tr>
<tr><td style="text-align:right">103</td>
    <td style="text-align:right">104</td>
    <td style="text-align:right">1,190</td>
    <td style="text-align:right">1.94</td>
    <td></td>
    <td style="text-align:right">593</td>
    <td style="text-align:right">594</td>
    <td style="text-align:right">58,320</td>
    <td style="text-align:right">4,533.79</td></tr>
<tr><td style="text-align:right">107</td>
    <td style="text-align:right">108</td>
    <td style="text-align:right">1,512</td>
    <td style="text-align:right">2.26</td>
    <td></td>
    <td style="text-align:right">599</td>
    <td style="text-align:right">600</td>
    <td style="text-align:right">51,342</td>
    <td style="text-align:right">4,013.26</td></tr>
<tr><td style="text-align:right">109</td>
    <td style="text-align:right">110</td>
    <td style="text-align:right">1,140</td>
    <td style="text-align:right">1.99</td>
    <td></td>
    <td style="text-align:right">601</td>
    <td style="text-align:right">602</td>
    <td style="text-align:right">37,104</td>
    <td style="text-align:right">3,045.40</td></tr>
<tr><td style="text-align:right">113</td>
    <td style="text-align:right">114</td>
    <td style="text-align:right">1,980</td>
    <td style="text-align:right">2.91</td>
    <td></td>
    <td style="text-align:right">607</td>
    <td style="text-align:right">608</td>
    <td style="text-align:right">37,848</td>
    <td style="text-align:right">3,302.01</td></tr>
<tr><td style="text-align:right">127</td>
    <td style="text-align:right">128</td>
    <td style="text-align:right">1,806</td>
    <td style="text-align:right">4.96</td>
    <td></td>
    <td style="text-align:right">613</td>
    <td style="text-align:right">614</td>
    <td style="text-align:right">35,844</td>
    <td style="text-align:right">3,239.19</td></tr>
<tr><td style="text-align:right">131</td>
    <td style="text-align:right">132</td>
    <td style="text-align:right">2,882</td>
    <td style="text-align:right">7.08</td>
    <td></td>
    <td style="text-align:right">617</td>
    <td style="text-align:right">618</td>
    <td style="text-align:right">62,880</td>
    <td style="text-align:right">5,014.99</td></tr>
<tr><td style="text-align:right">137</td>
    <td style="text-align:right">138</td>
    <td style="text-align:right">2,592</td>
    <td style="text-align:right">7.22</td>
    <td></td>
    <td style="text-align:right">619</td>
    <td style="text-align:right">620</td>
    <td style="text-align:right">40,392</td>
    <td style="text-align:right">3,729.30</td></tr>
<tr><td style="text-align:right">139</td>
    <td style="text-align:right">140</td>
    <td style="text-align:right">1,992</td>
    <td style="text-align:right">5.89</td>
    <td></td>
    <td style="text-align:right">631</td>
    <td style="text-align:right">632</td>
    <td style="text-align:right">44,064</td>
    <td style="text-align:right">3,736.13</td></tr>
<tr><td style="text-align:right">149</td>
    <td style="text-align:right">150</td>
    <td style="text-align:right">3,060</td>
    <td style="text-align:right">8.91</td>
    <td></td>
    <td style="text-align:right">641</td>
    <td style="text-align:right">642</td>
    <td style="text-align:right">58,788</td>
    <td style="text-align:right">5,045.67</td></tr>
<tr><td style="text-align:right">151</td>
    <td style="text-align:right">152</td>
    <td style="text-align:right">2,184</td>
    <td style="text-align:right">7.20</td>
    <td></td>
    <td style="text-align:right">643</td>
    <td style="text-align:right">644</td>
    <td style="text-align:right">45,504</td>
    <td style="text-align:right">4,114.96</td></tr>
<tr><td style="text-align:right">157</td>
    <td style="text-align:right">158</td>
    <td style="text-align:right">2,756</td>
    <td style="text-align:right">12.95</td>
    <td></td>
    <td style="text-align:right">647</td>
    <td style="text-align:right">648</td>
    <td style="text-align:right">69,510</td>
    <td style="text-align:right">5,868.24</td></tr>
<tr><td style="text-align:right">163</td>
    <td style="text-align:right">164</td>
    <td style="text-align:right">2,376</td>
    <td style="text-align:right">11.96</td>
    <td></td>
    <td style="text-align:right">653</td>
    <td style="text-align:right">654</td>
    <td style="text-align:right">53,352</td>
    <td style="text-align:right">5,049.29</td></tr>
<tr><td style="text-align:right">167</td>
    <td style="text-align:right">168</td>
    <td style="text-align:right">4,676</td>
    <td style="text-align:right">20.78</td>
    <td></td>
    <td style="text-align:right">659</td>
    <td style="text-align:right">660</td>
    <td style="text-align:right">66,912</td>
    <td style="text-align:right">5,612.40</td></tr>
<tr><td style="text-align:right">173</td>
    <td style="text-align:right">174</td>
    <td style="text-align:right">5,017</td>
    <td style="text-align:right">23.21</td>
    <td></td>
    <td style="text-align:right">661</td>
    <td style="text-align:right">662</td>
    <td style="text-align:right">48,620</td>
    <td style="text-align:right">4,319.87</td></tr>
<tr><td style="text-align:right">179</td>
    <td style="text-align:right">180</td>
    <td style="text-align:right">4,602</td>
    <td style="text-align:right">22.04</td>
    <td></td>
    <td style="text-align:right">673</td>
    <td style="text-align:right">674</td>
    <td style="text-align:right">50,400</td>
    <td style="text-align:right">5,019.19</td></tr>
<tr><td style="text-align:right">181</td>
    <td style="text-align:right">182</td>
    <td style="text-align:right">3,588</td>
    <td style="text-align:right">17.98</td>
    <td></td>
    <td style="text-align:right">677</td>
    <td style="text-align:right">678</td>
    <td style="text-align:right">76,501</td>
    <td style="text-align:right">6,956.82</td></tr>
<tr><td style="text-align:right">191</td>
    <td style="text-align:right">192</td>
    <td style="text-align:right">4,680</td>
    <td style="text-align:right">24.76</td>
    <td></td>
    <td style="text-align:right">683</td>
    <td style="text-align:right">684</td>
    <td style="text-align:right">66,738</td>
    <td style="text-align:right">5,963.07</td></tr>
<tr><td style="text-align:right">193</td>
    <td style="text-align:right">194</td>
    <td style="text-align:right">3,564</td>
    <td style="text-align:right">22.82</td>
    <td></td>
    <td style="text-align:right">691</td>
    <td style="text-align:right">692</td>
    <td style="text-align:right">50,328</td>
    <td style="text-align:right">4,963.96</td></tr>
<tr><td style="text-align:right">197</td>
    <td style="text-align:right">198</td>
    <td style="text-align:right">6,156</td>
    <td style="text-align:right">35.11</td>
    <td></td>
    <td style="text-align:right">701</td>
    <td style="text-align:right">702</td>
    <td style="text-align:right">82,017</td>
    <td style="text-align:right">7,052.33</td></tr>
<tr><td style="text-align:right">199</td>
    <td style="text-align:right">200</td>
    <td style="text-align:right">4,422</td>
    <td style="text-align:right">28.28</td>
    <td></td>
    <td style="text-align:right">709</td>
    <td style="text-align:right">710</td>
    <td style="text-align:right">47,940</td>
    <td style="text-align:right">5,738.11</td></tr>
<tr><td style="text-align:right">211</td>
    <td style="text-align:right">212</td>
    <td style="text-align:right">4,320</td>
    <td style="text-align:right">35.47</td>
    <td></td>
    <td style="text-align:right">719</td>
    <td style="text-align:right">720</td>
    <td style="text-align:right">86,022</td>
    <td style="text-align:right">9,799.94</td></tr>
<tr><td style="text-align:right">223</td>
    <td style="text-align:right">224</td>
    <td style="text-align:right">5,550</td>
    <td style="text-align:right">46.80</td>
    <td></td>
    <td style="text-align:right">727</td>
    <td style="text-align:right">728</td>
    <td style="text-align:right">58,806</td>
    <td style="text-align:right">7,654.91</td></tr>
<tr><td style="text-align:right">227</td>
    <td style="text-align:right">228</td>
    <td style="text-align:right">8,496</td>
    <td style="text-align:right">67.90</td>
    <td></td>
    <td style="text-align:right">733</td>
    <td style="text-align:right">734</td>
    <td style="text-align:right">56,628</td>
    <td style="text-align:right">8,465.28</td></tr>
<tr><td style="text-align:right">229</td>
    <td style="text-align:right">230</td>
    <td style="text-align:right">5,760</td>
    <td style="text-align:right">50.36</td>
    <td></td>
    <td style="text-align:right">739</td>
    <td style="text-align:right">740</td>
    <td style="text-align:right">52,080</td>
    <td style="text-align:right">7,393.03</td></tr>
<tr><td style="text-align:right">233</td>
    <td style="text-align:right">234</td>
    <td style="text-align:right">7,788</td>
    <td style="text-align:right">63.83</td>
    <td></td>
    <td style="text-align:right">743</td>
    <td style="text-align:right">744</td>
    <td style="text-align:right">92,132</td>
    <td style="text-align:right">12,316.02</td></tr>
<tr><td style="text-align:right">239</td>
    <td style="text-align:right">240</td>
    <td style="text-align:right">9,054</td>
    <td style="text-align:right">69.72</td>
    <td></td>
    <td style="text-align:right">751</td>
    <td style="text-align:right">752</td>
    <td style="text-align:right">53,784</td>
    <td style="text-align:right">7,953.77</td></tr>
<tr><td style="text-align:right">241</td>
    <td style="text-align:right">242</td>
    <td style="text-align:right">6,480</td>
    <td style="text-align:right">60.63</td>
    <td></td>
    <td style="text-align:right">757</td>
    <td style="text-align:right">758</td>
    <td style="text-align:right">58,848</td>
    <td style="text-align:right">8,950.20</td></tr>
<tr><td style="text-align:right">251</td>
    <td style="text-align:right">252</td>
    <td style="text-align:right">10,290</td>
    <td style="text-align:right">83.36</td>
    <td></td>
    <td style="text-align:right">761</td>
    <td style="text-align:right">762</td>
    <td style="text-align:right">96,647</td>
    <td style="text-align:right">13,131.07</td></tr>
<tr><td style="text-align:right">257</td>
    <td style="text-align:right">258</td>
    <td style="text-align:right">10,860</td>
    <td style="text-align:right">113.39</td>
    <td></td>
    <td style="text-align:right">769</td>
    <td style="text-align:right">770</td>
    <td style="text-align:right">63,660</td>
    <td style="text-align:right">9,096.61</td></tr>
<tr><td style="text-align:right">263</td>
    <td style="text-align:right">264</td>
    <td style="text-align:right">9,072</td>
    <td style="text-align:right">116.41</td>
    <td></td>
    <td style="text-align:right">773</td>
    <td style="text-align:right">774</td>
    <td style="text-align:right">99,717</td>
    <td style="text-align:right">14,676.52</td></tr>
<tr><td style="text-align:right">269</td>
    <td style="text-align:right">270</td>
    <td style="text-align:right">10,800</td>
    <td style="text-align:right">133.79</td>
    <td></td>
    <td style="text-align:right">787</td>
    <td style="text-align:right">788</td>
    <td style="text-align:right">66,600</td>
    <td style="text-align:right">11,306.95</td></tr>
<tr><td style="text-align:right">271</td>
    <td style="text-align:right">272</td>
    <td style="text-align:right">8,190</td>
    <td style="text-align:right">110.64</td>
    <td></td>
    <td style="text-align:right">797</td>
    <td style="text-align:right">798</td>
    <td style="text-align:right">105,300</td>
    <td style="text-align:right">16,009.53</td></tr>
<tr><td style="text-align:right">277</td>
    <td style="text-align:right">278</td>
    <td style="text-align:right">6,912</td>
    <td style="text-align:right">106.75</td>
    <td></td>
    <td style="text-align:right">809</td>
    <td style="text-align:right">810</td>
    <td style="text-align:right">81,648</td>
    <td style="text-align:right">12,155.67</td></tr>
<tr><td style="text-align:right">281</td>
    <td style="text-align:right">282</td>
    <td style="text-align:right">13,068</td>
    <td style="text-align:right">158.82</td>
    <td></td>
    <td style="text-align:right">811</td>
    <td style="text-align:right">812</td>
    <td style="text-align:right">69,120</td>
    <td style="text-align:right">10,777.85</td></tr>
<tr><td style="text-align:right">283</td>
    <td style="text-align:right">284</td>
    <td style="text-align:right">8,784</td>
    <td style="text-align:right">130.99</td>
    <td></td>
    <td style="text-align:right">821</td>
    <td style="text-align:right">822</td>
    <td style="text-align:right">95,760</td>
    <td style="text-align:right">14,687.07</td></tr>
<tr><td style="text-align:right">293</td>
    <td style="text-align:right">294</td>
    <td style="text-align:right">14,357</td>
    <td style="text-align:right">187.02</td>
    <td></td>
    <td style="text-align:right">823</td>
    <td style="text-align:right">824</td>
    <td style="text-align:right">63,000</td>
    <td style="text-align:right">11,267.67</td></tr>
<tr><td style="text-align:right">307</td>
    <td style="text-align:right">308</td>
    <td style="text-align:right">10,248</td>
    <td style="text-align:right">153.80</td>
    <td></td>
    <td style="text-align:right">827</td>
    <td style="text-align:right">828</td>
    <td style="text-align:right">114,126</td>
    <td style="text-align:right">17,042.65</td></tr>
<tr><td style="text-align:right">311</td>
    <td style="text-align:right">312</td>
    <td style="text-align:right">15,318</td>
    <td style="text-align:right">207.49</td>
    <td></td>
    <td style="text-align:right">829</td>
    <td style="text-align:right">830</td>
    <td style="text-align:right">76,020</td>
    <td style="text-align:right">12,299.66</td></tr>
<tr><td style="text-align:right">313</td>
    <td style="text-align:right">314</td>
    <td style="text-align:right">10,860</td>
    <td style="text-align:right">177.18</td>
    <td></td>
    <td style="text-align:right">839</td>
    <td style="text-align:right">840</td>
    <td style="text-align:right">117,460</td>
    <td style="text-align:right">17,219.54</td></tr>
<tr><td style="text-align:right">317</td>
    <td style="text-align:right">318</td>
    <td style="text-align:right">14,400</td>
    <td style="text-align:right">212.21</td>
    <td></td>
    <td style="text-align:right">853</td>
    <td style="text-align:right">854</td>
    <td style="text-align:right">79,044</td>
    <td style="text-align:right">13,708.36</td></tr>
<tr><td style="text-align:right">331</td>
    <td style="text-align:right">332</td>
    <td style="text-align:right">10,464</td>
    <td style="text-align:right">204.47</td>
    <td></td>
    <td style="text-align:right">857</td>
    <td style="text-align:right">858</td>
    <td style="text-align:right">122,551</td>
    <td style="text-align:right">18,859.21</td></tr>
<tr><td style="text-align:right">337</td>
    <td style="text-align:right">338</td>
    <td style="text-align:right">12,348</td>
    <td style="text-align:right">233.08</td>
    <td></td>
    <td style="text-align:right">859</td>
    <td style="text-align:right">860</td>
    <td style="text-align:right">122,688</td>
    <td style="text-align:right">18,593.30</td></tr>
<tr><td style="text-align:right">347</td>
    <td style="text-align:right">348</td>
    <td style="text-align:right">15,912</td>
    <td style="text-align:right">361.69</td>
    <td></td>
    <td style="text-align:right">863</td>
    <td style="text-align:right">864</td>
    <td style="text-align:right">106,512</td>
    <td style="text-align:right">17,825.33</td></tr>
<tr><td style="text-align:right">349</td>
    <td style="text-align:right">350</td>
    <td style="text-align:right">12,852</td>
    <td style="text-align:right">291.46</td>
    <td></td>
    <td style="text-align:right">877</td>
    <td style="text-align:right">878</td>
    <td style="text-align:right">71,280</td>
    <td style="text-align:right">14,084.91</td></tr>
<tr><td style="text-align:right">353</td>
    <td style="text-align:right">354</td>
    <td style="text-align:right">19,728</td>
    <td style="text-align:right">423.26</td>
    <td></td>
    <td style="text-align:right">881</td>
    <td style="text-align:right">882</td>
    <td style="text-align:right">122,688</td>
    <td style="text-align:right">18,593.30</td></tr>
<tr><td style="text-align:right">359</td>
    <td style="text-align:right">360</td>
    <td style="text-align:right">17,928</td>
    <td style="text-align:right">394.40</td>
    <td></td>
    <td style="text-align:right">883</td>
    <td style="text-align:right">884</td>
    <td style="text-align:right">86,730</td>
    <td style="text-align:right">14,803.78</td></tr>
<tr><td style="text-align:right">367</td>
    <td style="text-align:right">368</td>
    <td style="text-align:right">13,848</td>
    <td style="text-align:right">362.52</td>
    <td></td>
    <td style="text-align:right">887</td>
    <td style="text-align:right">888</td>
    <td style="text-align:right">121,176</td>
    <td style="text-align:right">20,232.70</td></tr>
<tr><td style="text-align:right">373</td>
    <td style="text-align:right">374</td>
    <td style="text-align:right">12,096</td>
    <td style="text-align:right">321.66</td>
    <td></td>
    <td style="text-align:right">907</td>
    <td style="text-align:right">908</td>
    <td style="text-align:right">78,432</td>
    <td style="text-align:right">16,557.91</td></tr>
<tr><td style="text-align:right">379</td>
    <td style="text-align:right">380</td>
    <td style="text-align:right">15,720</td>
    <td style="text-align:right">375.68</td>
    <td></td>
    <td style="text-align:right">911</td>
    <td style="text-align:right">912</td>
    <td style="text-align:right">138,472</td>
    <td style="text-align:right">25,789.41</td></tr>
<tr><td style="text-align:right">383</td>
    <td style="text-align:right">384</td>
    <td style="text-align:right">24,512</td>
    <td style="text-align:right">566.84</td>
    <td></td>
    <td style="text-align:right">919</td>
    <td style="text-align:right">920</td>
    <td style="text-align:right">69,984</td>
    <td style="text-align:right">14,896.11</td></tr>
<tr><td style="text-align:right">389</td>
    <td style="text-align:right">390</td>
    <td style="text-align:right">21,672</td>
    <td style="text-align:right">486.71</td>
    <td></td>
    <td style="text-align:right">929</td>
    <td style="text-align:right">930</td>
    <td style="text-align:right">143,052</td>
    <td style="text-align:right">28,408.93</td></tr>
<tr><td style="text-align:right">397</td>
    <td style="text-align:right">398</td>
    <td style="text-align:right">16,980</td>
    <td style="text-align:right">452.97</td>
    <td></td>
    <td style="text-align:right">937</td>
    <td style="text-align:right">938</td>
    <td style="text-align:right">97,656</td>
    <td style="text-align:right">18,722.13</td></tr>
<tr><td style="text-align:right">401</td>
    <td style="text-align:right">402</td>
    <td style="text-align:right">23,028</td>
    <td style="text-align:right">542.96</td>
    <td></td>
    <td style="text-align:right">941</td>
    <td style="text-align:right">942</td>
    <td style="text-align:right">147,420</td>
    <td style="text-align:right">35,457.38</td></tr>
<tr><td style="text-align:right">409</td>
    <td style="text-align:right">410</td>
    <td style="text-align:right">18,632</td>
    <td style="text-align:right">541.98</td>
    <td></td>
    <td style="text-align:right">947</td>
    <td style="text-align:right">948</td>
    <td style="text-align:right">127,512</td>
    <td style="text-align:right">32,863.05</td></tr>
<tr><td style="text-align:right">419</td>
    <td style="text-align:right">420</td>
    <td style="text-align:right">27,072</td>
    <td style="text-align:right">735.34</td>
    <td></td>
    <td style="text-align:right">953</td>
    <td style="text-align:right">954</td>
    <td style="text-align:right">150,660</td>
    <td style="text-align:right">32,272.79</td></tr>
</tbody>
</table>

</details>

## 3.3 玉の数が「１＋素数のべき乗」の場合

[シリーズ第５回の記事](https://qiita.com/TETSURO1999/items/78345dadd089c6c90c76?1)で紹介したプログラムを少し改良した。これまで指数ごとに別々のプログラムに分かれていたが，一つのプログラムに統合した。また GaloisField.js と同じ改良を加えた。実装コードを以下に示す。

<details><summary><font color=blue><b>JavaScript プログラム GaloisField2.js のソースはコチラ</b></font></summary>

```javascript:GaloisField2.js
var	args = WScript.Arguments.Unnamed;
var	opts = WScript.Arguments.Named;
var	ret = main(args, opts)
try {
	WScript.Quit(ret);
} catch(e) {
	/* 何もしない */
}
//------------------------------------------------------------------------------
// メイン関数
//------------------------------------------------------------------------------
function main(args, opts) {
	//--------------------------------------------------------------------------
	// ヘルプメッセージ
	//--------------------------------------------------------------------------
	if(args.Count == 0) {
		WScript.Echo("ボールの個数が「１＋素数のべき乗」の場合の解を求めます。");
		WScript.Echo("");
		WScript.Echo("GaloisField2(.js) (オプション) [素数] (指数)");
		WScript.Echo("");
		WScript.Echo("/F　最初の解を発見したら終了します。");
		return false;
	}
	//--------------------------------------------------------------------------
	// コマンドライン解析
	//--------------------------------------------------------------------------
	var	p = parseInt(args(0));
	var	m = args.Count > 1 ? parseInt(args(1)) : 1;
	var	f_flag = opts.Exists("F") ? true : false;
	//--------------------------------------------------------------------------
	// 線形再帰剰余数列のパラメータ a[] を全探索する
	//--------------------------------------------------------------------------
	var	num   = 1 + Math.pow(p, m);				// ボールの個数
	var	sum   = 1 + num * (num - 1);			// ボールの数値の総和
	var	limit = euler_totient(sum) / (6 * m);	// 解の個数（予想値）
	var	count = 0;								// 解の個数
	var	d = [];									// 解（ホール配置）
	var	a = [];									// 数列の係数
	//--------------------------------------------------------------------------
	// 完全差集合をボール配置に変換して出力する
	//--------------------------------------------------------------------------
	var	output = function(t) {
		//----------------------------------------------------------------------
		// 完全差集合をボール配置に変換する
		//----------------------------------------------------------------------
		var	u = [];
		for(var i = 1; i < t.length; i++)
			u.push(t[i] - t[i - 1]);
		//----------------------------------------------------------------------
		// 1 のボールを探す
		//----------------------------------------------------------------------
		for(var i = 0; i < u.length; i++)
			if(u[i] == 1) break;
		if(i >= num) return;
		var	v = [];
		for(j = 0; j < num + 1; j++)
			v.push(u[i + j]);
		//----------------------------------------------------------------------
		// ボール配置を正規化（左右反転）
		//----------------------------------------------------------------------
		if(v[1] > v[num - 1]) v.reverse();
		v.pop();
		//----------------------------------------------------------------------
		// ボール配置のチェック＆重複するボール配置を取り除く
		//----------------------------------------------------------------------
		if(!check_ball(v)) return;
		//----------------------------------------------------------------------
		// ボール配置の出力
		//----------------------------------------------------------------------
		var	k = a.join(" ");
		var	w = v.join(" ");
		if(f_flag) {
			WScript.Echo([num, k, w].join("\t"));
			throw new Error();
		} else if(d[w] == null) {
			d[w] = k;
			if(++count >= limit) throw new Error();
		}
	};
	//--------------------------------------------------------------------------
	// 解の探索を行う：1＋素数の場合
	//--------------------------------------------------------------------------
	var	search1 = function() {
		var	s = [0, 0, 1];
		for(var i = 3 * 1; i < sum * 2; i++) {
			var	v = a[0] * s[i - 1] + a[1] * s[i - 2] + a[2] * s[i - 3];
			s.push(v % p);
		}
		var	t = [];
		for(var i = sum * 0; i < sum * 2; i++)
			if(s[i] == 0)
				t.push(i);
		if(t.length == 2 * num) output(t);
	};
	//--------------------------------------------------------------------------
	// 解の探索を行う：1＋素数の二乗の場合
	//--------------------------------------------------------------------------
	var	search2 = function() {
		var	s = [0, 0, 0, 0, 0, 1];
		for(var i = 3 * 2; i < sum * 3; i++) {
			var	v = a[0] * s[i - 1] + a[1] * s[i - 2] + a[2] * s[i - 3]
				  + a[3] * s[i - 4] + a[4] * s[i - 5] + a[5] * s[i - 6];
			s.push(v % p);
		}
		var	t = [];
		for(var i = sum * 1; i < sum * 3; i++)
			if(s[i] == 0 && s[i - sum * 1] == 0)
				t.push(i);
		if(t.length == 2 * num) output(t);
	};
	//--------------------------------------------------------------------------
	// 解の探索を行う：1＋素数の三乗の場合
	//--------------------------------------------------------------------------
	var	search3 = function() {
		var	s = [0, 0, 0, 0, 0, 0, 0, 0, 1];
		for(var i = 3 * 3; i < sum * 4; i++) {
			var	v = a[0] * s[i - 1] + a[1] * s[i - 2] + a[2] * s[i - 3]
				  + a[3] * s[i - 4] + a[4] * s[i - 5] + a[5] * s[i - 6]
				  + a[6] * s[i - 7] + a[7] * s[i - 8] + a[8] * s[i - 9];
			s.push(v % p);
		}
		var	t = [];
		for(var i = sum * 2; i < sum * 4; i++)
			if(s[i] == 0 && s[i - sum * 1] == 0 && s[i - sum * 2] == 0)
				t.push(i);
		if(t.length == 2 * num) output(t);
	};
	//--------------------------------------------------------------------------
	// 解の探索を行う：1＋素数の四乗の場合
	//--------------------------------------------------------------------------
	var	search4 = function() {
		var	s = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
		for(var i = 3 * 4; i < sum * 5; i++) {
			var	v = a[0] * s[i - 1]  + a[1]  * s[i - 2]  + a[2]  * s[i - 3]
				  + a[3] * s[i - 4]  + a[4]  * s[i - 5]  + a[5]  * s[i - 6]
				  + a[6] * s[i - 7]  + a[7]  * s[i - 8]  + a[8]  * s[i - 9]
				  + a[9] * s[i - 10] + a[10] * s[i - 11] + a[11] * s[i - 12];
			s.push(v % p);
		}
		var	t = [];
		for(var i = sum * 3; i < sum * 5; i++)
			if(s[i] == 0 && s[i - sum * 1] == 0 && s[i - sum * 2] == 0 && s[i - sum * 3] == 0)
				t.push(i);
		if(t.length == 2 * num) output(t);
	};
	//--------------------------------------------------------------------------
	// 解の探索を行う：1＋素数の五乗の場合
	//--------------------------------------------------------------------------
	var	search5 = function() {
		var	s = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
		for(var i = 3 * 5; i < sum * 6; i++) {
			var	v = a[0]  * s[i - 1]  + a[1]  * s[i - 2]  + a[2]  * s[i - 3]
				  + a[3]  * s[i - 4]  + a[4]  * s[i - 5]  + a[5]  * s[i - 6]
				  + a[6]  * s[i - 7]  + a[7]  * s[i - 8]  + a[8]  * s[i - 9]
				  + a[9]  * s[i - 10] + a[10] * s[i - 11] + a[11] * s[i - 12]
				  + a[12] * s[i - 13] + a[13] * s[i - 14] + a[14] * s[i - 15];
			s.push(v % p);
		}
		var	t = [];
		for(var i = sum * 4; i < sum * 6; i++)
			if(s[i] == 0 && s[i - sum * 1] == 0 && s[i - sum * 2] == 0 && s[i - sum * 3] == 0
						 && s[i - sum * 4] == 0)
				t.push(i);
		if(t.length == 2 * num) output(t);
	};
	//--------------------------------------------------------------------------
	// 解の探索を行う：1＋素数の六乗の場合
	//--------------------------------------------------------------------------
	var	search6 = function() {
		var	s = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
		for(var i = 3 * 6; i < sum * 7; i++) {
			var	v = a[0]  * s[i - 1]  + a[1]  * s[i - 2]  + a[2]  * s[i - 3]
				  + a[3]  * s[i - 4]  + a[4]  * s[i - 5]  + a[5]  * s[i - 6]
				  + a[6]  * s[i - 7]  + a[7]  * s[i - 8]  + a[8]  * s[i - 9]
				  + a[9]  * s[i - 10] + a[10] * s[i - 11] + a[11] * s[i - 12]
				  + a[12] * s[i - 13] + a[13] * s[i - 14] + a[14] * s[i - 15]
				  + a[15] * s[i - 16] + a[16] * s[i - 17] + a[17] * s[i - 18];
			s.push(v % p);
		}
		var	t = [];
		for(var i = sum * 5; i < sum * 7; i++)
			if(s[i] == 0 && s[i - sum * 1] == 0 && s[i - sum * 2] == 0 && s[i - sum * 3] == 0
						 && s[i - sum * 4] == 0 && s[i - sum * 5] == 0)
				t.push(i);
		if(t.length == 2 * num) output(t);
	};
	//--------------------------------------------------------------------------
	// 解の探索を行う：1＋素数の七乗の場合
	//--------------------------------------------------------------------------
	var	search7 = function() {
		var	s = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
		for(var i = 3 * 7; i < sum * 8; i++) {
			var	v = a[0]  * s[i - 1]  + a[1]  * s[i - 2]  + a[2]  * s[i - 3]
				  + a[3]  * s[i - 4]  + a[4]  * s[i - 5]  + a[5]  * s[i - 6]
				  + a[6]  * s[i - 7]  + a[7]  * s[i - 8]  + a[8]  * s[i - 9]
				  + a[9]  * s[i - 10] + a[10] * s[i - 11] + a[11] * s[i - 12]
				  + a[12] * s[i - 13] + a[13] * s[i - 14] + a[14] * s[i - 15]
				  + a[15] * s[i - 16] + a[16] * s[i - 17] + a[17] * s[i - 18]
				  + a[18] * s[i - 19] + a[19] * s[i - 20] + a[20] * s[i - 21];
			s.push(v % p);
		}
		var	t = [];
		for(var i = sum * 6; i < sum * 8; i++)
			if(s[i] == 0 && s[i - sum * 1] == 0 && s[i - sum * 2] == 0 && s[i - sum * 3] == 0
						 && s[i - sum * 4] == 0 && s[i - sum * 5] == 0 && s[i - sum * 6] == 0)
				t.push(i);
		if(t.length == 2 * num) output(t);
	};
	//--------------------------------------------------------------------------
	// 解の探索を行う：1＋素数の八乗の場合
	//--------------------------------------------------------------------------
	var	search8 = function() {
		var	s = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
		for(var i = 3 * 8; i < sum * 9; i++) {
			var	v = a[0]  * s[i - 1]  + a[1]  * s[i - 2]  + a[2]  * s[i - 3]
				  + a[3]  * s[i - 4]  + a[4]  * s[i - 5]  + a[5]  * s[i - 6]
				  + a[6]  * s[i - 7]  + a[7]  * s[i - 8]  + a[8]  * s[i - 9]
				  + a[9]  * s[i - 10] + a[10] * s[i - 11] + a[11] * s[i - 12]
				  + a[12] * s[i - 13] + a[13] * s[i - 14] + a[14] * s[i - 15]
				  + a[15] * s[i - 16] + a[16] * s[i - 17] + a[17] * s[i - 18]
				  + a[18] * s[i - 19] + a[19] * s[i - 20] + a[20] * s[i - 21]
				  + a[21] * s[i - 22] + a[22] * s[i - 23] + a[23] * s[i - 24];
			s.push(v % p);
		}
		var	t = [];
		for(var i = sum * 7; i < sum * 9; i++)
			if(s[i] == 0 && s[i - sum * 1] == 0 && s[i - sum * 2] == 0 && s[i - sum * 3] == 0
						 && s[i - sum * 4] == 0 && s[i - sum * 5] == 0 && s[i - sum * 6] == 0
						 && s[i - sum * 7] == 0)
				t.push(i);
		if(t.length == 2 * num) output(t);
	};
	var	table = [null, search1, search2, search3, search4, search5, search6, search7, search8];
	//--------------------------------------------------------------------------
	// 再帰的ループ関数
	//--------------------------------------------------------------------------
	function loop(n) {
		for(a[n] = 0; a[n] < p; a[n]++)
			for(a[n + 1] = 0; a[n + 1] < p; a[n + 1]++)
				for(a[n + 2] = 0; a[n + 2] < p; a[n + 2]++)
					if(n + 3 < 3 * m)
						loop(n + 3);
					else
						table[m]();
	}
	//--------------------------------------------------------------------------
	// 例外で深いループを脱出する
	//--------------------------------------------------------------------------
	try {
		loop(0);
	} catch(e) {
		if(f_flag) return true;
	}
	//--------------------------------------------------------------------------
	// ボール配置をソートして出力する
	//--------------------------------------------------------------------------
	var	e = [];
	for(var v in d)
		e.push({ key:d[v], val:v, arr:v.split(" ") });
	e.sort(function(a, b) {
		for(var i = 0; i < a.arr.length; i++)
			if(a.arr[i] != b.arr[i]) return a.arr[i] - b.arr[i];
		return 0;
	});
	for(var i = 0; i < e.length; i++)
		WScript.Echo([num, e[i].key, e[i].val].join("\t"));
	//--------------------------------------------------------------------------
	// 解の個数を出力する
	//--------------------------------------------------------------------------
	WScript.Echo(e.length);
	return true;
}
//------------------------------------------------------------------------------
// ボール配置のチェック
//------------------------------------------------------------------------------
function check_ball(ball) {
	var	num = ball.length;
	var	sum = num * (num -  1) + 1;
	for(var i = 0, s = 0; i < ball.length; i++)
		s += ball[i];
	if(sum != s) return false;
	var	flag = [];
	for(var i = 0; i <= sum; i++)
		flag.push(false);
	for(var i = 0; i < ball.length - 1; i++) {
		for(var j = 0, s = 0; i + j >= 0; j--) {
			s += ball[i + j];
			if(flag[s] || flag[sum - s]) return false;
			flag[s] = true;
			flag[sum - s] = true;
		}
	}
	return true;
}
//------------------------------------------------------------------------------
// オイラーのφ関数
//------------------------------------------------------------------------------
function euler_totient(n) {
	var	r = n;
	for(var i = 2; i * i <= n; i++) {
		if(n % i != 0) continue;
		while(n % i == 0) n /= i;
		r -= r / i;
	}
	if(n > 1) r -= r / n;
	return r;
}
```

</details>

実行例を以下に示す。引数を何も指定しないとヘルプメッセージを表示する。

```:引数を何も指定しない場合
Chakra GaloisField2.JS
ボールの個数が「１＋素数のべき乗」の場合の解を求めます。

GaloisField2(.js) (オプション) [素数] (指数)

/F　最初の解を発見したら終了します。
```

素数（と指数）を指定した場合，解をソートして出力する。重複する解は出力しない。なお出力行において最初の 5 はボールの個数（１＋与えた素数のべき乗）であり，続く六個の数値は数列のパラメータ，その次の数列がボールの配置となる。最後の行の 1 は得られた解の個数である。

```:素数と指数を指定した場合
Chakra GaloisField2.JS 2 2
5       0 0 0 0 1 1     1 3 10 2 5
1
```

指定可能な指数の範囲は 1～8 である。ちなみに指数の指定を省略すると 1 とみなすが，その場合は GaloisField.js を使用したほうが圧倒的に速い。

```plantuml
@startuml
<style>
root {
  margin: 0;
  padding: 0;
}
caption {
  fontSize: 17;
}
</style>
title <img:https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3490655/8123397a-8527-bb16-234b-06d6f7104dab.png>
caption 図３　ボールの個数と計算時間
@enduml
```

<details><summary><font color=blue><b>JavaScript プログラム GaloisField2.js の実行結果はコチラ</b></font></summary>

<table>
<caption>表４　ボールの個数 $n = 1 + p^m$ と計算時間</caption>
<thead>
<tr><th style="text-align:center">素数 $p$</th>
    <th style="text-align:center">指数 $m$</th>
    <th style="text-align:center">玉の数 $n$</th>
    <th style="text-align:center">解の数</th>
    <th style="text-align:center">計算時間[s]</th></tr>
</thead>
<tbody>
<tr><td style="text-align:right">2</td>
   <td style="text-align:right">2</td>
   <td style="text-align:right">5</td>
   <td style="text-align:right">1</td>
   <td style="text-align:right">0.04</td></tr>
<tr><td style="text-align:right">2</td>
   <td style="text-align:right">3</td>
   <td style="text-align:right">9</td>
   <td style="text-align:right">4</td>
   <td style="text-align:right">0.04</td></tr>
<tr><td style="text-align:right">3</td>
   <td style="text-align:right">2</td>
   <td style="text-align:right">10</td>
   <td style="text-align:right">6</td>
   <td style="text-align:right">0.04</td></tr>
<tr><td style="text-align:right">2</td>
   <td style="text-align:right">4</td>
   <td style="text-align:right">17</td>
   <td style="text-align:right">6</td>
   <td style="text-align:right">0.06</td></tr>
<tr><td style="text-align:right">5</td>
   <td style="text-align:right">2</td>
   <td style="text-align:right">26</td>
   <td style="text-align:right">30</td>
   <td style="text-align:right">0.10</td></tr>
<tr><td style="text-align:right">3</td>
   <td style="text-align:right">3</td>
   <td style="text-align:right">28</td>
   <td style="text-align:right">42</td>
   <td style="text-align:right">0.27</td></tr>
<tr><td style="text-align:right">2</td>
   <td style="text-align:right">5</td>
   <td style="text-align:right">33</td>
   <td style="text-align:right">30</td>
   <td style="text-align:right">0.31</td></tr>
<tr><td style="text-align:right">7</td>
   <td style="text-align:right">2</td>
   <td style="text-align:right">50</td>
   <td style="text-align:right">126</td>
   <td style="text-align:right">2.30</td></tr>
<tr><td style="text-align:right">2</td>
   <td style="text-align:right">6</td>
   <td style="text-align:right">65</td>
   <td style="text-align:right">72</td>
   <td style="text-align:right">7.51</td></tr>
<tr><td style="text-align:right">3</td>
   <td style="text-align:right">4</td>
   <td style="text-align:right">82</td>
   <td style="text-align:right">216</td>
   <td style="text-align:right">28.46</td></tr>
<tr><td style="text-align:right">11</td>
   <td style="text-align:right">2</td>
   <td style="text-align:right">122</td>
   <td style="text-align:right">648</td>
   <td style="text-align:right">28.52</td></tr>
<tr><td style="text-align:right">5</td>
   <td style="text-align:right">3</td>
   <td style="text-align:right">126</td>
   <td style="text-align:right">828</td>
   <td style="text-align:right">161.61</td></tr>
<tr><td style="text-align:right">2</td>
    <td style="text-align:right">7</td>
    <td style="text-align:right">129</td>
    <td style="text-align:right">336</td>
    <td style="text-align:right">157.32</td></tr>
<tr><td style="text-align:right">13</td>
    <td style="text-align:right">2</td>
    <td style="text-align:right">170</td>
    <td style="text-align:right">1,560</td>
    <td style="text-align:right">658.47</td></tr>
<tr><td style="text-align:right">3</td>
    <td style="text-align:right">5</td>
    <td style="text-align:right">244</td>
    <td style="text-align:right">1,824</td>
    <td style="text-align:right">4,975.03</td></tr>
<tr><td style="text-align:right">2</td>
    <td style="text-align:right">8</td>
    <td style="text-align:right">257</td>
    <td style="text-align:right">720</td>
    <td style="text-align:right">7,888.13</td></tr>
<tr><td style="text-align:right">17</td>
    <td style="text-align:right">2</td>
    <td style="text-align:right">290</td>
    <td style="text-align:right">3,672</td>
    <td style="text-align:right">1,248.15</td></tr>
<tr><td style="text-align:right">7</td>
    <td style="text-align:right">3</td>
    <td style="text-align:right">344</td>
    <td style="text-align:right">4,248</td>
    <td style="text-align:right">13,688.70</td></tr>
<tr><td style="text-align:right">19</td>
    <td style="text-align:right">2</td>
    <td style="text-align:right">362</td>
    <td style="text-align:right">6,174</td>
    <td style="text-align:right">17,590.66</td></tr>
</tbody>
</table>

</details>

## 3.4 一つの解から他の解を求める方法

[シリーズ第６回の記事](https://qiita.com/TETSURO1999/items/5211735a34921a73d1e1)で紹介したプログラムを少し改良した。

<details><summary><font color=blue><b>JavaScript プログラム AnotherSolver2.js のソースはコチラ</b></font></summary>

```javascript:AnotherSolver2.js
var	args = WScript.Arguments.Unnamed;
var	opts = WScript.Arguments.Named;
var	ret = main(args, opts)
try {
	WScript.Quit(ret);
} catch(e) {
	/* 何もしない */
}
//------------------------------------------------------------------------------
// メイン関数
//------------------------------------------------------------------------------
function main(args, opts) {
	//--------------------------------------------------------------------------
	// ヘルプメッセージ
	//--------------------------------------------------------------------------
	if(args.Count == 0) {
		WScript.Echo("他のボール配置を求めます。");
		WScript.Echo("");
		WScript.Echo("AnotherSolver2(.js) (オプション) [ボール配置] ...");
		WScript.Echo("");
		WScript.Echo("/R　重複解を含めてそのまま出力します。");
		return false;
	}
	//--------------------------------------------------------------------------
	// コマンドライン解析
	//--------------------------------------------------------------------------
	var	ball = [];
	for(var i = 0; i < args.Count; i++) {
		var	n = parseInt(args(i));
		ball.push(n);
	}
	var	r_flag = opts.Exists("R") ? true : false;
	//--------------------------------------------------------------------------
	// ボール配置のチェック
	//--------------------------------------------------------------------------
	if(!check_ball(ball)) {
		WScript.Echo("ボール配置が不正です!!");
		return false;
	}
	//--------------------------------------------------------------------------
	// ボール配置を完全差集合に変換する
	//--------------------------------------------------------------------------
	var	pcds = [];
	for(var i = 0, n = 0; i < ball.length; i++) {
		pcds.push(n);
		n += ball[i];
	}
	var	ball_sum = n;
	//--------------------------------------------------------------------------
	// 別解を探す
	//--------------------------------------------------------------------------
	var	d = [];
	for(var m = 1; m < ball_sum; m++) {
		//----------------------------------------------------------------------
		// 差集合を生成
		//----------------------------------------------------------------------
		var	s = [];
		for(var i = 0; i < pcds.length; i++) {
			var	n = pcds[i] * m % ball_sum; 
			s.push(n);
		}
		s.push(ball_sum);
		s.sort(function(a, b) {
			return a - b;
		});
		//----------------------------------------------------------------------
		// ボール配置に変換
		//----------------------------------------------------------------------
		var	t = [];
		for(var i = 1; i < s.length; i++)
			t.push(s[i] - s[i - 1]);
		//----------------------------------------------------------------------
		// 回転対称形を除く（１のボールを頭出し）
		//----------------------------------------------------------------------
		for(var i = 0; i < t.length; i++)
			if(t[i] == 1) break;
		if(i >= t.length) continue;
		var	u = [];
		for(var j = i, k = 0; k < t.length; k++) {
			u.push(t[j]);
			if(++j >= t.length) j = 0;
		}
		//----------------------------------------------------------------------
		// 左右対称形を除く
		//----------------------------------------------------------------------
		u.push(1);
		if(u[1] > u[u.length - 2]) u.reverse();
		u.pop();
		//----------------------------------------------------------------------
		// ボール配置のチェック＆重複するボール配置を除く
		//----------------------------------------------------------------------
		if(check_ball(u)) {
			var	v = u.join(" ");
			if(r_flag)
				WScript.Echo(v);
			else if(d[v] == null)
				d[v] = m;
		}
	}
	if(r_flag) return true;
	//--------------------------------------------------------------------------
	// ボール配置をソートして出力する
	//--------------------------------------------------------------------------
	var	e = [];
	for(var v in d)
		e.push({ key:d[v], val:v, arr:v.split(" ") });
	e.sort(function(a, b) {
		for(var i = 0; i < a.arr.length; i++)
			if(a.arr[i] != b.arr[i]) return a.arr[i] - b.arr[i];
		return 0;
	});
	for(var i = 0; i < e.length; i++)
		WScript.Echo(e[i].val);
	//--------------------------------------------------------------------------
	// 解の個数を出力する
	//--------------------------------------------------------------------------
	WScript.Echo(e.length);
	return true;
}
//------------------------------------------------------------------------------
// ボール配置のチェック
//------------------------------------------------------------------------------
function check_ball(ball) {
	var	num = ball.length;
	var	sum = num * (num -  1) + 1;
	for(var i = 0, s = 0; i < ball.length; i++)
		s += ball[i];
	if(sum != s) return false;
	var	flag = [];
	for(var i = 0; i <= sum; i++)
		flag.push(false);
	for(var i = 0; i < ball.length - 1; i++) {
		for(var j = 0, s = 0; i + j >= 0; j--) {
			s += ball[i + j];
			if(flag[s] || flag[sum - s]) return false;
			flag[s] = true;
			flag[sum - s] = true;
		}
	}
	return true;
}
```

</details>

実行例を以下に示す。引数を何も指定しないとヘルプメッセージを表示する。

```:引数を何も指定しない場合
Chakra AnotherSolver2.js
他のボール配置を求めます。

AnotherSolver2(.js) (オプション) [ボール配置] ...

/R　重複解を含めてそのまま出力します。
```

ボール配置のみを指定した場合，全ての解（指定した解も含む）をソートして出力する。最後の行の 2 は得られた解の個数である。

```:ボール配置のみを指定した場合
Chakra AnotherSolver2.js 1 2 6 4
1 2 6 4
1 3 2 7
2
```

オプション `/R` を指定すると重複解を含めて得られた解を発見順に出力する。なお，オプション `/R` は使用メモリが少なくて済むので，与えたデータサイズが大き過ぎてメモリ不足になる場合はこのオプションを使用すると良い。

```:オプション /R を指定した場合
Chakra AnotherSolver2.js 1 2 6 4 /R
1 2 6 4
1 3 2 7
1 2 6 4
1 2 6 4
1 3 2 7
1 3 2 7
1 3 2 7
1 3 2 7
1 2 6 4
1 2 6 4
1 3 2 7
1 2 6 4
```

この場合，Windows 付属の `sort` コマンドを使えば重複解を削除できる。

```:sort コマンドを使って重複解を削除
Chakra AnotherSolver2.js 1 2 6 4 /R | sort /unique
1 2 6 4
1 3 2 7
```

いつのまにか Windows10 付属の `sort` コマンドに `/unique` オプションが追加されていたようだ。下記サイトは Windows Server の情報であるが，クライアント側の Windows10 やもちろん Windows11 でも使用できるようだ。

[https://learn.microsoft.com/ja-jp/windows-server/administration/windows-commands/sort](https://learn.microsoft.com/ja-jp/windows-server/administration/windows-commands/sort)

# 4. まとめ

総括編と言いつつも，これまでの一連のシリーズで紹介してきた自作プログラムのブラッシュアップ版の紹介になってしまった。

理論的な説明については，[シリーズ第７回の記事](https://qiita.com/TETSURO1999/items/f6d0839aec49692a03e0)あるいは[第８回の記事](https://qiita.com/TETSURO1999/items/051bb652fb5c9b6c8b6e)あたりを参照して欲しいが，下記参考文献に直接当たるのも良いかもしれない。実は James Singer の論文にほぼ全てのことが最初から記載されていたのだ。

# 5. 参考文献

- [森博嗣の小説中の問題をRubyで解いた - Qiita](https://qiita.com/tachiyama/items/c7faf3cc2029a10a1cd0)
- 島内剛一，λ倍取りゲーム，数学セミナー1982年1月号，10～14頁，日本評論社
- 下林山稔，パソコンで魔円陣を，数学セミナー1987年7月号，55～59頁，日本評論社
- [秋山茂樹，魔円陣と有限幾何，数学セミナー2013年7月号，25～31頁，日本評論社 <img width=16 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3490655/66e66c9d-a6f3-f215-951a-9d79cc94a9d6.png">](http://math.tsukuba.ac.jp/~akiyama/papers/proc/MagicCircle.pdf)
- [小林吹代，ガロアの数学「体」入門～魔円陣とオイラー方陣を例に～，技術評論社](https://gihyo.jp/book/2018/978-4-7741-9748-7)
- [Kris Coolsaet, Cyclic Difference Sets](http://www.inference.org.uk/cds/index.htm)
- [Justin Colannino, Circular and Modular Golomb Rulers](http://cgm.cs.mcgill.ca/~athens/cs507/Projects/2003/JustinColannino/)
- [James Singer, A Theorem in Finite Projective Geometry and Some Applications to Number Theory, American  Mathematical Society, 43-3 (1938), pp.377-385 <img width=16 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3490655/66e66c9d-a6f3-f215-951a-9d79cc94a9d6.png">](https://www.ams.org/journals/tran/1938-043-03/S0002-9947-1938-1501951-4/S0002-9947-1938-1501951-4.pdf)
- David A. James, Magic Circles, Mathematics Magazine, Vol.54, No.3 (1981), pp.122-125