Dla początkujących · Deska Galtona

Lekcja 3: Losowość i liczenie

Zbuduj w Pythonie wzór „Deska Galtona” i po drodze naucz się:

  • liczby losowe z np.random.default_rng
  • True i False liczone jako 1 i 0
  • liczenie przez np.bincount
  • wyrażenie listowe
  • symulacja sprawdzona wzorem

Najpierw obejrzyj

Film gra z YouTube po kliknięciu.

galton-board.py
"""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

Nie masz Pythona na tym komputerze? Notatnik uruchamia ten sam skrypt, zadania i odpowiedzi w przeglądarce, w Google Colab. Notatnik jest po angielsku.

Linijka po linijce

  1. rng = np.random.default_rng()

    Generator liczb losowych numpy. Daj mu liczbę, default_rng(1), a każde uruchomienie da te same „losowe” liczby.

  2. bins = (rng.random((balls, rows)) < p).sum(axis=1)

    Tablica 5000 × 12 liczb losowych od 0 do 1. Każda mniejsza od p to True, czyli odbicie w prawo; suma wiersza liczy jego wartości True, a to jest przegródka kulki.

  3. counts = np.bincount(bins, minlength=rows + 1)

    Ile kulek wpadło do każdej przegródki od 0 do 12; minlength pilnuje, żeby puste przegródki na brzegach liczyły się jako 0.

  4. expected = [balls * comb(rows, i) * p**i * (1 - p) ** (rows - i) for i in k]

    Wyrażenie listowe: wzór dwumianowy dla każdej przegródki i, razy liczba kulek.

  5. plt.bar(k, counts, color="#5eead4", label="balls")

    Jeden słupek na przegródkę. Biała linia narysowana potem to wzór, do porównania.

Twoja kolej

  1. Uruchom skrypt dwa razy. Czy słupki są takie same? Potem spraw, żeby każde uruchomienie było takie samo.

    Podpowiedź

    Daj default_rng liczbę.

    Odpowiedź

    Słupki za każdym razem trochę się różnią. rng = np.random.default_rng(1) daje za każdym razem ten sam przebieg.

  2. Ile z 5000 kulek powinno wpaść do środkowej przegródki przy 12 rzędach?

    Podpowiedź

    Udział środkowej przegródki to comb(12, 6) / 2**12.

    Odpowiedź

    5000 × 924 ÷ 4096 ≈ 1128.

  3. Przechyl deskę: p = 0.65. Gdzie jest teraz środek dzwonu?

    Podpowiedź

    Środek to rows · p.

    Odpowiedź

    12 × 0,65 = 7,8, więc szczyt przesuwa się do przegródek 7 i 8.

Quiz

1. Co zawiera rng.random((5000, 12)) < 0.5?
2. Dlaczego .sum(axis=1) daje przegródkę kulki?
3. Ile wynosi np.bincount([0, 2, 2, 3])?
4. Co zmienia default_rng(1)?
5. Która część wzoru dwumianowego liczy drogi do przegródki k?

Fiszki

Przeczytaj przód, powiedz sobie, co jest z tyłu, potem otwórz kartę.

  • np.random.default_rng(seed)

    Generator liczb losowych; to samo ziarno daje te same liczby.

  • array < 0.5

    Tablica True i False; przy dodawaniu True liczy się jako 1.

  • np.bincount(xs)

    Ile razy każda liczba całkowita 0, 1, 2, … występuje w xs.

  • [f(i) for i in k]

    Wyrażenie listowe: nowa lista z f zastosowaną do każdego elementu k.

  • comb(n, k)

    Liczba sposobów wyboru k rzeczy z n: math.comb(12, 6) to 924.

Te fiszki na osobnej stronie →