Extra

Deze pagina bevat uitvoerbare code.

Pi met Pijltjes

Het is misschien verbazingwekkend dat het mogelijk is om de wiskundige constante π te berekenen zonder dat je andere operaties nodig hebt dan tellen, optellen en vermenigvuldigen. In deze opgave ga je twee functies schrijven om pi (3.14159…) te benaderen door het gooien van pijltjes.

Eén worp

throw_dart gooit één pijltje op het vierkant van -1 tot 1 en kijkt of het binnen de cirkel valt. De worp is willekeurig, dus om er iets over vast te kunnen leggen zetten we eerst random.seed(0). Daarmee ligt de reeks worpen vast en weten we wat eruit komt.

import math
import random


def throw_dart():
    """Geeft terug of een willekeurig geworpen pijltje binnen de cirkel valt."""
    x = random.uniform(-1.0, 1.0)
    y = random.uniform(-1.0, 1.0)

    distance = math.sqrt((x * x) + (y * y))
    if distance <= 1.0:  # raakt de cirkel
        return True

    return False


random.seed(0)
assert throw_dart() is True

for_pi: een vast aantal worpen

for_pi gooit n keer en drukt na elke worp de schatting tot dan toe af. Het aandeel treffers maal 4 benadert π, want de cirkel beslaat π/4 van het vierkant.

Met random.seed(0) raken 8 van de 10 pijltjes, dus komt er 4.0 * 8 / 10 uit. Wil je zien hoe de schatting verder naar 3,14159… kruipt, roep for_pi dan met een groter aantal worpen aan — houd er dan rekening mee dat hij per worp een regel afdrukt.

def for_pi(n):
    """Gooit n pijltjes en geeft de schatting van pi terug."""
    times_hit = 0.0

    for dart in range(1, n + 1):  # range begint bij 0, tel er 1 bij op
        if throw_dart():
            times_hit += 1.0

        print(
            str(times_hit)
            + " raak van de "
            + str(dart)
            + " worpen dus pi is "
            + str((4.0 * times_hit) / dart)
        )

    return (4.0 * times_hit) / n


random.seed(0)
assert for_pi(10) == 4.0 * 8 / 10  # 8 van de 10 worpen raken
1.0 raak van de 1 worpen dus pi is 4.0
2.0 raak van de 2 worpen dus pi is 4.0
3.0 raak van de 3 worpen dus pi is 4.0
4.0 raak van de 4 worpen dus pi is 4.0
5.0 raak van de 5 worpen dus pi is 4.0
6.0 raak van de 6 worpen dus pi is 4.0
7.0 raak van de 7 worpen dus pi is 4.0
8.0 raak van de 8 worpen dus pi is 4.0
8.0 raak van de 9 worpen dus pi is 3.5555555555555554
8.0 raak van de 10 worpen dus pi is 3.2

while_pi: gooien tot de schatting goed genoeg is

Hier ligt het aantal worpen niet vast, dus dit is een onbegrensde lus. Vraag 4 van het lusrecept — wanneer ben je klaar? — beantwoordt de opgave zelf: zodra het absolute verschil tussen de schatting en math.pi kleiner is dan accuracy. Dat is de stopvoorwaarde hieronder.

Vergelijk de schatting dus met math.pi en niet met een getal dat uit accuracy is afgeleid: de schatting kruipt naar π toe, dus alleen dan komt de lus ooit binnen de marge.

Met random.seed(0) zijn er 10 worpen nodig voor accuracy = 0.1. Bij accuracy = 0.001 eindigt de lus voor de seeds 0 tot en met 9 na respectievelijk 4552, 312, 219, 163, 945, 289, 135, 1816, 149 en 191 worpen.

def while_pi(accuracy):
    """Geeft het aantal pijltjes terug dat nodig was om accuracy te halen."""
    times_hit = 0  # 1. verzamelen, startwaarde
    dart = 0

    while True:  # 2. gooien tot het goed genoeg is
        if throw_dart():  # 3. per worp bijhouden
            times_hit += 1
        dart += 1

        guess_pi = (4.0 * times_hit) / dart
        print(
            str(times_hit)
            + " raak van de "
            + str(dart)
            + " worpen dus pi is "
            + str(guess_pi)
        )

        if abs(guess_pi - math.pi) < accuracy:  # 4. klaar
            return dart  # 5. het aantal worpen


random.seed(0)
assert while_pi(0.1) == 10
1 raak van de 1 worpen dus pi is 4.0
2 raak van de 2 worpen dus pi is 4.0
3 raak van de 3 worpen dus pi is 4.0
4 raak van de 4 worpen dus pi is 4.0
5 raak van de 5 worpen dus pi is 4.0
6 raak van de 6 worpen dus pi is 4.0
7 raak van de 7 worpen dus pi is 4.0
8 raak van de 8 worpen dus pi is 4.0
8 raak van de 9 worpen dus pi is 3.5555555555555554
8 raak van de 10 worpen dus pi is 3.2