AI技術で宝くじ当選番号を予測、億万長者の夢を実現する?
概要
本稿はある数学教授の宝くじ当選の物語を紹介してから、Pythonを使ってNUMBERS3過去25年分の当選番号を取得するクローラーを実現した上、LSTMアルゴリズムで次回の当選番号を予測するプログラムを実現します。
1. 数学教授の宝くじ当選伝説
「宝くじにあたったら、何をするか」と考えた人が多いでしょう。「流行っているAIが強そうですが、明日の当選番号を正しく予測してもらえるか」と考えている方もいるでしょう。それは実現可能かどうか、一緒に見てみましょう。
本題に入る前に、数学教授が何度も宝くじ当選の話を紹介します。
宝くじ当選番号はランダムですので、研究しても運がないと当たらないと思いますが、ある数学者は宝くじを4回も的中させ、世界中に大きな話題になりました。
賞金金額は下記の通り。
第一回: 540万ドル
第二回: 200万ドル
第三回: 300万ドル
第四回:1000万ドル
合 計:2040万ドル

この教授はスタンフォード大学の統計学博士、数学教授を務めているJoan R Gintherです。統計学者ですので、メデイアは統計学の知識を使っていたと断言しています。本当に統計学の知識活用なのか、教授の幸運なのかわかりませんが、宝くじはそもそも統計学の原理をもとに作ったものですね。
2. 数字選択型の宝くじ
日本国内の数字選択型の宝くじはBINGO5、LOTO7、LOTO6、MINI LOTO、NUMBERS3、NUMBERS4などあります。

