← ブログ一覧

ミニロトをスペクトル解析——結論が逆転した理由

ミニロトの当選番号を物理学の乱数行列理論(GOE/GUE統計)で解析するスクリプトを実行したら、サンプルデータと実データ1393回で正反対の結論が出た検証記録。


この記事の結論: 統計スクリプトの「データが無ければサンプルで自動フォールバック」という仕様に気づかず結果だけ見ると、実データと真逆の結論を信じてしまう。p値の大小だけで「非ランダムだ」と判断するのも危険で、離散データ×大サンプルではKS検定のp値は機械的に小さくなりやすい。

リナ社長(高校生社長のやり手ギャル)× タクヤ(入社3年目)— 今回は、物理学の乱数行列理論でミニロトの当選番号を解析するスクリプトを2パターンで実行したら結論が逆転した、その原因を追いかける回。(登場人物について


リナ社長「タクヤ、analysis/miniloto_spectral.py って前からリポジトリにあったやつ、まだ動かしたことなくない?」

タクヤ「ありますね……GOEとかGUEとか、量子カオスの統計理論を使ってるって聞いたときは正直ちょっと引きました」

リナ社長「あたしも中身見てビビった。でもせっかくあるし動かしてみよ。前にバックテストで頻度ベースの予測手法を20個比較した記事あったじゃん?あれとは全然別のアプローチらしいよ」

タクヤ「頻度ベースは”どの番号がよく出るか”を数えるやつでしたけど、これは違うんですか?」

リナ社長「そう。今回のは番号と番号の”間隔”に着目して、その間隔がPoisson分布(完全ランダム)に従うか、GOE・GUE(原子核のエネルギー準位やリーマンゼータ零点の研究で使われる乱数行列理論)に従うかをKS検定で比べるやつ。とりあえず動かそ」


実行1:何も考えずに動かしたら「非ランダムっぽい」結果が出た

タクヤpython3 analysis/miniloto_spectral.py 叩きますね」

[データ] サンプルデータ使用(50回分)
         ※精度向上には公式CSVを analysis/miniloto_data.csv に配置してください
[データ] 50回分の抽選データを読み込み

[間隔統計]
  サンプル数    : 200
  平均間隔      : 6.455
  正規化後の平均: 1.000(~1.0 が正常)
  正規化後の分散: 0.098(Poisson理論値=1.0)

[KS検定結果] ─── 小さいほど理論分布に近い ───
  Poisson(完全ランダム)                KS=0.4219  p=0.0000
  GOE(実対称ランダム行列)                 KS=0.2204  p=0.0000
  GUE(複素エルミートランダム行列)             KS=0.1534  p=0.0001 ← 最も近い

[解釈]
  最も適合する分布: GUE(複素エルミートランダム行列)
  → Poissonからの有意な乖離を検出 (p=0.000 < 0.05)
     何らかの非ランダム構造が存在する可能性を示唆

リナ社長「え、GUEが一番適合してるじゃん!GUEって”番号同士が反発し合ってる”構造でしょ?これ当たりやすい番号があるってこと!?」

タクヤ「ちょ、ちょっと待ってください社長。サンプル数200って出てますけど、これ何回分のデータですか?」

リナ社長「……あれ、“50回分”って書いてある。え、うちのCSVって1300回以上あったよね?」


実行2:本物のCSV(1393回分)を読ませたら結論が逆転した

原因はすぐわかった。fetch_official_data() はローカルに analysis/miniloto_data.csv が無いと、スクリプト内に埋め込まれた50回分のサンプルデータへ黙ってフォールバックする仕様になっている。printで一応警告は出ているが、実行結果だけ見ていると見落とす。

そこでサイトのミニロト解析ページが実際に使っている src/data/loto/miniloto_all_results.csv(みずほ銀行公式データ由来、全1393回分)を analysis/miniloto_data.csv として置き、再実行した。

[データ] ローカルCSV読み込み: analysis/miniloto_data.csv
[データ] 1393回分の抽選データを読み込み

[間隔統計]
  サンプル数    : 5572
  平均間隔      : 5.331
  正規化後の平均: 1.000(~1.0 が正常)
  正規化後の分散: 0.574(Poisson理論値=1.0)

[KS検定結果] ─── 小さいほど理論分布に近い ───
  Poisson(完全ランダム)                KS=0.1710  p=0.0000 ← 最も近い
  GOE(実対称ランダム行列)                 KS=0.2001  p=0.0000
  GUE(複素エルミートランダム行列)             KS=0.2664  p=0.0000

[解釈]
  最も適合する分布: Poisson(完全ランダム)
  → Poissonからの有意な乖離を検出 (p=0.000 < 0.05)
     何らかの非ランダム構造が存在する可能性を示唆

タクヤ「社長、最も適合する分布が……GUEからPoissonに変わってます」

リナ社長「うそでしょ、真逆じゃん。さっきまで”GUE的な反発構造がある”って盛り上がってたのに、データ量を28倍にしたら”やっぱりランダムです”側に寄るって、どういうこと?」


なぜ逆転したのか——サンプル数と「見た目のp値」の罠

ここで一度、2人の見立てが分かれた。

リナ社長「でもさ、p値はどっちの実行でも0.05切ってるじゃん。これって”非ランダムな証拠”がずっとあるってことじゃないの?ビジネス的に言うと、p値だけ見て”有意です”って報告資料に書いたら普通に恥かくやつじゃん」

タクヤ「そこなんですよね……。僕は逆に、KS統計量そのものの値を見るべきだと思います。実行1はGUEのKS=0.1534が一番小さくて、実行2はPoissonのKS=0.1710が一番小さい。p値はどちらも小さく出続けてますけど、“どの理論分布に一番近いか”という相対評価は明確に変わってるので」

リナ社長「え、p値じゃなくてKS統計量の大小を見るのが正解ってこと?」

タクヤ「はい。理由はこのスクリプトの前提にあります。unfold_spacings() で間隔を平均1に正規化して、Poisson/GOE/GUEという連続分布のCDFに対して stats.kstest を当てています。でもミニロトの当選番号はそもそも1〜31の離散整数です。離散データを連続分布のKS検定にかけると、サンプル数が増えるほど、離散化による微小なズレまで統計的に”有意”と判定されやすくなります。サンプル数200でも5572でもp値がほぼ0固定なのは、乱数行列的な強い構造があるからというより、この離散×連続のミスマッチが積み上がっているだけの可能性が高いです」

リナ社長「なるほど……p値は”ズレがあるかないか”の話で、KS統計量は”どのくらい近いか”の話。だからp値が両方とも小さくても、KS統計量を見ればサンプルデータ→実データで”非ランダムっぽい”から”ランダムに近い”へ変わったのが本質ってこと」

タクヤ「そうです。p=0.000という表示だけを見て『非ランダム構造がある』と結論づけるのは早計で、実際にはデータ量を28倍にしたことで、より真実に近い”ランダムに近い”という評価に近づいたと読むのが妥当だと思います」


判断基準:p値だけで統計スクリプトの結果を信じない

今回の一件で、統計スクリプトの出力を読むときの基準を整理した。

確認項目実行1(サンプル50回)実行2(実データ1393回)見るべきポイント
サンプル数2005572まず出力先頭のデータ件数を確認。フォールバック表示を見逃さない
最も適合する分布GUEPoissonKS統計量の大小で比較する(p値だけで見ない)
Poissonとのp値p=0.0000p=0.0000表示上どちらも同じ「有意」にしか見えない罠
データの型離散整数(1〜31)離散整数(1〜31)連続分布向けのKS検定を離散データに使っている前提を忘れない
結論の扱い参考記録として保留GUEよりPoissonに近いサンプル数が変われば結論が変わりうることを前提に読む

リナ社長「これ表にしてくれると分かりやすい。うちのブログでもp値だけドヤ顔で貼るの、今後やめよ」

タクヤ「そうですね。KS統計量そのものと、サンプル数・データの型もセットで見る、というのを社内ルールにしたいです」


suggest_numbers() には触れない方がいいものだった

スクリプトには診断結果に基づいて番号を提案する suggest_numbers() という関数も入っている。ただし関数自体のコメントに「これは予測ではなく診断結果に基づく偏り補正の試み」「このモデルの予測精度は実証されていません」とはっきり書かれている。実行1・実行2どちらのログでも、この関数まわりの出力は本編の解釈には使っていない。KS統計量の解釈が実行1→実行2で反転した以上、番号提案の中身を紹介する意味はなく、今回はあえて割愛した。


まとめ

タクヤ「今回の教訓、2つありますね。1つはフォールバック設計。CSVが無いときに黙って(一応printはあるけど)50回分のサンプルに差し替わる仕様は、気づかずに結果だけ見ると誤った結論のまま話を進めてしまう危険があります」

リナ社長「もう1つはp値だけで判断しないこと。今回みたいに離散データを連続分布のKS検定にかけると、サンプル数が増えるほどp値は機械的に小さくなりやすい。だからp値の”小さい・大きい”じゃなくて、KS統計量そのものの大小で”どの分布に近いか”を比べるのが正しい読み方」

タクヤ「サンプル50回では”GUE的な非ランダム構造がありそう”に見えたものが、実データ1393回では”Poissonに近い”に変わった。これはデータ量が増えて、より実態に近い評価になったと考えるのが自然です」

なお、ミニロトは確率的事象であり、この記事の内容や今回のスクリプトの出力は当選を保証するものではありません。むしろ今回の検証は「非ランダムな当たりやすい番号がある」という見方を裏付けるどころか、データを増やすほどその見方が薄まる方向に働いた、という話として読んでほしい。

持ち帰り: 統計スクリプトの出力を見るときは、(1)サンプル数がフォールバック値になっていないか、(2)p値ではなくKS統計量など「近さ」の指標そのものを比較しているか、(3)連続分布の検定を離散データに使っていないか、の3点を確認する。

みんなが日常で目にする統計データ、そのサンプル数がどこかの初期値やデモ値のままになっていないか、一度確認したことはありますか。


参考

コメント