性懲りもなくPythonでLOTO6に挑む

## まえがき

えぇ、もう既に分析され尽くして特にこれと言ってめぼしい結論が出てないことは知ってます。それでもPythonの勉強がてらちょっとやってみようかなと。

## 開発環境

- Python : 3.8.3
- BeautifulSoup : 4.9.2
- resuests : 2.24.0
- Visual Studio Code : 1.49.3

## 過去データ取得

手動コピペなどやってられないのでスクレイピングしてくることにしました。本日(20/10/09)時点の最後のデータは第1524回です。

尚、LOTO6では本数字6コとボーナス数字1コが抽選されますが、今回は本数字のみで勝負します。抽選される数字は1～43で重複することはありません。

```Python:scloto6.py
import requests
from bs4 import BeautifulSoup

r = requests.get('http://hogehoge.com/loto6/data/list1/')
soup = BeautifulSoup(r.content, "html.parser")
numbers = soup.find_all('td', class_='w4')

i = 1
with open('index.txt','w') as f:
    for number in numbers:
        s = number.text.replace('\t', '')
        s = s.replace('\n', '')

        if len(s) == 1:
            s = '0' + s
        if (i % 6) != 0:
            s = s + '\t'
        else:
            s = s + '\n'
        f.write(s)
        i += 1
```

こんな感じのTAB区切りのテキストファイルが生成されます。

```Console:index.txt
02 08 10 13 27 30
01 09 16 20 21 43
01 05 15 31 36 38
16 18 26 27 34 40
09 15 21 23 27 28
```

これを入力ファイルとして次のような処理を行います。

- 各回の合計からσを求める
- 各数字(1..43)の出現回数をカウントし、降順ソートする
- 5口買うことにする
- 1口分6コの数字のうち2コは出現回数上位10コからピックアップする
- 残りの数字4コは出現回数下位10コを除いた11..33の中からランダムにピックアップする
- 6コ選択した数字の総和が±σの範囲から外れていたらやり直し

第1524回までの各回の合計の平均は131.94、σは28.37なので-σ～σは103.57～160.31の範囲になります。

| | データ数 | 割合 |
|:-----------:|------------:|------------:|
|-σ～σ   |1,042 | 68.37%|
|-2σ～2σ |407  | 95.08%|
|-3σ～3σ |73   |  99.87%|

つまり6コの本数字の合計値が104～160の間に入る確率は約2/3ということです。[1, 2, 3, 4, 5, 6]などの抽選結果は合計21で3σからも外れるのでほぼ無視して良い(≒もしそうなった時は諦めもつく)ということ。

```Python:pyloto6.py
import math
import random
import numpy as np

appearance_count = {}       # 各数字の出現回数

for i in range(44):
    appearance_count[i] = 0

sums = []       # 各回合計値の配列

with open('C:\\Python\\scloto6\\index.txt', 'r') as f:
    lines = f.readlines()
    for line in lines:
        array = line.split('\t')
        array_n = list(map(int, array))     # arrayはstr配列なのでint配列に変換
        sums.append(sum(array_n))

        for i in array_n:
           appearance_count[i] = appearance_count[i] + 1

# 出現回数で降順ソート
sorted_appearance_count = sorted(appearance_count.items(), key=lambda x:x[1], reverse=True)

avg = sum(sums) / len(sums)
print(f"AVG:{avg}")

sigma = np.std(sums)        # sumsの標準偏差
sigmalower = avg - sigma
sigmaupper = avg + sigma
print(f"σ:{sigma}({sigmalower}～{sigmaupper})")

for index in [0, 2, 4, 6, 8]:
    while True:
        a = []

        a.append(sorted_appearance_count[index][0])
        a.append(sorted_appearance_count[index + 1][0])

        # 残りの4コはランダム(つまりピックアップの根拠なし)
        while len(a) < 6:
            no = random.randint(10, 32)
            value = sorted_appearance_count[no][0]
            if not value in a:
                a.append(value)

        asum = sum(a)

        if sigmalower <= asum and asum <= sigmaupper:
            # ピックアップした6コの数字の総和が-σ～σの範囲なら採用
            break

    print(f"{asum}{a}")
```

出力はこんな感じになります。

```Console:実行結果
AVG:131.93635170603673
σ:28.37024181798044(103.56610988805629～160.30659352401716)
123[6, 38, 22, 26, 11, 20]
128[10, 27, 39, 21, 26, 5]
108[37, 12, 2, 16, 21, 20]
126[24, 15, 39, 35, 5, 8]
129[19, 43, 25, 3, 23, 16]
```

## あとがき

1口200円ですから5口で1000円とキリが良いので5口にしていますがどこかでLOTO6は3口がベストとか読んだ気もします(根拠は忘れた・・・)

1524回分の出現数字を見える化しても何の傾向も見えてこないので物理機械抽選方式は攻略法が無いのかもしれません。もしあったらもう丸20年も行われているので既に先人が発見しているかと思われます。抽選日は毎週月・木の２回ですよ！