Python
数学
確率

サイコロを1分で1億回振ってみる


はじめに

「サイコロって本当に同じ確率で目が出るの?」

これを現実で試すにはとてつもない時間と忍耐が必要です。でもプログラミングならあっという間にやってくれます。

今回この記事を書くきっかけとなったのは、私の大学の先生がとある講義でRを用いてサイコロを一億回振るということをやっており、それを真似してみたかったからです。私はRはよくわからないのでPythonで実装します。またJupyter Notebook上で実装します。

※PCのスペックによっては1分で1億回振れないかもしれません。私のデスクトップとノートPCを比較すると

・デスクトップ:core i5-4590 RAM:16GB  40秒程

・ノートPC:core i3-7020 RAM:8GB  50~60秒


とりあえずそれっぽいコード

「サイコロを1億回振るんだから、for文で一億回サイコロの出目を発生させればええやろ」

そう思ってコードを書き始めました。しかしこのコードは処理に5分かかる上、ダサいコードです

とりあえずそのコード・・・


ver1

%%time
import random
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

count_1 = count_2 = count_3 = count_4 = count_5 = count_6 = 0

n = 100000000

for i in range(n):
A = random.randint(1, 6)
if A == 1:
count_1 += 1
elif A == 2:
count_2 += 1
elif A == 3:
count_3 += 1
elif A == 4:
count_4 += 1
elif A == 5:
count_5 += 1
elif A == 6:
count_6 += 1
left = np.array([1,2,3,4,5,6])
height = np.array([count_1, count_2, count_3, count_4, count_5, count_6])
print(height/n)
plt.xlabel("出目")
plt.bar(left, height)


まさに、for文で1億回まわし目の出た回数を数えるという安直なコードです。「pythonのfor文はおそい」と様々なところで見聞きしているにも関わらずこの所業。

そこで、先生にRのコードを見せてもらいアドバイスをもらいました。その時先生からひと言「RでもPythonでも他の言語でも、イカしたコードの書き方と、ダサいコードの書き方があるんだ」と言われました。実際、先生のコードは、sampleという関数を用いて出目を1億個発生させ、そのうちどの目が何回出現したかを数えるものでした。僕の最初のコードはfor文を1億回実行しているのに対し、先生のコードは6回実行しているだけです。(プログラミング内の処理についてちゃんと理解してできていないので語弊があるかもしれません)


改良されたコード

かっこいいコードを書いてやろうということで以下のようにコードを書き直しました。


ver2


%%time
import random
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

i = [1,2,3,4,5,6]
k=100000000
r = random.choices(i, k=k)

count_1 = r.count(1)
count_2 = r.count(2)
count_3 = r.count(3)
count_4 = r.count(4)
count_5 = r.count(5)
count_6 = r.count(6)

left = np.array([1,2,3,4,5,6])
height = np.array([count_1, count_2, count_3, count_4, count_5, count_6])
print(height/k)
plt.xlabel("出目")
plt.bar(left, height)


このコードだと1分でサイコロが1億回ふることができます。まだ野暮ったいところもありますがこれが私の限界です。count_1~6のところをfor文で書いてみたのですが、count_1~6が宣言されてないと怒られるのでどう対処すればいいのか・・・

上記のコードの結果(ノートPCで実行)

alt

それぞれの目の出た確率はほぼ0.166・・・に近づいており1/6の確率に近いことが分かります。

また処理時間1分2秒とほぼ1分でサイコロを1億回振ることが出来ました。


コードの軽い解説

最後にコードの説明を軽くします。

%%time

Jupyter Notebook上でプログラミングの処理時間を計測するコードです。

i = [1,2,3,4,5,6]

k=100000000
r = random.choices(i, k=k)

random.choices(i, k=k) についてはある範囲(i)からk個の要素をランダムにとってきてもらうというものです。その前にk=100000000と宣言してありますが、これは特にする必要はなく、random.choices(i, k=100000000)とすればいいです。今回は、後で出目の確率を求めるために一億という数字を使いまわしたいためあらかじめ宣言しました。

count_1 = r.count(1)

countという関数を用いてrというリストに格納されている出目を数えています。

残りのコードは棒グラフを表示させるだけですのであってもなくてもいいです。

以上サイコロを1分で1億回振ってみるでした。

もしアドバイス・質問等ありましたら気軽にコメントの方よろしくお願いします。

参考サイト

pip install して import するだけで matplotlib を日本語表示対応させる

PythonのCounterでリストの各要素の出現個数をカウント

Jupyter Notebookでセルの実行時間をはかるなら%%timeを使おうって話

Pythonでリストからランダムに要素を選択するchoice, sample, choices

matplotlib で棒グラフを描く