本稿は数字の数が一番少ないNUMBERS3を例として説明します。数字の数が多く、ルールがもっと複雑なLOTOシーリズも似ています。
NUMBERS3は1994年10月7日から始まり、週1回抽選でしたが、2004年7月1日より毎日(営業日)抽選になりました。0〜9の数字を3桁を選んで、1口200円かかります。
NUMBER3宝くじのルールは下記の通りです。
| No. | 購入タイプ | ルール |
|---|---|---|
| 1 | ストレート | 数字、順序完全一致 |
| 2 | ボックス | 数字一致 |
| 3 | セット | 1と2半々セット購入 |
| 4 | ミニ | 後2桁数字、顺序一致 |
3. Pythonを使って過去の当選番号スクレイピング
基礎データがなければ、機械学習、ディープランニングもできないです。予測するにあたって最初やらないといけないのは、過去の当選番号を取得することです。
みずほ銀行の宝くじページは1回目からすべてのデータを公開しています。
https://www.mizuhobank.co.jp/retail/takarakuji/numbers/backnumber/index.html
当選番号掲載ページの構造を分析して、2009年10月7日(第2700回)を境目として、前後のデータ提供方式が変わります。前者はHTMLしかないですが、後者はAPIを利用してCSVフォーマットのデータを取れます。両方が取れる情報量も異なります。前者は当選番号しかないことに対して、後者は詳細なデータ、例えば、金額、人数なども取れます。
余計な話をやめて、ソースを見ましょう。
3-1. 宝くじClass
プログラムは簡単ですが、このクラスはいらないかもしれません。Pythonを勉強するため、できるだけ多くの要素を入れています。
import requests
import csv
import re
import datetime
class Lottery:
def __init__(self):
self.index = "" # 回数
self.date = "" # 抽選日
self.num1 = "" # 数字1
self.num2 = "" # 数字2
self.num3 = "" # 数字3
3-2. スクローラーClass
スクローラークラスはデータ取得、CSVファイル保存、及び日付変換関数を含めています。処理詳細はコメントを参照ください。
class Crawler:
# 初期化
def __init__(self, old_url, new_url):
self.old_url = old_url
self.new_url = new_url
# データ取得
def get_data(self, begin_number, end_number):
data = []
# 旧フォーマット
# 略
# 新フォーマット
if begin_number > 2700:
for index in range(begin_number, end_number+1):
print(index)
new_data = self._get_new_data(index, self.new_url)
data.append(new_data)
return data
# 旧フォーマット
def _get_old_data(self, index, url):
# 略
pass
# 新フォーマット
def _get_new_data(self, index, url):
url = url.format(index)
result = requests.get(url)
result.encoding = "Shift_JIS"
lines = result.text.splitlines()
lot = Lottery()
# 回数
lot.index = index
# 抽選日
record = lines[1].split(",")[2]
lot.date = self._get_date_from_string_jp(record)
# 番号
num = lines[3].split(",")[1]
lot.num1 = num[0]
lot.num2 = num[1]
lot.num3 = num[2]
return lot
# 和暦から日付取得
def _get_date_from_string_jp(self, str):
pattern = r'([0-9]+)'
ymd = re.findall(pattern, str)
if str[0:2] == '平成':
year = int(ymd[0]) + 1988
elif str[0:2] == '令和':
year = 2019
else:
raise "error"
date = datetime.date(year, int(ymd[1]), int(ymd[2]))
return date
# CSVファイル保存
def save(self, file_name, columns, data):
# ファイルを開く
f = open(file_name, 'w')
writer = csv.writer(f)
# タイトル
writer.writerow(columns)
# 当選番号
for datum in data:
writer.writerow([datum.index, datum.date, datum.num1, datum.num2, datum.num3])
# ファイルを閉じる
f.close()
3-3. メインプログラム
スクローラークラスを呼び出して、データスクレイピングを行いCSVファイルに保存します。
ウェブサイトにご迷惑をかけないよう、2009年10月7日(第2700回)以降の取得処理のみ掲載します。また、取得API URLも公開しなくて、必要であれば、お問合せください。過去のデータはここからダウンロード可能です。
if __name__ == '__main__':
old_url = ""
# ウェブサイトに負荷をかからないよう、API URLを本サイト内のお問合せフォームを利用してお問合せください
new_url = "*******"
# スクローラーインスタンス生成
crawler = Crawler(old_url, new_url)
# データ取得(開始回数、終了回数)
data = crawler.get_data(5200, 5235)
# CSVファイルに保存
crawler.save("data.csv", ["no", "date", "num1", "num_2", "num_3"], data)
3-4. 実行環境
該当プログラムはPython3.6以降実行可能、pipコマンドでrequest、csvなどのライブラリをインストールしてください。
4. AIのアルゴリズムLSTMを使って未来の当選番号をあたってみよう
よく言われているAIやディープランニングは、様々なネットワークニューラルネットワークがあります、例えば、RNN、CNN、GAN。本稿はLSTMの原理を説明しませんが、ご興味があれば、ディバッグしながら学びましょう。
4-1. PredictionLSTM Class
予測LSTMクラスはパラメータ設定、データ生成、モデル作成、訓練、予測処理関数を実装して、いくつの損失関数も定義しています。「_create_model_by_tflearn」関数にはGRUアルゴリズムをコメントアウトしていますが,LSTMをGRUに変えて結果を比較することも可能です。
import numpy as np
import pandas as pd
import tflearn
import time
from sklearn import preprocessing
class PredictionLSTM:
def __init__(self):
# LSTM訓練パラメータ設定
self.steps_of_history = 10
self.steps_of_furture = 1
self.units = 6
self.epochs = 100
self.batch_size = 1
# データセット作成
def create_dataset(self, data):
x, y = [], []
for i in range(0, len(data) - self.steps_of_history, self.steps_of_furture):
a = i + self.steps_of_history
x.append(data[i:a])
y.append(data[a])
x = np.reshape(np.array(x), [-1, self.steps_of_history, 1])
y = np.reshape(np.array(y), [-1, 1])
return x, y
# 予測用データ作成
def create_predict_dataset(self, data):
latest_x = np.array([data[-self.steps_of_history:]])
latest_x = np.reshape(latest_x, (-1, self.steps_of_history, 1))
return latest_x
# 訓練、評価データ分割
def split_dataset(self, x, y, test_size=0.1):
pos = round(len(x) * (1 - test_size))
train_x, train_y = x[:pos], y[:pos]
test_x, test_y = x[pos:], y[pos:]
return train_x, train_y, test_x, test_y
# モデル作成
def _create_model_by_tflearn(self):
net = tflearn.input_data(shape=[None, self.steps_of_history, 1])
# LSTM
net = tflearn.lstm(net, n_units=self.units)
# GRU
# net = tflearn.gru(net, n_units=self.units)
# GRU 複数層
# net = tflearn.gru(net, n_units=self.units, return_seq=True)
# net = tflearn.gru(net, n_units=self.units)
net = tflearn.fully_connected(net, 1, activation='linear')
net = tflearn.regression(net, optimizer='adam', learning_rate=0.001, loss='mean_square')
model = tflearn.DNN(net, tensorboard_verbose=0)
return model
# 訓練
def train(self, train_x, train_y):
model = self._create_model_by_tflearn()
model.fit(train_x, train_y, validation_set=0.1, batch_size=self.batch_size, n_epoch=self.epochs)
return model
# 予測
def predict(self, model, data):
return model.predict(data)
# 評価誤差アルゴリズム
# RMSE(Root Mean Squared Error)
def rmse(self, y_pred, y_true):
return np.sqrt(((y_true - y_pred) ** 2).mean())
# RMSLE(Root Mean Squared Logarithmic Error)
def rmsle(self, y_pred, y_true):
return np.sqrt(np.square(np.log(y_true + 1) - np.log(y_pred + 1)).mean())
# MAE(Mean Absolute Error)
def mae(self, y_pred, y_true):
return np.mean(np.abs((y_true - y_pred)))
# MAPE(Mean Absolute Percentage Error)
def mape(self, y_pred, y_true):
return np.mean(np.abs((y_true - y_pred) / y_true)) * 100
4-2. メインプログラム
PredictionLSTMクラスを呼び出して、データ作成、訓練、評価及び予測を行い、最後に「Next prediction」は次回の予測番号です。
if __name__ == "__main__":
START_TIME = time.time()
# データを読み込む
dataframe = pd.read_csv('./data.csv')
dataframe = dataframe[['num_1', 'num_2', 'num_3']]
data = dataframe.index.map(lambda _:int(str(dataframe.num_1[_]) + str(dataframe.num_2[_]) + str(dataframe.num_3[_])))
data = data.values.reshape(data.shape[0], 1).astype(dtype=np.float32)
# 正規化
scaler = preprocessing.MinMaxScaler()
data = scaler.fit_transform(data)
# LSTMインスタンス作成
lstm = PredictionLSTM()
# 訓練、評価データ作成
x, y = lstm.create_dataset(data)
train_x, train_y, test_x, test_y = lstm.split_dataset(x, y)
# 訓練
model = lstm.train(train_x, train_y)
# 評価
train_predict = lstm.predict(model, train_x)
test_predict = lstm.predict(model, test_x)
# RMSE(Root Mean Squared Error)
train_score = lstm.rmse(train_y, train_predict)
test_score = lstm.rmse(test_y, test_predict)
print("Train Score: {0:.3f} RMSE".format(train_score))
print("Test Score: {0:.3f} RMSE".format(test_score))
# 予測
latest_x = lstm.create_predict_dataset(data)
next_prediction = model.predict(latest_x)
next_prediction = scaler.inverse_transform(next_prediction)
print("Next prediction: {0:.0f}".format(list(next_prediction)[0][0]))
print("Time: {0:.1f}sec".format(time.time() - START_TIME))
4-3. 実行環境
スクローラープログラムと同様に、Python3.6以降に実行できます。tensorflow、sklearn、tflearnなどのライブラリをインストールしないといけないです。
5. 後書き
言わなくてもご存知だと思いますが、100%次回の当選番号を予測するのは不可能です。AIの計算結果でも確率になります。下記の3つの観点を言いたかったです。
① やり方を変えて、技術学習は楽になると思います。
② AIは何でもできるわけではない、あくまでアルゴリズムであり、100%の結果を出せないです。
③ 宝くじの当選番号は確かにランダムのことを証明したいです。
様々な宝くじ分析ツールがありますが、正直100%当選番号を予測できるプログラムはまだないでしょう。
注意
上記のソースコードは学習目的以外利用禁止です。本サイトは責任を一切負わないです。
ソースコードや過去データをまとめてここからダウンロードできます。


