<jupyter_start><jupyter_text>SimulatieOver kansen en kiezen!  RecursieJij zorgt voor het EERSTE geval, recursie zorgt voor de REST Een korte herhaling van recursie en één van de belangrijkste lessen als het gaan om het ontwerp van recursieve functies: naast het bepalen van de base case(s) zorg jij voor het eerste geval en recursie zorgt voor de rest. Laten we dit nog een keer bekijken.  Inwendig productOf in het Engels: *dot product* 1. `[1, 2] * [3, 4]`2. `1 * 3 + 2 * 4`3. `3 + 8`4. `11` Het inwendig product is het resultaat van de vermenigvuldiging van twee *vectoren* van gelijke grootte. Vectoren kunnen worden gerepresenteerd als lists. ```pythondef dot(L, K):    """Het inwendig (dot) product van L en K    """    if len(L) == 0 or len(K) == 0:        return 0.0        elif len(L) != len(K):        return 0.0    else:        return L[0] * K[0] + dot(L[1:], K[1:])``` Er zijn *twee* base cases, de lijsten `L` en `K` zijn leeg of ze zijn niet van gelijke lengte. Zie je ook dat we tests kunnen combineren met `or` (als de een óf de ander faalt)? Recursie zorgt voor de REST```pythonelse:    return L[0] * K[0] + dot(L[1:], K[1:])``` Als de base cases zijn gedefieerd dan zorg jij voor het *eerste* geval `L[0] * K[0]` en recursie zorgt voor de *rest* `dot(L[1:], K[1:])`. Het is een product, dus jouw geval en de rest moeten wel met elkaar gecombineerd worden (`+`).  VisualiserenEen hulpmiddel voor het visualiseren van de stack: [pythontutor.com](http://www.pythontutor.com/) ![pythontutor.org](images/5/pythontutor_dot.png) Gebruik [pythontutor.com](http://www.pythontutor.com/) voor het verkennen van een probleem of (visueel) testen van jouw oplossing! Het `dot` voorbeeld kan je gelijk [uitproberen](https://pythontutor.com/visualize.htmlcode=def%20dot%28L,%20K%29%3A%0A%20%20%20%20if%20len%28L%29%20%3D%3D%200%20or%20len%28K%29%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20return%200.0%0A%20%20%20%20%0A%20%20%20%20elif%20len%28L%29%20!%3D%20len%28K%29%3A%0A%20%20%20%20%20%20%20%20return%200.0%0A%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20return%20L%5B0%5D%20*%20K%5B0%5D%20%2B%20dot%28L%5B1%3A%5D,%20K%5B1%3A%5D%29%0A%0Adot%28%5B3,%202,%204%5D,%20%5B4,%207,%204%5D%29&cumulative=false&heapPrimitives=nevernest&mode=edit&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false).   Toeval en keus```pythonimport random```Na de import kan je `dir(random)` en `help(random)` gebruiken Met deze syntax importeer je de Python *module* random. ```pythonfrom random import *```Alle random functies zijn nu beschikbaar! Met deze syntax importeert je *alle* functies in de module random. ```pythonfrom random import choice```Alleen de functie `choice` is nu beschikbaar ...  Import ```pythonimport randomrandom.choice(...)``` ```pythonfrom random import *choice(...)``` Wat is het verschil en maakt het uit? Nee, zoals je kan zien kan je met beide import statements de functie `choice` gebruiken. In het eerste geval leeft de functie in een zogenaamde *namespace* of context (de namespace "random") en roep je het via die context aan (`random.choice`), in het tweede geval komt het in de context van jouw programma terecht en kan je het direct aanroepen (`choice`). ```pythonfrom random import *def choice(...):    """Mijn choice functie    """``` In dit geval zal jouw functie `choice` worden aanroepen, het "overschrijft" de functie `choice` die je eerder met het import statement uit de module random hebt geïmporteerd. In dit geval kan het handig zijn om alleen de module random te importeren en via `random.choice` de oorspronkelijke functie te kunnen aanroepen (als dat nodig is).  Gebruik```pythonchoice(L)```Kies willekeurig 1 element uit de sequentie L ```pythonchoice("roos")```of 1 karakter uit een string De kans op `"r"` is $\frac{1}{4}$, op `"o"` $\frac{1}{2}$ en op `"s"` $\frac{1}{4}$ ... ```pythonchoice(['slapen', 'eten', 'netflix', 'studeren'])``` of 1 element uit een list  Reeksen makenKan je ook eigen reeksen maken?<jupyter_code>list(range(5))<jupyter_output><empty_output><jupyter_text>Lees dit als "ik wil de waarden van `range(5)` als een list". Let op dat `range(5)`  5 *elementen* oplevert!<jupyter_code>list(range(1,5))<jupyter_output><empty_output><jupyter_text>Dit begint op de list slicing syntax te lijken met een start en stop positie! Zou het ook de step kennen?<jupyter_code>list(range(0, 10, 2))<jupyter_output><empty_output><jupyter_text>Een inclusieve reeksEen willekeurig getal uit de reeks 0 **tot en met** 99?<jupyter_code>from random import choice

choice(list(range(100)))<jupyter_output><empty_output><jupyter_text>Een precieze keus```pythonuniform(low, hi)```Kies een random *float* waarde tussen low en hi<jupyter_code>from random import uniform

uniform(41.9, 42.1)<jupyter_output><empty_output><jupyter_text>Helaas, geen `42.0`. Python `float` waarden hebben 16 decimale posities aan precisie en die worden in dit geval allemaal gebruikt!  Een random functie```pythonfrom random import *def guess(hidden):    """De computer raadt een geheim getal    """    comp_guess = choice(list(range(100)))   [0, ..., 99]        if comp_guess == hidden:   base case, eindelijk...        print("Gevonden!")    else:                      recursive case        guess(hidden)``` De *docstring* verraadt al wat deze functie doet, de computer moet een getal raden dat wij hebben gekozen (een getal tussen 0 en 99), dit is de waarde van de parameter `hidden`. Als je de functie uitvoert zien we niet veel, alleen dat de computer op een gegeven moment het getal heeft gevonden. Laten we een paar aanpassingen maken.  Uitbreidingen- print steeds de keuze?- het aantal pogingen als returnwaarde?- dit gaat snel, kan het vertraagd worden?- het te verwachten aantal pogingen?!?  Recursief pogingen tellen```pythonfrom random import *import timedef guess(hidden):    """De computer raadt een geheim getal    """    comp_guess = choice(list(range(100)))   [0, ..., 99]        print("Ik koos", comp_guess)   print de keus    time.sleep(0.5)                pauzeer een halve seconde        if comp_guess == hidden:       base case, eindelijk...        print("Gevonden!")         de computer is blij :)        return 1                   poging    else:                          recursive case        return 1 + guess(hidden)   volgende poging!``` Je ziet hier een aantal aanvullingen, bijvoorbeeld het printen van de keus, een halve seconde pauzeren na de keus en het tellen en teruggeven van het aantal keer dat is gekozen. Maar wat valt te zeggen over het te *verwachten* aantal pogingen? Ook dit probleem kan *recursief* worden benaderd!De kans dat de eerste keer het getal wordt geraden is $\frac{1}{100}$ en $\frac{99}{100}$ dat het *niet* wordt geraden (en weer opnieuw moet worden begonnen, maar we hebben al een eerste poging gedaan). Laat $E$ nu het te verwachten aantal pogingen zijn voor het raden van het juiste getal, op basis van de bovenstaande (recursieve) redenatie komen we dan tot een EERSTE geval $1\cdot\frac{1}{100}$ en een REST $(E + 1)\cdot\frac{99}{100}$ ($E + 1$ omdat de recursieve aanroep moet weten dat het EERSTE geval, een *eerste* poging, al is gedaan voor het verhogen van de telling). We kunnen dit nu schrijven als$$E = 1\cdot\frac{1}{100} + (E + 1)\cdot\frac{99}{100}$$en dit is een vergelijking die kan worden [opgelost](https://mathsolver.microsoft.com/nl/solve-problem/E%20%3D%20%201%20%60cdot%20%20%20%60frac%7B%201%20%20%7D%7B%20100%20%20%7D%20%20%2B%20%60left(%20E%2B1%20%20%60right)%20%20%20%60cdot%20%20%20%60frac%7B%2099%20%20%7D%7B%20100%20%20%7D) tot$$E = 100$$We kunnen dus 100 pogingen *verwachten*, maar wat betekent dit getal nu? In het geval dat we deze funtie een oneindig aantal keer zouden uitvoeren zal het gemiddeld aantal pogingen uitkomen op 100 (het gemiddelde van alle mogelijke uitkomsten).  Quiz```pythonfrom random import *```1. `choice([1,2,3,2])`: wat is de kans op 2?2. `choice(list(range(5)) + [4,2,4,2])`: wat is de kans op 4?3. `choice('1,2,3,4')`: wat is de meest waarschijnlijke returnwaarde?4. `choice(['1,2,3,4'])`: wat is de meest waarschijnlijke returnwaarde?5. `choice('[1,2,3,4]')`: wat is de meest waarschijnlijke returnwaarde?6. `choice(list(range(5)))`: welke kans is groter, een even of oneven getal?7. `uniform(-20.5, 0.5)`: Wat is de kans dat de returnwaarde groter dan 0 is?Syntax: welke is correct?8. `choice(0,1,2,3,4)`9. `choice([list(range(5))])`10. `choice[list(range(5))]`  Oplossing1. $\frac{2}{4} = \frac{1}{2}$2. $\frac{3}{9} = \frac{1}{3}$ (`[0,1,2,3,4] + [4,2,4,2]` is `[0,1,2,3,4,4,2,4,2]`)3. `','` (kans is $\frac{3}{7}$)4. `'1,2,3,4'` (kans is $\frac{1}{1}$)5. `','` (kans is $\frac{3}{9}$)6. even getal (kans is $\frac{3}{5}$)7. $\frac{1}{42}$ (in totaal 42 stappen van 0,5 ($\frac{20,5 + 0,5}{0,5}$) , waarvan één stap groter dan 0)Syntax8. vierkante haken ontbreken voor een list: `choice([0,1,2,3,4])`9. is correct! (en geeft altijd `[0,1,2,3,4]` terug)10. `choice` is een functie, dus ronde haken voor openen en sluiten: `choice(list(range(5)))`  Twee Monte Carlo's ![Casino Monte Carlo](images/5/casino_monte_carlo.jpg) Het casino van Monte Carlo, als je een gokje wilt wagen! ![Casino Monte Carlo](images/5/casino-royale-bond.jpg)Bond, <NAME> (bekend van [Casino Royale](https://nl.wikipedia.org/wiki/Casino_Royale_(2006)) ) ![Dartboard](images/5/500px-Dartboard_unlabeled.svg.png) In het boek wordt een methode beschreven voor een simulatie met dartboard om de waarde van $\pi$ ([pi](https://nl.wikipedia.org/wiki/Pi_(wiskunde))) te bepalen. Maar, wat heeft dit te maken met Monte Carlo?!? Als je het boek niet hebt dan geeft de volgende video een goede uitleg. Misschien is dit iets om zelf uit te proberen! ![<NAME>](images/5/stan_ulam.jpg)Ulam, <NAME> (bekend van [Monte Carlo simulaties](https://nl.wikipedia.org/wiki/Monte-Carlosimulatie)) Bij het spelen van solitaire tijdens zijn herstel na een operatie had <NAME> gedacht aan het spelen van honderden spelletjes om de kans op een succesvolle uitkomst statistisch in te schatten. Ulam was betrokken bij [ENIAC](https://en.wikipedia.org/wiki/ENIAC), de eerste elektronische computer voor algemeen gebruik en collega [<NAME>](https://nl.wikipedia.org/wiki/John_von_Neumann) stelde in maart 1947 voor een vergelijkbaar probleem op ENIAC uit te werken.Waarom een "Monte Carlo" simulatie? Het is vernoemd naar [Monte Carlo](https://nl.wikipedia.org/wiki/Monte_Carlo_(Monaco)), waar je in het casino kan aanschuiven om verschillende gokspellen te spelen. De kans om te winnen kan je berekenen, maar ook simuleren met `random`izatie!.  Monte Carlo in actie  Dobbelen![Dubbel 6](images/5/dice-6.png)Hoeveel dubbel paar ogen gooi je als je N keer werpt?<jupyter_code>from random import *

def count_doubles(N):
    """Tel aantal dubbele ogen bij N worpen
    """
    if N == 0:    # base case
        return 0  # 0 worpen, 0 dubbele ogen...

    d1 = choice([1,2,3,4,5,6])     # eerste dobbelsteen
    d2 = choice(list(range(1,7)))  # tweede dobbelsteen

    if d1 != d2:
        return 0 + count_doubles(N - 1)  # niet gelijk
    else:
        return 1 + count_doubles(N - 1)  # gelijk! tel 1 op

count_doubles(600)<jupyter_output><empty_output><jupyter_text>Let op waar de dobbelstenen in de code worden geworpen, kan je zien hoe de worp voor beide wordt gesimuleerd? `choice([1,2,3,4,5,6])` en `choice(list(range(1,7)))` geven natuurlijk hetzelfde resultaat, de computer zal het meest blij zijn met de eerste variant omdat het efficënter is. In het eerste geval is de list als argument voor `choice` al *gedefinieerd*, in het tweede geval moet het steeds weer worden *gegenereerd* (aangemaakt).Bij elke worp zijn $6 \times 6 = 36$ mogelijke combinaties van ogen mogelijk. 6 van deze mogelijkheden zijn gelijke paren. De kans op een gelijk paar ogen is dus $\frac{6}{36}$ wat gelijk is aan $\frac{1}{6}$ en deze simulatie komt daar héél dichtbij!  Monte Carlo Monty HallHet [driedeurenprobleem](https://nl.wikipedia.org/wiki/Driedeurenprobleem) ![Let's make a deal](images/5/monty_hall.jpg) In de jaren 60 van de vorige eeuw kon in de spelshow "Let's Make a Deal", gepresenteerd door [Monty Hall](https://nl.wikipedia.org/wiki/Monty_Hall), een auto worden gewonnen door één van de drie deuren te kiezen (achter de andere twee deuren stond een geit!).  > Stel dat je deelneemt aan een spelprogramma en je mag kiezen uit drie deuren: achter een van de deuren staat een auto, achter de andere twee staan geiten. Je kiest een deur, zeg nr. 1, en de presentator, die weet wat er achter de deuren staat, opent een andere deur, zeg nr. 3, met een geit erachter. Hij zegt dan tegen je: "Zou je deur nr. 2 willen kiezen?" Is het in je voordeel om van deur te wisselen? Wat zou het meest succes (auto!) opleveren, bij de eerste keus blijven of van deur wisselen? Dit dilemma is bekend geworden als het "Monty Hall"- of driedeurenprobleem. Het blijkt dat de beste kans op winnen het wisselen van deur is! ![Spam and PMFP](images/5/spam_pmfp.png) In plaats van een auto wordt onze prijs [SPAM](https://nl.wikipedia.org/wiki/Spam_(vlees)), ook wel bekend van jouw [mailbox](https://nl.wikipedia.org/wiki/Spam_(post)). SPAM is ... een ingeblikt gekookt vleesproduct en een anonieme variant (Potted Meat Food Product, of misschien [PMFP](https://www.urbandictionary.com/define.php?term=PMFP)) wordt de geit.<jupyter_code>from random import *

def MCMH(init, sors, N):
    """Speel Let's Make a Deal N keer
    
    Geeft het aantal keer dat *Spam!* wordt gewonnen
    """
    if N == 0:  # base case
        return 0

    prz_door = choice([1,2,3])  # de deur met de prijs!

    if init == prz_door: 
        if sors == 'stay':
            result = 'Spam!'
        else: 
            result = 'pmfp.'
    else: 
        if sors == 'switch':
            result = 'Spam!'
        else: 
            result = 'pmfp.'      

    if result == 'Spam!':  
        return 1 + MCMH(init, sors, N-1)
    else:
        return 0 + MCMH(init, sors, N-1)

print('Bij blijven winnen we', MCMH(1, 'stay', 600), 'keer')
print('Bij wisselen winnen we', MCMH(1, 'switch', 600), 'keer')<jupyter_output>Bij blijven winnen we 187 keer
Bij wisselen winnen we 404 keer
{'fraction_non_alphanumeric': 0.12442526702978, 'fraction_numerical': 0.04604937398316474, 'mean_word_length': 4.531298904538341, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 34, 'https://': 13, 'lorem ipsum': 0, 'www.': 3, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '7', 'dir': 'jupyter-structured-clean-dedup', 'id': '256566', 'license': 'MIT', 'n_tokens_mistral': 5549, 'n_tokens_neox': 5227, 'n_words': 1715, 'path': 'topics/5a_simulatie.ipynb', 'repo_name': 'misja/programmeren', 'size': '14165'}