最近,YouTubeチャンネル「予備校のノリで学ぶ「大学の数学・物理」」様において
``ビンゴせずに穴は何個まで開けられるか''
という話題で数学的な解析も含めて解説されていました.
元動画は以下のものです.
本記事では計算機シミュレーションを通して数学に加えてプログラミングの理解を深めていきます.
目標としては,Pythonを用いて抽選回数とビンゴの確率分布を得ることを目標とします.
それでは実装を行っていきます.
パッケージインポート
数値計算パッケージとしてnumpy,グラフ描写のためにmatplotlibをインポートします.
import numpy as np
import matplotlib.pyplot as plt
ビンゴの判定関数
ビンゴカードを$5 \times 5$の配列を使って表現し,穴が開いている部分は$-1$とします.
このように実装することでビンゴの判定が多少簡単になります.
穴が開いている部分を$-1$としたため,縦横斜めいずれかの和が$-5$となればビンゴです.
def checkBingo(card):
for i in range(5):
# 横列のチェック.
if np.sum(card[i]) == -5:
return True
# 縦列のチェック
if np.sum(card[:,i]) == -5:
return True
# 斜めのチェック
if (card[0][0] + card[1][1] + card[2][2] + card[3][3] + card[4][4]) == -5:
return True
if (card[0][4] + card[1][3] + card[2][2] + card[3][1] + card[4][0]) == -5:
return True
return False
ビンゴカード生成
ビンゴカードは$0 \sim 74$の数字と仮定しています.動画では$1 \sim 75$としていますが,Pythonの$0$番目スタートの仕様に従っています.
注意点としてはrandint関数のような一様乱数を用いてビンゴカードを生成した場合は同じ値を出力する可能性があり,ビンゴカード$1$枚の中で同じ値は存在しないという性質を満たせない可能性があります.
重複しない乱数を実現するために,$0 \sim 74$の連番整数を生成してそれをシャッフルして取り出すことで実装を行います.
生成後の後処理としてビンゴカードの真ん中に$-1$を代入して穴を開ける判定にしておきます.
numberOfcards = 10000
card = np.arange(75)
cards = np.zeros((numberOfcards,5,5))
for i in range(numberOfcards):
np.random.default_rng().shuffle(card)
cards[i] = card[:25].reshape(5,5)
cards[i][2][2] = -1
抽選と結果
抽選番号を生成して番号ごとにビンゴカードに穴を開けていきます.
ビンゴしているカードの枚数と抽選回数を記録していきます.
この結果では抽選回数とビンゴしているカードの枚数の累積分布のようなものが得られます.
lot = np.arange(75)
np.random.default_rng().shuffle(lot)
result = np.zeros(len(lot))
for i in range(len(lot)):
for j in range(numberOfcards):
np.place(cards[j],cards[j] == lot[i],-1)
if checkBingo(cards[j]) == True:
result[i] += 1
print(result)
結果の描写
最後にmatplotlibを使ってデータの描画を行います.
今回は$10000$枚のビンゴカードを使ってシミュレーションを行いました.
データ数が足りないので,増やすと滑らかなグラフになることが期待できます.
data = np.zeros(len(lot))
for i in range(1,len(data)):
data[i] = result[i] - result[i-1]
print(data)
plt.bar(np.arange(len(data)), data, width=1.0)
まとめ
YouTubeチャンネル「予備校のノリで学ぶ「大学の数学・物理」」様の動画を基に計算機シミュレーションを使って,理解を深めていきました.
数学と密接に関連がある計算機の世界を少しでも知っていただければ幸いです.
ソースコードまとめ
import numpy as np
import matplotlib.pyplot as plt
def checkBingo(card):
for i in range(5):
# 横列のチェック.
if np.sum(card[i]) == -5:
return True
# 縦列のチェック
if np.sum(card[:,i]) == -5:
return True
# 斜めのチェック
if (card[0][0] + card[1][1] + card[2][2] + card[3][3] + card[4][4]) == -5:
return True
if (card[0][4] + card[1][3] + card[2][2] + card[3][1] + card[4][0]) == -5:
return True
return False
# ビンゴカードの生成 (10000枚)
numberOfcards = 10000
card = np.arange(75)
cards = np.zeros((numberOfcards,5,5))
for i in range(numberOfcards):
np.random.default_rng().shuffle(card)
cards[i] = card[:25].reshape(5,5)
cards[i][2][2] = -1
lot = np.arange(75)
np.random.default_rng().shuffle(lot)
result = np.zeros(len(lot))
for i in range(len(lot)):
for j in range(numberOfcards):
np.place(cards[j],cards[j] == lot[i],-1)
if checkBingo(cards[j]) == True:
result[i] += 1
print(result)
data = np.zeros(len(lot))
for i in range(1,len(data)):
data[i] = result[i] - result[i-1]
print(data)
plt.bar(np.arange(len(data)), data, width=1.0)