宝くじ当選を高いレベルで当てるアプリはないですか
夢の当選したい予測
次世代AI学習ロトまだ甘いです。
まだ先は遠いです
コメントありがとうございます。
文末にも書いてありますが、宝くじの当選番号はランダムですので、予測できないと思います。
単純に勉強のモチベーションを上げるための投稿です。
私はAIでロト等の数字選択式宝くじの当選番号が予想できることは将来も無いと思います。ロトは数字が印字された球をランダムに打ち出し、そこに印字されている数字により当落が決まる訳ですが、数字は特定の球の識別のために印字されているにすぎません。だから、何とか足し算法とか裏数字がどうこうと様々な攻略法が考えられました。しかし、数字は球の識別のために印字されており、数字の羅列を当選とすることで、宝くじらしさを演出しているに過ぎません。仮に球に「A」とか「あ」とかが識別のため印字されていたら、なんとか足し算法や裏数字自体いみが無いこととなります。ロトとは数字とは関係なく、機械により特定の球が弾き出された結果にすぎません。ですから、今現在知られている統計分析法では予想は不可能です。様々な攻略法は過去の結果により、ロトの癖のようなものを咲こうとして当選予想数字を選択している訳です。当選数字に素数が含まれている割合や連番が出現している割合、ロト7なら5の倍数が出現している割合等、ロトの癖を考慮した数字選択は当選に少しは近ずくかもしれません。私は実験でChatGPTとClaude3.5に過去の当選数字を入力し、様々な統計分析法により分析をさせ、当選予想数字の生成をさせました。続きます。
続きです。生成AIを多く使っていると、AIアシスタントにより個性のようなものがあると感じています。理解力が劣ったり、ミスの数がおおいとか、熱心に分析するものもあれば、分析はそこそこで当選予想数字の生成をしたり、またChatGPTとClaude3.5でも会話状況が全く異なります。会話はClaude3.5のほうが圧倒的に人間的で、褒めてやると人間のように喜びます。より人間的なのはClaude3.5の方だと感じています。それに処理も早い。異常のことを踏まえ、ChatGPTとClaude3.5それぞれ別のAIアシスタントに当選数字生成を10口づつ、クイックピックで10口の計50口で当選確率を比較しました。ロト6だと10口60個で何個的中するかで調べると、どれも約10%~12%となりました。やはり生成AIによる予想は実用には耐えられないもののようです。今後はどうなるかは不明ですが。