レッスン3:乱数と数えあげ
Pythonで 「ガルトンボード」 を作りながら、次のことを学びます:
- np.random.default_rng による乱数
- 1と0として数えられる True と False
- np.bincount で数える
- リスト内包表記
- 公式と照らし合わせるシミュレーション
まず見てみる
クリックするとYouTubeから再生されます。
"""Galton board: every ball goes left or right at random at each of 12 rows of pegs.
Run: python galton-board.py (needs: pip install numpy matplotlib)
"""
from math import comb
import numpy as np
import matplotlib.pyplot as plt
plt.style.use("dark_background")
rows, balls, p = 12, 5000, 0.5 # p: the chance of bouncing right
rng = np.random.default_rng()
bins = (rng.random((balls, rows)) < p).sum(axis=1) # a ball's bin = how often it went right
counts = np.bincount(bins, minlength=rows + 1)
k = np.arange(rows + 1)
expected = [balls * comb(rows, i) * p**i * (1 - p) ** (rows - i) for i in k] # the binomial curve
plt.figure(figsize=(7, 4))
plt.bar(k, counts, color="#5eead4", label="balls")
plt.xlabel("bin = times the ball went right")
plt.plot(k, expected, color="white", label="C(n, k) p^k (1-p)^(n-k)")
plt.legend()
plt.show()
# Try: p = 0.65 tilts the board; rows = 24 makes the bell narrower for its width
このコンピューターにPythonが入っていませんか?ノートブックなら、同じスクリプト、練習問題、答えを、ブラウザ上のGoogle Colabで実行できます。ノートブックは英語です。
1行ずつ
rng = np.random.default_rng()numpy の乱数生成器です。default_rng(1) のように数を渡すと、実行するたびに同じ「でたらめな」数が出ます。
bins = (rng.random((balls, rows)) < p).sum(axis=1)0から1までの乱数を 5,000 × 12 個並べた表です。p より小さいものは True、つまり右への跳ね返りです。1行を足し合わせるとその行の True の数が数えられ、それが玉の入る箱になります。
counts = np.bincount(bins, minlength=rows + 1)0から12までの各箱に、玉がいくつ入ったか。minlength のおかげで、空っぽの端の箱も0として数えられます。
expected = [balls * comb(rows, i) * p**i * (1 - p) ** (rows - i) for i in k]リスト内包表記です。箱 i ごとの二項分布の公式に、玉の数を掛けます。
plt.bar(k, counts, color="#5eead4", label="balls")箱ひとつにつき棒を1本。そのあとに描く白い線が公式で、見比べるためのものです。
やってみよう
スクリプトを2回実行してみましょう。棒は同じですか?次に、毎回同じ結果になるようにしてみましょう。
ヒント
default_rng に数を渡します。
答え
棒は毎回少しずつ違います。rng = np.random.default_rng(1) にすると、毎回同じ結果になります。
12段で、玉5,000個のうちいくつが真ん中の箱に入るはずですか?
ヒント
真ん中の箱の割合は comb(12, 6) / 2**12 です。
答え
5000 × 924 ÷ 4096 ≈ 1128。
p = 0.65 でボードを傾けてみましょう。釣鐘の中心は今どこにありますか?
ヒント
中心は rows · p です。
答え
12 × 0.65 = 7.8 なので、山は箱7と箱8へ移ります。
クイズ
フラッシュカード
表を読み、裏に何があるかを自分で言ってみてから、カードを開いてください。
np.random.default_rng(seed)
乱数生成器。同じシードなら同じ数が出ます。
array < 0.5
True と False の配列。足し算では True が1として数えられます。
np.bincount(xs)
0、1、2、… の各整数が xs に何回現れるか。
[f(i) for i in k]
リスト内包表記。k のすべての要素に f を適用した新しいリスト。
comb(n, k)
n個からk個を選ぶ方法の数。math.comb(12, 6) は924です。