素数大富豪で天和するためのヒューリスティックのヒューリスティック

これは [素数大富豪 Advent Calendar 2024](https://adventar.org/calendars/9973) 14日目の記事です。

昨日はもりしーさんによる [素数大富豪で遊ぼう会in札幌　開催報告/告知](https://prm9973.hatenablog.com/entry/2024/12/13/000000) でした。素数大富豪は本当に色々なところでプレイされていますね。

## 導入

素数大富豪には**天和**と呼ばれる現象があります。初手で手札の全てを出し、それが素数であることによって**先手の第1ターンでゲームが終了する**という現象です。天和は主にカマトトに失敗したとき発生します。私の経験では天和の発生率は5%～10%くらい、素数大富豪大会全体で1回は見る感じがします。

天和といえば、2024年5月前半に開催された「素数大富豪夏の選手権2024」(PQCS2024)で「【ネタ枠】天 和子」さんが**セット勝率0.07**という衝撃的な成績を叩き出したことが思い出されますね。**私なんですけど。**

この記事では、そんな天和を出しやすくするためのヒューリスティックについて考察します。

**簡単のため、以下ではジョーカーについては一旦考えないことにします。**

## 定義

ここでは簡単な戦法として、「あらかじめA～Kの順番を決めておいて、手札をその順で出す。ただし末尾が偶数(5も含む)の時は、偶数でない最も右のカードがもしあればそれを末尾に移動させる」というものを考えます。決めたA～Kの順番のことを**戦略**と呼ぶことにします。

例えば戦略が1TJQK23456789で手札が123357788KKであれば、1KK23357887を出すことになります[^1]。

トランプ52枚からスートを無視して11枚を取り出す方法は1111760通りあります[^2]。全1111760通りのうち、ある戦略で出した時に素数となるものがいくつあるかをその戦略の**評価値**と呼ぶことにします。
評価値の高い戦略ほど、天和に適したものとなります。

## 実際にやってみようとした

というわけで各戦略の評価値を求めて最も大きいものを探せばこの記事は完結…といきたいところですが、そう簡単ではありません。
戦略は全部で$ 13! $個あり、1つの戦略の評価値を計算するのに私の環境では30秒くらいかかります。
すなわち全部の評価値を求めるのに

$$ \frac{13! \times 30}{31536000} = 5923.7 \cdots (\text{years}) $$

5923年かかります。

これではアドベントカレンダーに間に合いません。というわけで、ヒューリスティックを使用します。

## 作戦

素数の出現頻度は数の大きさに依存するものの、大きさがほぼ同じ数の間ではランダムです。そこで、出す数の値が小さくなりやすい戦略(N系統)、出す数の値が大きくなりやすい戦略(X系統)、ランダムに選んだ戦略(D系統)[^3]を同じ個数評価して、数の大きさによる評価値の違いを見ます。

サンプル数を稼ぎたいため、3段階に分けて評価を行います。

### 予選

手札から10000個(約1%)のサンプルをランダムに選択し、その中で素数ができる個数を評価値の代わりに使います。

対象とする戦略は

- N系統では左はAJTQK2で固定し右は3456789の順列を全探索
- X系統では左は987654で固定し右はA23TJQKの順列を全探索
- R系統ではランダムに5040個選ぶ

とします。これの所要時間は

$$ \frac{30 \times 0.01 \times 5040 \times 3}{60} = 75.6 (\text{minutes}) $$

と推定され、アドベントカレンダーに間に合います。

#### 結果

| 系統 | 平均 | 標準偏差 | 1位 |
|:----------:|:------------:|:------------:|:------------:|
| N系統 | 822.698 | 27.951 | AJTQK29654738(924個) |
| X系統 | 771.815 | 26.913 | 987654AKQTJ23(865個) |
| D系統 | 792.960 | 29.313 | KA847592TQJ63(900個) |

最大と最小で2標準偏差程度の違いがあるようです。また、X系統の1位はAと絵札が固まっており、やはり小さい数字の方が素数の割合が高いようです。

## 準決勝

N系統の5040個に対し、サンプリングする手札を3倍に増やし、上位120個の評価値を決勝で個別に求めます。同順の場合は全員その順位としてその分決勝に進むエントリーが増えます。

…と思いましたが、同順も含めて1544個出てきました。さすがに多すぎます。アドベントカレンダーに間に合いません。

上位60個にしてもう一度決勝エントリーを集めたところ、227個になりました。これならなんとか間に合いそうです。

## 決勝

VSCodeからWSLを2時間ぶん回して結果が出ました。優勝は

$$ \text{AJTQK24357869} $$

です！！！

1111760通りの手札のうち、93255個が素数になります。割合だと約8.4%で、先行を獲得できる確率が50%であるとすれば**約4.2%の確率でワンターンキルできる**ことがわかります！！！

## 検証コード
候補をコード内にそのまま貼るとQiitaのエディタがあまりにも重くなるので分割しています。実行する際は各自のエディタ内で候補の配列を該当箇所に貼り付けてください。

### 候補
```
[[1, 11, 10, 12, 13, 2, 3, 4, 5, 7, 6, 9, 8], [1, 11, 10, 12, 13, 2, 3, 4, 5, 7, 8, 9, 6], [1, 11, 10, 12, 13, 2, 3, 4, 5, 8, 7, 9, 6], [1, 11, 10, 12, 13, 2, 3, 4, 7, 5, 8, 6, 9], [1, 11, 10, 12, 13, 2, 3, 4, 7, 5, 9, 6, 8], [1, 11, 10, 12, 13, 2, 3, 4, 7, 5, 9, 8, 6], [1, 11, 10, 12, 13, 2, 3, 4, 7, 6, 5, 9, 8], [1, 11, 10, 12, 13, 2, 3, 4, 7, 8, 6, 5, 9], [1, 11, 10, 12, 13, 2, 3, 4, 8, 7, 9, 5, 6], [1, 11, 10, 12, 13, 2, 3, 4, 9, 5, 7, 8, 6], [1, 11, 10, 12, 13, 2, 3, 5, 4, 6, 7, 8, 9], [1, 11, 10, 12, 13, 2, 3, 5, 4, 6, 7, 9, 8], [1, 11, 10, 12, 13, 2, 3, 5, 4, 7, 6, 8, 9], [1, 11, 10, 12, 13, 2, 3, 5, 4, 9, 7, 8, 6], [1, 11, 10, 12, 13, 2, 3, 5, 6, 4, 7, 8, 9], [1, 11, 10, 12, 13, 2, 3, 5, 6, 7, 4, 8, 9], [1, 11, 10, 12, 13, 2, 3, 5, 6, 7, 8, 4, 9], [1, 11, 10, 12, 13, 2, 3, 5, 6, 8, 4, 7, 9], [1, 11, 10, 12, 13, 2, 3, 5, 7, 6, 9, 4, 8], [1, 11, 10, 12, 13, 2, 3, 5, 8, 4, 9, 6, 7], [1, 11, 10, 12, 13, 2, 3, 6, 5, 4, 8, 7, 9], [1, 11, 10, 12, 13, 2, 3, 6, 5, 8, 4, 7, 9], [1, 11, 10, 12, 13, 2, 3, 6, 7, 9, 8, 4, 5], [1, 11, 10, 12, 13, 2, 3, 6, 8, 5, 4, 9, 7], [1, 11, 10, 12, 13, 2, 3, 7, 5, 4, 6, 9, 8], [1, 11, 10, 12, 13, 2, 3, 7, 5, 8, 4, 6, 9], [1, 11, 10, 12, 13, 2, 3, 7, 6, 8, 5, 9, 4], [1, 11, 10, 12, 13, 2, 3, 7, 9, 6, 4, 5, 8], [1, 11, 10, 12, 13, 2, 3, 8, 5, 4, 9, 7, 6], [1, 11, 10, 12, 13, 2, 3, 8, 5, 7, 6, 4, 9], [1, 11, 10, 12, 13, 2, 3, 8, 6, 7, 4, 9, 5], [1, 11, 10, 12, 13, 2, 3, 8, 6, 7, 9, 4, 5], [1, 11, 10, 12, 13, 2, 3, 8, 7, 5, 9, 4, 6], [1, 11, 10, 12, 13, 2, 3, 9, 4, 5, 8, 7, 6], [1, 11, 10, 12, 13, 2, 3, 9, 5, 4, 7, 6, 8], [1, 11, 10, 12, 13, 2, 3, 9, 5, 7, 4, 6, 8], [1, 11, 10, 12, 13, 2, 3, 9, 5, 7, 8, 4, 6], [1, 11, 10, 12, 13, 2, 3, 9, 7, 4, 6, 8, 5], [1, 11, 10, 12, 13, 2, 3, 9, 7, 6, 5, 8, 4], [1, 11, 10, 12, 13, 2, 4, 3, 5, 6, 8, 9, 7], [1, 11, 10, 12, 13, 2, 4, 3, 5, 7, 6, 8, 9], [1, 11, 10, 12, 13, 2, 4, 3, 5, 7, 8, 6, 9], [1, 11, 10, 12, 13, 2, 4, 3, 5, 7, 8, 9, 6], [1, 11, 10, 12, 13, 2, 4, 3, 5, 8, 6, 9, 7], [1, 11, 10, 12, 13, 2, 4, 3, 6, 5, 7, 8, 9], [1, 11, 10, 12, 13, 2, 4, 3, 6, 5, 8, 7, 9], [1, 11, 10, 12, 13, 2, 4, 3, 6, 5, 8, 9, 7], [1, 11, 10, 12, 13, 2, 4, 3, 6, 5, 9, 7, 8], [1, 11, 10, 12, 13, 2, 4, 3, 6, 7, 5, 9, 8], [1, 11, 10, 12, 13, 2, 4, 3, 6, 9, 7, 8, 5], [1, 11, 10, 12, 13, 2, 4, 3, 7, 5, 9, 8, 6], [1, 11, 10, 12, 13, 2, 4, 3, 7, 6, 8, 9, 5], [1, 11, 10, 12, 13, 2, 4, 5, 3, 7, 9, 8, 6], [1, 11, 10, 12, 13, 2, 4, 5, 3, 8, 9, 7, 6], [1, 11, 10, 12, 13, 2, 4, 5, 6, 3, 8, 7, 9], [1, 11, 10, 12, 13, 2, 4, 5, 6, 9, 8, 3, 7], [1, 11, 10, 12, 13, 2, 4, 5, 7, 3, 9, 8, 6], [1, 11, 10, 12, 13, 2, 4, 5, 7, 8, 6, 9, 3], [1, 11, 10, 12, 13, 2, 4, 5, 8, 3, 7, 9, 6], [1, 11, 10, 12, 13, 2, 4, 5, 8, 6, 3, 7, 9], [1, 11, 10, 12, 13, 2, 4, 5, 9, 8, 3, 7, 6], [1, 11, 10, 12, 13, 2, 4, 5, 9, 8, 7, 6, 3], [1, 11, 10, 12, 13, 2, 4, 6, 3, 9, 8, 5, 7], [1, 11, 10, 12, 13, 2, 4, 6, 8, 5, 7, 3, 9], [1, 11, 10, 12, 13, 2, 4, 6, 8, 9, 3, 5, 7], [1, 11, 10, 12, 13, 2, 4, 6, 9, 8, 5, 3, 7], [1, 11, 10, 12, 13, 2, 4, 7, 3, 5, 8, 6, 9], [1, 11, 10, 12, 13, 2, 4, 7, 3, 6, 8, 9, 5], [1, 11, 10, 12, 13, 2, 4, 7, 3, 9, 6, 5, 8], [1, 11, 10, 12, 13, 2, 4, 7, 5, 3, 6, 8, 9], [1, 11, 10, 12, 13, 2, 4, 7, 5, 8, 9, 3, 6], [1, 11, 10, 12, 13, 2, 4, 7, 6, 5, 8, 3, 9], [1, 11, 10, 12, 13, 2, 4, 7, 8, 3, 6, 5, 9], [1, 11, 10, 12, 13, 2, 4, 8, 3, 7, 5, 9, 6], [1, 11, 10, 12, 13, 2, 4, 8, 5, 6, 3, 7, 9], [1, 11, 10, 12, 13, 2, 4, 8, 5, 7, 6, 3, 9], [1, 11, 10, 12, 13, 2, 4, 8, 6, 5, 9, 3, 7], [1, 11, 10, 12, 13, 2, 4, 8, 7, 5, 3, 9, 6], [1, 11, 10, 12, 13, 2, 4, 8, 7, 6, 3, 5, 9], [1, 11, 10, 12, 13, 2, 4, 8, 7, 9, 6, 5, 3], [1, 11, 10, 12, 13, 2, 4, 8, 9, 3, 7, 5, 6], [1, 11, 10, 12, 13, 2, 4, 8, 9, 7, 6, 5, 3], [1, 11, 10, 12, 13, 2, 4, 9, 3, 6, 5, 8, 7], [1, 11, 10, 12, 13, 2, 4, 9, 5, 3, 6, 8, 7], [1, 11, 10, 12, 13, 2, 4, 9, 5, 7, 8, 6, 3], [1, 11, 10, 12, 13, 2, 4, 9, 6, 5, 7, 3, 8], [1, 11, 10, 12, 13, 2, 4, 9, 7, 6, 5, 3, 8], [1, 11, 10, 12, 13, 2, 4, 9, 7, 6, 8, 5, 3], [1, 11, 10, 12, 13, 2, 4, 9, 8, 3, 7, 6, 5], [1, 11, 10, 12, 13, 2, 4, 9, 8, 5, 3, 7, 6], [1, 11, 10, 12, 13, 2, 5, 3, 4, 6, 8, 7, 9], [1, 11, 10, 12, 13, 2, 5, 3, 4, 7, 6, 8, 9], [1, 11, 10, 12, 13, 2, 5, 3, 4, 7, 9, 8, 6], [1, 11, 10, 12, 13, 2, 5, 3, 6, 4, 7, 9, 8], [1, 11, 10, 12, 13, 2, 5, 3, 6, 4, 8, 7, 9], [1, 11, 10, 12, 13, 2, 5, 3, 8, 4, 7, 9, 6], [1, 11, 10, 12, 13, 2, 5, 3, 8, 7, 9, 4, 6], [1, 11, 10, 12, 13, 2, 5, 3, 9, 6, 7, 4, 8], [1, 11, 10, 12, 13, 2, 5, 4, 3, 6, 8, 7, 9], [1, 11, 10, 12, 13, 2, 5, 4, 3, 8, 9, 7, 6], [1, 11, 10, 12, 13, 2, 5, 4, 6, 8, 9, 3, 7], [1, 11, 10, 12, 13, 2, 5, 4, 7, 8, 9, 6, 3], [1, 11, 10, 12, 13, 2, 5, 4, 7, 9, 6, 3, 8], [1, 11, 10, 12, 13, 2, 5, 4, 8, 9, 6, 3, 7], [1, 11, 10, 12, 13, 2, 5, 4, 8, 9, 7, 3, 6], [1, 11, 10, 12, 13, 2, 5, 6, 4, 7, 9, 8, 3], [1, 11, 10, 12, 13, 2, 5, 6, 4, 9, 3, 8, 7], [1, 11, 10, 12, 13, 2, 5, 6, 8, 3, 7, 4, 9], [1, 11, 10, 12, 13, 2, 5, 7, 3, 4, 6, 9, 8], [1, 11, 10, 12, 13, 2, 5, 7, 4, 3, 9, 8, 6], [1, 11, 10, 12, 13, 2, 5, 7, 9, 4, 6, 3, 8], [1, 11, 10, 12, 13, 2, 5, 7, 9, 8, 3, 6, 4], [1, 11, 10, 12, 13, 2, 5, 8, 3, 7, 6, 4, 9], [1, 11, 10, 12, 13, 2, 5, 8, 6, 3, 4, 7, 9], [1, 11, 10, 12, 13, 2, 5, 8, 6, 7, 4, 3, 9], [1, 11, 10, 12, 13, 2, 5, 8, 6, 9, 3, 4, 7], [1, 11, 10, 12, 13, 2, 5, 9, 3, 4, 8, 6, 7], [1, 11, 10, 12, 13, 2, 5, 9, 3, 7, 6, 4, 8], [1, 11, 10, 12, 13, 2, 5, 9, 6, 7, 4, 8, 3], [1, 11, 10, 12, 13, 2, 5, 9, 8, 4, 3, 7, 6], [1, 11, 10, 12, 13, 2, 6, 3, 4, 8, 9, 7, 5], [1, 11, 10, 12, 13, 2, 6, 3, 5, 4, 7, 9, 8], [1, 11, 10, 12, 13, 2, 6, 3, 5, 4, 8, 7, 9], [1, 11, 10, 12, 13, 2, 6, 3, 7, 9, 8, 5, 4], [1, 11, 10, 12, 13, 2, 6, 3, 8, 9, 4, 5, 7], [1, 11, 10, 12, 13, 2, 6, 3, 9, 8, 4, 5, 7], [1, 11, 10, 12, 13, 2, 6, 3, 9, 8, 5, 7, 4], [1, 11, 10, 12, 13, 2, 6, 4, 3, 5, 7, 9, 8], [1, 11, 10, 12, 13, 2, 6, 4, 3, 8, 7, 9, 5], [1, 11, 10, 12, 13, 2, 6, 4, 7, 5, 3, 8, 9], [1, 11, 10, 12, 13, 2, 6, 4, 8, 3, 7, 5, 9], [1, 11, 10, 12, 13, 2, 6, 4, 8, 5, 3, 7, 9], [1, 11, 10, 12, 13, 2, 6, 5, 4, 3, 7, 8, 9], [1, 11, 10, 12, 13, 2, 6, 5, 4, 3, 7, 9, 8], [1, 11, 10, 12, 13, 2, 6, 5, 4, 7, 8, 3, 9], [1, 11, 10, 12, 13, 2, 6, 5, 4, 8, 3, 7, 9], [1, 11, 10, 12, 13, 2, 6, 5, 4, 8, 7, 9, 3], [1, 11, 10, 12, 13, 2, 6, 7, 3, 9, 5, 4, 8], [1, 11, 10, 12, 13, 2, 6, 7, 8, 3, 5, 4, 9], [1, 11, 10, 12, 13, 2, 6, 7, 9, 3, 8, 4, 5], [1, 11, 10, 12, 13, 2, 6, 7, 9, 5, 8, 4, 3], [1, 11, 10, 12, 13, 2, 6, 8, 5, 3, 9, 4, 7], [1, 11, 10, 12, 13, 2, 6, 8, 5, 7, 4, 9, 3], [1, 11, 10, 12, 13, 2, 6, 8, 7, 5, 9, 4, 3], [1, 11, 10, 12, 13, 2, 6, 8, 9, 7, 3, 5, 4], [1, 11, 10, 12, 13, 2, 6, 9, 3, 4, 8, 5, 7], [1, 11, 10, 12, 13, 2, 6, 9, 3, 7, 5, 8, 4], [1, 11, 10, 12, 13, 2, 6, 9, 7, 5, 8, 4, 3], [1, 11, 10, 12, 13, 2, 6, 9, 8, 3, 5, 7, 4], [1, 11, 10, 12, 13, 2, 6, 9, 8, 5, 4, 3, 7], [1, 11, 10, 12, 13, 2, 7, 3, 4, 9, 8, 6, 5], [1, 11, 10, 12, 13, 2, 7, 3, 6, 5, 8, 4, 9], [1, 11, 10, 12, 13, 2, 7, 3, 6, 5, 9, 8, 4], [1, 11, 10, 12, 13, 2, 7, 3, 9, 8, 5, 4, 6], [1, 11, 10, 12, 13, 2, 7, 4, 3, 6, 8, 9, 5], [1, 11, 10, 12, 13, 2, 7, 4, 6, 8, 5, 3, 9], [1, 11, 10, 12, 13, 2, 7, 4, 8, 6, 5, 9, 3], [1, 11, 10, 12, 13, 2, 7, 5, 4, 8, 9, 6, 3], [1, 11, 10, 12, 13, 2, 7, 5, 6, 3, 4, 9, 8], [1, 11, 10, 12, 13, 2, 7, 5, 6, 4, 3, 8, 9], [1, 11, 10, 12, 13, 2, 7, 5, 8, 9, 6, 4, 3], [1, 11, 10, 12, 13, 2, 7, 5, 9, 6, 4, 8, 3], [1, 11, 10, 12, 13, 2, 7, 5, 9, 8, 3, 6, 4], [1, 11, 10, 12, 13, 2, 7, 6, 4, 3, 5, 9, 8], [1, 11, 10, 12, 13, 2, 7, 6, 5, 9, 3, 4, 8], [1, 11, 10, 12, 13, 2, 7, 6, 8, 4, 9, 3, 5], [1, 11, 10, 12, 13, 2, 7, 6, 9, 8, 3, 4, 5], [1, 11, 10, 12, 13, 2, 7, 8, 6, 4, 5, 9, 3], [1, 11, 10, 12, 13, 2, 7, 8, 6, 4, 9, 5, 3], [1, 11, 10, 12, 13, 2, 7, 8, 9, 5, 3, 6, 4], [1, 11, 10, 12, 13, 2, 7, 8, 9, 6, 3, 4, 5], [1, 11, 10, 12, 13, 2, 7, 8, 9, 6, 5, 4, 3], [1, 11, 10, 12, 13, 2, 7, 9, 6, 4, 3, 5, 8], [1, 11, 10, 12, 13, 2, 7, 9, 8, 4, 6, 5, 3], [1, 11, 10, 12, 13, 2, 8, 3, 4, 9, 5, 6, 7], [1, 11, 10, 12, 13, 2, 8, 3, 4, 9, 6, 5, 7], [1, 11, 10, 12, 13, 2, 8, 3, 6, 4, 9, 7, 5], [1, 11, 10, 12, 13, 2, 8, 4, 3, 5, 9, 6, 7], [1, 11, 10, 12, 13, 2, 8, 4, 3, 6, 5, 7, 9], [1, 11, 10, 12, 13, 2, 8, 4, 5, 9, 7, 6, 3], [1, 11, 10, 12, 13, 2, 8, 4, 6, 7, 3, 9, 5], [1, 11, 10, 12, 13, 2, 8, 4, 7, 9, 6, 3, 5], [1, 11, 10, 12, 13, 2, 8, 4, 9, 6, 5, 7, 3], [1, 11, 10, 12, 13, 2, 8, 5, 3, 4, 7, 6, 9], [1, 11, 10, 12, 13, 2, 8, 5, 6, 4, 9, 3, 7], [1, 11, 10, 12, 13, 2, 8, 5, 6, 7, 3, 9, 4], [1, 11, 10, 12, 13, 2, 8, 5, 9, 6, 3, 4, 7], [1, 11, 10, 12, 13, 2, 8, 6, 7, 5, 3, 9, 4], [1, 11, 10, 12, 13, 2, 8, 7, 3, 5, 4, 6, 9], [1, 11, 10, 12, 13, 2, 8, 7, 5, 3, 6, 4, 9], [1, 11, 10, 12, 13, 2, 8, 7, 5, 6, 4, 9, 3], [1, 11, 10, 12, 13, 2, 8, 7, 5, 9, 4, 6, 3], [1, 11, 10, 12, 13, 2, 8, 7, 6, 4, 5, 3, 9], [1, 11, 10, 12, 13, 2, 8, 9, 3, 4, 7, 6, 5], [1, 11, 10, 12, 13, 2, 8, 9, 3, 5, 6, 4, 7], [1, 11, 10, 12, 13, 2, 8, 9, 3, 7, 5, 4, 6], [1, 11, 10, 12, 13, 2, 8, 9, 3, 7, 6, 5, 4], [1, 11, 10, 12, 13, 2, 8, 9, 4, 3, 5, 6, 7], [1, 11, 10, 12, 13, 2, 8, 9, 5, 7, 3, 6, 4], [1, 11, 10, 12, 13, 2, 8, 9, 5, 7, 6, 3, 4], [1, 11, 10, 12, 13, 2, 8, 9, 6, 4, 3, 7, 5], [1, 11, 10, 12, 13, 2, 8, 9, 6, 4, 5, 7, 3], [1, 11, 10, 12, 13, 2, 8, 9, 7, 5, 6, 3, 4], [1, 11, 10, 12, 13, 2, 8, 9, 7, 6, 4, 3, 5], [1, 11, 10, 12, 13, 2, 9, 3, 6, 4, 5, 8, 7], [1, 11, 10, 12, 13, 2, 9, 3, 6, 5, 8, 4, 7], [1, 11, 10, 12, 13, 2, 9, 3, 8, 6, 4, 5, 7], [1, 11, 10, 12, 13, 2, 9, 4, 6, 8, 5, 7, 3], [1, 11, 10, 12, 13, 2, 9, 5, 3, 4, 7, 8, 6], [1, 11, 10, 12, 13, 2, 9, 5, 3, 7, 4, 6, 8], [1, 11, 10, 12, 13, 2, 9, 5, 7, 3, 8, 6, 4], [1, 11, 10, 12, 13, 2, 9, 5, 7, 6, 3, 4, 8], [1, 11, 10, 12, 13, 2, 9, 6, 3, 5, 8, 7, 4], [1, 11, 10, 12, 13, 2, 9, 6, 5, 4, 7, 3, 8], [1, 11, 10, 12, 13, 2, 9, 6, 8, 4, 3, 7, 5], [1, 11, 10, 12, 13, 2, 9, 6, 8, 7, 3, 5, 4], [1, 11, 10, 12, 13, 2, 9, 7, 3, 8, 6, 4, 5], [1, 11, 10, 12, 13, 2, 9, 7, 5, 6, 4, 3, 8], [1, 11, 10, 12, 13, 2, 9, 7, 6, 3, 4, 5, 8], [1, 11, 10, 12, 13, 2, 9, 7, 6, 8, 4, 3, 5], [1, 11, 10, 12, 13, 2, 9, 7, 6, 8, 5, 4, 3], [1, 11, 10, 12, 13, 2, 9, 7, 8, 3, 4, 5, 6], [1, 11, 10, 12, 13, 2, 9, 7, 8, 5, 4, 6, 3], [1, 11, 10, 12, 13, 2, 9, 7, 8, 6, 4, 3, 5], [1, 11, 10, 12, 13, 2, 9, 8, 5, 7, 3, 6, 4], [1, 11, 10, 12, 13, 2, 9, 8, 7, 6, 3, 4, 5], [1, 11, 10, 12, 13, 2, 9, 8, 7, 6, 3, 5, 4]]
```

### コード
```ruby
# https://qiita.com/Masaaki/items/c28fa5660ca77bcebf9c ここから
class Integer
  def prime?
    n = self.abs()
    return true if n == 2
    return false if n == 1 || n & 1 == 0
    d = n-1
    d >>= 1 while d & 1 == 0
    # 4^(-20)の確率で合成数が素数と判定されることに注意
    # 30.times doにすると、4^(-30)の確率になる
    20.times do
      a = rand(n-2) + 1
      t = d
      y = ModMath.pow(a,t,n)
      while t != n-1 && y != 1 && y != n-1
        y = (y * y) % n
        t <<= 1
      end
      return false if y != n-1 && t & 1 == 0
    end
    return true
  end
end

module ModMath
  def ModMath.pow(base, power, mod)
    result = 1
    while power > 0
      result = (result * base) % mod if power & 1 == 1
      base = (base * base) % mod
      power >>= 1;
    end
    result
  end
end
# https://qiita.com/Masaaki/items/c28fa5660ca77bcebf9c ここまで

# 5は偶数
def is_even?(number)
  number % 2 == 0 || number % 5 == 0
end

def is_odd?(number)
  !is_even?(number)
end

# 52枚から11枚を引く方法として有効かを調べる
# ジョーカーには対応していない
def valid_hand?(hand, length = 11)
  return false if hand.any? { |card| card <= 0 || 13 < card }
  return false unless hand.length == length
  return false if hand.tally.values.any? { |count| count > 4 }
  true
end

# 11枚の引き方の全配列
ALL_HANDS = []

# 11枚の引き方の全配列を列挙する
current = [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3]
LAST = [11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13]
while true do
  ALL_HANDS.push(current.dup)
  break if current == LAST
  # 次に小さい引き方を探す
  # do-whileを実現したい
  i = -1
  loop do
    current[i] += 1
    for j in (i + 1)..-1 do
      current[j] = current[i]
    end
    break if valid_hand?(current)
    if current[-1] == 14
      i -= 1
    else
      i = -1
    end
  end
end

# 1111760と表示される
# https://otty8121013.hatenadiary.jp/entry/2022/12/14/064642 と同じ結果なので配列の中身はたぶん正しい
p ALL_HANDS.length

# 戦略に沿って手札を並べ替える
def get_rearrangement(strategy, hand)
  new_hand = hand.sort_by { |card| strategy.index(card) }
  result_hand = new_hand
  # 偶数が一番下で、奇数がもしあれば一番下までスライドさせる
  if is_even?(new_hand[-1]) && new_hand.any? { |card| is_odd?(card) }
    slide_index = new_hand.rindex { |card| is_odd?(card) }
    result_hand = new_hand[0...slide_index] + new_hand[(slide_index + 1)..-1] + [new_hand[slide_index]]
  end
  result_hand
end

# 戦略と手札から出す値を求める
def get_number(strategy, hand)
  get_rearrangement(strategy, hand).map(&:to_s).join("").to_i
end

# 戦略と手札から素数かどうか判定する
def get_result(strategy, hand)
  get_number(strategy, hand).prime?
end

# 戦略に対する素数の個数を数える
def get_score(strategy, sample = ALL_HANDS.length)
  i = 0
  count = 0
  ALL_HANDS.shuffle.first(sample).each do |hand|
    i += 1
    count += 1 if get_result(strategy, hand)
  end
  count
end

# 平均
def average(array)
  array.sum.fdiv(array.length)
end

# 標準偏差
def stdevp(array)
  # 分散 = 2乗の平均 - 平均の2乗
  variance = average(array.map { |num| num ** 2 }) - average(array) ** 2
  Math.sqrt(variance)
end

# 戦略
=begin
results = {}
result_counts = []
strategies = [3, 4, 5, 6, 7, 8, 9].permutation(7).map { |array| [1, 11, 10, 12, 13, 2] + array }
# strategies = [1, 2, 3, 10, 11, 12, 13].permutation(7).map { |array| [9, 8, 7, 6, 5, 4] + array }
# strategies = Array.new(5040) { (1..13).to_a.shuffle }
count = 0
strategies.each do |strategy|
  result = get_score(strategy, 30000)
  result_counts.push(result)
  results[strategy] = result
  count += 1
  p count if count % 10 == 0
end

## 集計
puts "Average: #{average(result_counts)}"
puts "Stdev.p: #{stdevp(result_counts)}"
max_result = result_counts.uniq.max(60)
p max_result
p results.filter { |strategy, result| max_result.index(result) }.keys
=end

## 決勝

candidates = [] # ここに候補を貼り付け
p candidates.length

results = {}
result_counts = []
count = 0
candidates.each do |strategy|
  result = get_score(strategy)
  result_counts.push(result)
  results[strategy] = result
  count += 1
  p [count, strategy, result, Time.now]
end

p results

## 集計
puts "Average: #{average(result_counts)}"
puts "Stdev.p: #{stdevp(result_counts)}"
max_result = result_counts.uniq.max
p max_result
p results.filter { |strategy, result| max_result == result }.keys

```

## リンク
- 素数判定: [与えられた数が素数か合成数かを判定する最速の方法(Ruby)](https://qiita.com/Masaaki/items/c28fa5660ca77bcebf9c) (Masaaki さん)
- 昨日の記事: [素数大富豪で遊ぼう会in札幌　開催報告/告知](https://prm9973.hatenablog.com/entry/2024/12/13/000000) (もりしー さん)
- 明日の記事: ななみ目線で今年の活動を振り返る(遊ぼう会) (ななみ さん)

[^1]: 1KK23357887は素数ではなく、6221×181855547です。
[^2]: JJ76X=2^4×5×K×T69|X=0であるため、初期手札+1枚ドローで2456679TJJKXになればこの数を合成数地和することができます。
[^3]: miN, maX, rnDから取っています。