Extra¶
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