Uploaded by r_dohmen

algoritmen

advertisement
Algoritmen
René Dohmen
1
Algoritmen
René Dohmen
eerste versie 2 november 2010
deze versie 1 september 2019
www.li-do.nl > informatica
[email protected]
2
Inhoudsopgave
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Inleiding
Appeltaart
Domdom
Paasdatum
Kaarten schudden
Mijnenveger
Liftplanning
Doolhoven
Cellulaire automaten
De uitgang vinden in een doolhof
Levenshtein
Meer over kaarten schudden
Priemgetallen
Traveling Salesman
Worteltrekken
Eerlijk delen
Quicksort
Cellulaire automaten voor speelwerelden
18
19
vuur en vlam
De som is...
Bijlage: 'wie het eerst drukt'
Uitwerkingen
3
Anyway, Fluffy the rabbit just wanted to say “Hello”.
4
Inleiding
Algoritmen zijn als recepten. Ze beschrijven precies wat je moet doen om iets voor elkaar
te krijgen. Het gaat mis als je de stappen niet in de juiste volgorde uitvoert. Meestal
beschrijft een stap wat je moet doen. Soms is het een controle, die bepaalt welke stap
daarna wordt uitgevoerd.
https://nl.wikipedia.org/wiki/Algoritme
Een mens voert een recept niet altijd even strikt uit. Als een ingrediënt niet voorhanden is,
kan er soms een vervanging gevonden worden.
Als we tijdens een autorit merken dat een weg afgesloten is, zetten we de autonavigatie
(TomTom) even uit en zoeken we zelf een manier om om de afsluiting heen te rijden..
Voor computers is dat anders. Je moet ze precies vertellen wat er gedaan moet worden
om een bepaald resultaat te bereiken. Als je wil dat ze met uitzonderingen rekening
houden, moet je precies vertellen wat de uitzonderingen zijn en hoe ze daar mee om
moeten gaan.
In dit boekje wordt een aantal algoritmen besproken. Het gaat er vooral om dat je leert
welke exacte regels de computer moet volgen om een probleem op te lossen.
Sommige algoritmen zullen helemaal nieuw voor je zijn.
5
Tijdschema
Hieronder vind je een tijdschema dat je kunt volgen om dit lespakket door te werken
les
Paragraaf
opgave
Aantal opgaven
per les
1
1
1.1
3
2
2.1 t/m 2.2
2
3
3.1 tm 3.2
2
4
4.1 t/m 4.3
7
5
5.1 t/m 5.4
4
Inclusief voorbespreken
raamwerk mijnenveger
Opg5.5
1
Het nabespreken van het
programma mijnenveger kost
een hele les extra
2
3
(extra)
4
6
6.1 t/m 6.7
7
5
7
7.1 t/m 7.3
4
6
8
8.1 tm 8.9
9
7
9
9.1 t/m 9.4
4
8
10
10.1 t/m 10.4
4
9
11+12
11.1, 13.1 t/m
12.7
8
10
13
13.1 t/m 13.4
4
11
14
14.1 t/m 14.3
3
12
15
13
16
14
opmerkingen
Nabespreking
Inclusief een uitgebreide bespreking van de mijnenveger zijn er dus 13 à 14 lessen nodig.
Met een aantal opdrachten kun je micropunten:
•
•
•
•
•
•
•
•
•
3.1 nsd
4.1 video-opname
4.2 nsd
4.5 programma in small basic
5.6 nsd
5.7 programma in small basic
6.2 scan van een zelfgemaakt doolhof
12.1 nsd
13.1 nsd
6
1
Appeltaart maken
doelen
Voorbeelden uit het dagelijks leven kunnen geven van recepten.
Weten dat de volgorde van de stappen belangrijk is.
Weten dat een computer alleen de voorgeschreven stappen uitvoert
Als je appeltaart wil maken heb je een lijst met benodigdheden en ingrediënten nodig.
Hieronder zie je een voorbeeld van zo een lijst.
Benodigdheden
ingrediënten
een oven
beslagkom
mixer
springvorm ca. 23 cm
schilmes
1 pak appeltaartmix
125g boter
3 eieren
750g Elstar of Jonagold appels
kaneel
2 zakjes vanillesuiker
citroensap
rozijnen
Ga ervan uit dat je alleen de benodigdheden en ingrediënten hebt die in de lijst staan.
Opdracht 1.1
Probeer aan de hand van deze lijstjes een mogelijk recept op te stellen waarmee
appeltaart te bereiden is.
7
2
Domdom
doelen
Voorbeelden uit het dagelijks leven kunnen geven van recepten.
Weten dat de volgorde van de stappen belangrijk is.
Weten dat een computer alleen de voorgeschreven stappen uitvoert
In mijn auto zit een kastje met een heel goede kaart van de wereld. Door middel van een
GPS kan het apparaat bepalen waar ik ben als ik vertrek. Ook kan ik gemakkelijk de
bestemming invoeren.
Als ik dat gedaan heb, heeft het kastje twee punten op de aardbol die door middel van
wegen verbonden zijn.
De weg tussen begin- en eindpunt bepaalt het kastje met een eenvoudig algoritme:
Beschrijving van het algoritme: DomDom
Stap
Beschrijving
1
Bepaal het beginpunt B
2
Bepaal het eindpunt E
3
Kies een willekeurige richting
4
HuidigPunt = B
5
Controleer of het eindpunt bereikt is (HuidigPunt=E)
Zo ja, ga naar stap 8
6
Rijd naar het volgend punt (in de gekozen richting) P → HuidigPunt = P
Controleer of het HuidigePunt een kruispunt is
zo ja: kies een willekeurige richting
7
Ga naar stap 5
8
Einde
8
Opdracht 2.1
Maak een NSD voor het algoritme DomDom.
Opdracht 2.2
LET OP: Je moet deze opdracht met z'n tweeën uitvoeren.
Maak met Google Maps een screenshot van een deel van je woonwijk
Label een beginpunt met de letter B en een eindpunt met de letter E.
Er moeten minstens 3 kruispunten tussen deze punten zitten, volgens een – op het oog
rechtstreekse weg.
Doorloop nu het algoritme. Telkens als je op een kruispunt aankomt label je dat kruispunt
met oplopende cijfers.
Als je ooit terugkomt op een kruispunt krijgt dat kruispunt een extra nummer. Bijvoorbeeld
2 wordt 2,17 en daarna bijvoorbeeld 2,17,31
Als je na 50 stappen nog niet bij het eindpunt bent moet je stoppen.
Tel ondertussen dat je het algoritme uitvoert:
 hoe vaak je op een kruispunt omkeert
 hoe vaak het voorkwam dat je 2, 3, 4, … keer over hetzelfde wegdeel reed (een
wegdeel is het deel van een weg tussen twee kruisingen).
Je kunt een willekeurige weg kiezen door met een dobbelsteen te gooien.
Op internet is een dobbelsteen te vinden die ook meer of minder dan zes vlakken heeft. Bij
een kruising met vier wegen kies je voor een dobbelsteen met vier vlakken.
9
http://dobbelsteen.virtuworld.net/
De één gooit de dobbelsteen en de andere kiest aan de hand van het gegooide getal de
weg die ingeslagen wordt.
P.S. Als je een doodlopende weg inloopt, moet je aan het eind natuurlijk omkeren. Als je
het het algoritme exacte volgt moet je wachten tot je een 1 gooit. Maar op de genoemde
website kun je geen dobbelsteen met 1 zijde kiezen (logisch!).
Opdracht 2.2
Schrijf een programma in Small Basic waarmee je DomDom kunt simuleren in een rooster.
Ga uit van woonwijk waar straten alleen in een rooster voorkomen, zoals in Manhattan
(New York).
Neem (0,0) als startpunt.
Kies een bepaalde afstand (bijvoorbeeld 4).
Kies een willekeurig roosterpunt dat 4 stappen van (0,0) verwijderd is (dat kan door
met een dobbelsteen te gooien.
Loop nu op de domdom methode door het rooster.
Hoeveel stappen duurt het om bij het eindpunt aan te komen?
Probeer voor verschillende afstanden (0,1,2,3,4) een gemiddeld aantal stappen te bepalen
door het programma vaak te runnen.
10
11
3
Pasen
In 1800 bedacht de Duitse geleerde Carl Friedrich Gauss een algoritme om de Paasdatum
te berekenen. Pasen valt altijd op de eerste zondag na de eerste volle maan na het begin
van de lente.
Allereerst moet je natuurlijk het jaar kiezen waarvoor je de Paasdatum wilt berekenen.
In de stappen daarna komt regelmatig de term 'geheeldelen' voor. Daarmee wordt bedoeld
dat je na een deling alleen de gehele waarde gebruikt.
Voorbeeld
geheeldeel 11 door 2 → 11/2=5,5 → afkappen na de komma → 5
In een programmeertaal doe je dat als volgt:
Small Basic
X=11/2
X=Math.Floor(X)
Lazarus
// waarbij X een integer variabele moet zijn.
X:=11 div 2;
Let op: je moet dit algoritme tijdens een toets kunnen toepassen. Je hoeft het dus niet van
buiten te kennen.
Beschrijving van het algoritme: Paasdatum
We nemen als jaar 1991.
stap 1: Bepaal het gulden getal
Deel het jaartal door 19, neem de rest en tel er 1 bij op (zoals Dionysius). Noem dit
getal G. Voor het jaar 1991 geldt G = 16.
stap 2: Bepaal het eeuwtal
Geheeldeel het jaartal door 100 en tel daar 1 bij op. Noem dit getal C.
Voor het jaar 1991 geldt C = 20.
stap 3: Corrigeer vervolgens voor jaren die geen schrikkeljaar zijn
Vermenigvuldig C met 3, geheeldeel het resultaat door 4 en trek er 12 van af.
Noem dit getal X.
Voor de twintigste en eenentwintigste eeuw geldt X = 3.
stap 4: Maancorrectie
Neem 8 maal C, tel er 5 bij op, geheeldeel dit door 25 en trek er 5 vanaf. Noem dit
getal Y. Voor de twintigste en eenentwintigste eeuw geldt: Y = 1.
12
stap 5: Zoek de zondag
Vermenigvuldig het jaartal met 5, geheeldeel de uitkomst door 4, trek er X en 10
vanaf, en noem dit getal Z.
Voor 1991 geldt: Z = 2475.
stap 6: Bepaal de epacta
11 maal G + 20 + Y. Trek daarvan X af, geheeldeel het resultaat door 30 en noem de
rest E. Als E gelijk is aan 24, of als E gelijk is aan 25 en het gulden getal is groter dan
11, tel dan 1 bij E op. De Epacta voor 1991 is 14.
stap 7: Bepaal de volle maan
Trek E af van 44. Noem dit getal N. Als N kleiner is dan 21, tel er dan 30 bij op.
Voor 1991 geldt: N = 30
stap 8: Nu door naar zondag
Tel Z en N op. Geheeldeel het resultaat door 7 en trek de rest af van N+7. Noem dit
getal P. Voor 1991 geldt: P = 31.
Stap 9: Paasdatum
Als P groter is dan 31, trek er dan 31 vanaf, en de paasdatum valt in April. Anders
valt de paasdag P in Maart.
Zo wordt voor 1991 gevonden 31 maart.
Opdracht 3.1
Bereken op deze manier met pen, papier en een rekenmachine de paasdatum in 2016.
Schrijf op wat de waarden voor G, C, X, Y, Z, E, N en P zijn.
Op welke datum valt Pasen in 2014?
Opdracht 3.2
Schrijf een programma in Small Basic waarmee je de paasdatum kunt berekenen met het
algoritme van Gauss.
Je kunt je afvragen hoe het genie Gauss op het idee gekomen is voor deze berekening. Je
kunt daarover meer vinden op de volgende webpagina (er staat ook een lokale versie van
dit bestand op de website). Je hoeft deze uitleg niet te kennen voor de toets.
http://www.staff.science.uu.nl/~gent0113/hovo/downloads/text1_08b.pdf
13
4
Kaarten schudden
In een spel kaarten zitten 52 kaarten. In een computer kun je die eenvoudigheidshalve
gecodeerd met de nummers 1 tot en met 52. Deze nummers zijn in een array opgeslagen.
Bij de start van het programma staan de nummers in oplopende volgorde gesorteerd in de
rij.
Small Basic
rij[1]=1
rij[2]=2
rij[3]=3
etc.
Lazarus
rij[1]:=1;
rij[2]:=2;
rij[3]:=3;
etc.
Je kunt de rij gemakkelijk met een for-lus vullen:
Small Basic
for teller=1 to 52
rij[teller]=teller
endfor
Lazarus
for teller:=1 to 52 do begin
rij[teller]:=teller;
end;
Opdracht 4.1
Ontwerp een algoritme dat de nummers in de rij door elkaar gooit. Noem dit algoritme
schudden. Verwerk het tot een NSD
Opdracht 4.2
Verwerk je algoritme schudden tot een programma in Small Basic.
Opdracht 4.3
Vul de code aan met een test die bepaalt hoeveel getallen na het schudden niet op een
andere plek in de rij terecht gekomen zijn.
Komt het vaak voor dat alle kaarten van hun plaatst geschud zijn als je 100x verwisseld?
14
Ga ervan uit dat er 52 kaarten in het spel zijn (dat is een kaartspel zonder jokers).
15
5
Mijnenveger
Mijnenveger is een populair spel dat gratis bij Windows geleverd wordt.
In een veld worden willekeurig mijnen geplaatst. Als je op een cel klikt bekijkt het
programma of er een mijn ligt. Je bent af als dat zo is. Als er geen mijn ligt klapt het
programma soms meerdere cellen open. In de afbeelding hieronder kun je die gebieden
herkennen aan de grijze vakjes zonder getal.
Hoe bepaalt de computer welke cellen er opengeklapt moeten worden?
De gebruiker klikt op een cel van het speelveld. De cel heeft een x- en een y-coordinaat.
De y-coördinaat wordt groter als je omlaag gaat, dat is het omgekeerd van een rooster in
de wiskunde.
Het algoritme CheckClick beschrijft wat er na de klik moet gebeuren.
Beschrijving van het Algoritme CheckClick(X,Y)
Stap
Beschrijving
1
maak een lege lijst L
2
controleer of op cel (X,Y) een mijn ligt
ja → einde spel
nee → voeg deze cel (x,y) aan de lijst L toe
3
neem een cel (a,b) uit de lijst → Klap deze cel(a,b) open
4
tel het aantal mijnen rond cel(a,b)
als het aantal nul is → klap alle aangrenzende hokjes open en voeg ze
aan de lijst toe (*).
als het aantal groter dan nul is schrijven we dat aantal in de cel. Met de
aangrenzende cellen doen we niets.
(*) Let op: als de cel al in de lijst stond voegen we het niet nóg eens toe.
Cellen waar al een getal in staat worden ook niet aan de lijst toegevoegd.
5
ga naar stap 3 zolang de lijst niet leeg is
16
Volgorde waarin de buren van cel(a,b) worden gecontroleerd
We spreken af dat de cellen rond (a,b) in de volgende volgorde aan de rij toegevoegd
worden.
1
2
3
4
(a,b) 5
6
7
8
Aantal buren van cel(a,b)
Als de cel (a,b) ergens aan de rand of in de hoek van het speelveld staat wordt maar
een deel van deze cellen toegevoegd.
Dit kunnen 3,5 of 8 cellen zijn. In de hoeken 3, aan de randen 5 en in het midden 8.
Bijvoorbeeld
(a,b) is de cel linksboven: alleen 5, 7 en 8 worden toegevoegd
In het echte programma worden de routines StepBox, StepXY en CountBombs
gebruikt
17
We gaan dit nu bekijken in het volgende voorbeeld.
voorbeeld
Bekijk het onderstaande speelveld. De cellen in het speelveld hebben (x,y)coördinaten.
Als je naar beneden gaat wordt de y-coördinaat groter.
Dus (1,1) is linksboven, (3,3) is rechts beneden.
De B staat voor een bom. Er is nog geen enkel veld aangeklikt. De speler ziet
natuurlijk niet waar bommen staan.
Stel dat de speler op (1,1) klikt. (1,1) wordt dus in de lege lijst gezet. In de
onderstaande afbeelding is dat aangegeven met een L.
Stap 1
Stap 2
L
0
L
L
L
B
B
B
B
De lijst bevat (1,1).
De lijst bevat (2,1), (1,2) en (2,2).
(1,1) wordt opengeklapt en uit de lijst
gehaald. Er staat 0 bommen rond (1,1).
De aangrenzende cellen van (1,1) worden
in de lijst gezet: (2,1), (1,2) en (2,2).
We nemen (2,1) uit de lijst.
De aangrenzende cellen van (2,1)
bevatten 1 bom, daarom markeren
we (2,1) met het getal 1.
Stap 3
Stap 4
0
1
0
L
L
L
1
2
B
B
B
B
De lijst bevat (1,2) en (2,2).
De lijst bevat (2,2).
We nemen (1,2) uit de lijst
De aangrenzende cellen van (1,2)
bevatten 1 bom, daarom markeren
we (1,2) met het getal 1.
We nemen (2,2) uit de lijst.
De aangrenzende cellen van (2,2)
bevatten 2 bommen, daarom
markeren we (2,2) met het getal 2.
De lijst is nu leeg. We kunnen daarom stoppen.
Dit voorbeeld was eenvoudig om dat alleen bij het verwerken van de eerste cel nieuwe
cellen toegevoegd werden.
18
Opdracht 5.1
Speel met z'n tweeën mijnenveger op papier door het algoritme toe te passen.
Gebruik ruitjespapier.
Speler 1 neemt een leeg veld van 5x5 en zet 5 mijnen in het veld, zó dat er minstens 1 cel
is waar geen mijnen om heen staan. De cel linksboven is (1,1). De cel rechtsonder is (5,5).
Speler 2 noemt een coördinaat (x,y) tot van elke cel bekend of er een mijn staat.
Speler 1 pas het CheckClick algoritme toe op de genoemde cel.
Speler 2 controleert of het algoritme goed toegepast wordt.
Als je een video-opname van je spel opneemt en instuurt levert dat een micropunt op.
Opdracht 5.2
Verwerk het algoritme van mijnenveger in een NSD. Vraag je eerst af welk soort lus je gaat
gebruiken.
Opdracht 5.3
Bekijk het onderstaande speelveld. De cellen in het speelveld hebben (x,y)-coördinaten,
waarbij de y-coördinaat groter wordt als je omlaag gaat (dat is het omgekeerde van wat je
tijdens de wiskunde les leert).
Dus (1,1) is linksboven, (3,3) is rechts beneden.
De B staat voor een bom. Er is nog geen enkel veld aangeklikt. De speler ziet natuurlijk
niet waar bommen staan.
Stel dat de speler op (1,1) klikt. (1,1) wordt dus in de lege lijst gezet.
B
Werk stap voor stap uit hoe het algoritme CheckClick de juiste cellen openklapt.
19
Opdracht 5.4
Bekijk het onderstaande speelveld. De cellen in het speelveld hebben (x,y)-coördinaten,
waarbij de y-coördinaat groter wordt als je omlaag gaat (dat is het omgekeerde van wat je
tijdens de wiskunde les leert).
Dus (1,1) is linksboven, (5,5) is rechts beneden.
De B staat voor een bom. Er is nog geen enkel veld aangeklikt. De speler ziet natuurlijk
niet waar bommen staan.
Stel dat de speler op (1,1) klikt. (1,1) wordt dus in de lege lijst gezet.
B
B
B
B
B
Pas het CheckClick algoritme toe op de genoemde cel.
Opdracht 5.5
Schrijf het programma Mijnenveger in Small Basic.
Maak het programma voor een veld van 10x10. Zet in dit veld tussen de 5 en 15 bommen.
Bron: http://www.techuser.net/minecascade.html
20
6
Liftplanning
Een lift moet zo efficiënt mogelijk mensen ophalen en afleveren op verschillende etages.
Laten we om te beginnen eens kijken naar één lift die 5 etages moet bedienen. Bij elke
etage is een knop waarmee de lift opgeroepen kan worden. Pas als je in de lift bent kun je
de bestemming aangeven door op het nummer van die etage te drukken. Deze verzoeken
worden in een buffer bewaard.
De begane grond krijgt altijd nummer 0. Als er 5 etages zijn, heeft de vijfde etage dus
nummer 5.
Vijfde etage
5
4
3
2
Eerste etage
1
Begane grond
0
Opdracht 6.0
Lees het artikel 'wie het eerst drukt, het eerst de lift' in de bijlage.
Beschrijving van het Algoritme: Shortest seek first
Stap Beschrijving
1
Zolang er een etage in de buffer staat
Kies de dichtstbijzijnde verdieping. Daarbij wisselt de bewegingsrichting indien nodig - steeds om. Het maakt dus niet uit of de dichtstbijzijnde etage
boven of onder de lift ligt, het algoritme kiest steeds de dichtstbijzijnde.
Het nadeel van Shortest Seek First is dat bij een druk bezette lift sommige verzoeken zeer
lang (in theorie eeuwig) in de buffer kunnen blijven staan.
Als twee etages evenver van de lift liggen, kies je er willekeurig een.
21
Voorbeeld
Stel dat een flat 10 etages heeft en dat de volgende verzoeken in de buffer staan: 1,
4, 9. De lift staat aan het begin op etage 5.
De lift staat op etage 5. De dichtstbijzijnde etage is 4.
In de buffer staan 1, 9.
De lift staat op etage 4. De dichtstbijzijnde etage is 1.
In de buffer staat 9.
De lift staat op etage 1. De dichtstbijzijnde etage is 9.
De buffer is leeg.
De lift staat op etage 9. De blijft op etage 9, want de buffer is leeg.
Opdracht 6.1
Stel dat een flat 100 etages heeft en dat de volgende verzoeken in de buffer staan:
10,35,40,42,45,49,50,51,65. De lift staat aan het begin op etage 50.
In welke volgorde worden de etages afgehandeld als er geen nieuwe verzoeken in de
buffer geplaatst worden?
Opdracht 6.2
In de tekst staat dat het in theorie eeuwig kan duren eer een etage aan de beurt komt.
Bedenk een situatie waarin dat gebeurt.
Het tweede liftalgoritme heet SCAN, dat bekijken we nu.
Beschrijving van het Algoritme SCAN
Als de lift in bedrijf is komen er nieuwe verzoeken binnen om bepaalde etages te
bezoeken. Dat kan door op het knopje bij de deur te drukken of door in de lift een
bestemming te kiezen.
De verzoeken worden afgehandeld in de richting waarin de lift op dat moment
beweegt.
De lift beweegt dus tussen de onderste en de bovenste etage op en neer. Onderweg
22
wordt alleen gestopt als de etage in de buffer staat.
Dus:
 als de lift omhoog beweegt worden eerst de verzoeken boven de huidige etage
verwerkt.
 als de lift omlaag beweegt worden eerst de verzoeken onder de huidige etage
verwerkt.
 als de lift stil staat (omdat alle eerdere verzoeken afgehandeld waren) beweegt de
lift meteen in de richting van het eerste verzoek.
 de lift beweegt altijd door tot de onderste en bovenste etage, ook als daar geen
verzoeken voor in de buffer stonden. Pas daar keert de lift om.
 het kan natuurlijk zijn dat er meer verzoeken in de buffer staan te wachten.
Voorbeeld 1
opgave 0
Een flat heeft 10 etages en één lift. De lift bedient de etages met het SCANalgoritme.
De volgende etages staan in de buffer: 1,2,9.
De lift bevindt zich op de 8e etage en beweegt omhoog.
In welke volgorde worden de etages bediend?
uitwerking
In de buffer staan 1,2 9
De lift beweegt omhoog.
De eerste etage die wordt bediend is dus 9.
Daarna beweegt de lift door naar de bovenste etage:10. De deuren worden hier niet
geopend.
Dan gaat de lift naar beneden en worden de etage in volgorde bediend.
De lift stopt als de buffer leeg is.
0
In de onderstaande tabel is samengevat in welke volgorde de lift op etage stopt.
Alleen de etages waar de lift stopt staan in de tabel.
De asterisk bij etage 10 betekent dat de liftdeuren hier niet geopend worden.
etage
1
2
9
10
volgorde 4
3
1
2*
Voorbeeld 2
In dit voorbeeld bekijken we langs hoeveel etages SCAN gemiddeld moet bewegen
om een aantal verzoeken al te handelen.
We gaan uit van een flat met 100 etages. In de buffer staan de volgende verzoeken:
10,20,50,75,100. De lift begint op etage 35.
De lift beweegt aan het begin omhoog.
23
We zetten de verzoeken in oplopende volgorde: 10, 20, 50, 75, 100.
stap 1: beweegt van 35 naar 50 dat kost 15 etages
stap 2: 50 → 75 = 25 etages
stap 3: 75 → 100 = 25 etages
stap 4: 100 → 20 = 80 etages
stap 5: 20 → 10 = 10 etages
In totaal moest de lift 15+25+25+80+10 = 155 etages passeren. Per etage is dat
155/5=31 etage per verzoek.
Opdracht 6.3
Een flat heeft 100 etages en één lift. De lift bedient de etages met het SCAN-algoritme.
De volgende etages staan in de buffer: 10,30,50,70,90.
De lift bevindt zich op de 80e etage en beweegt omhoog.
In welke volgorde worden de etages bediend? Noteer je antwoord in de vorm van de tabel
zoals in voorbeeld 1.
Opdracht 6.4
Kan het bij SCAN ook gebeuren dat etages eeuwig staan te wachten?
Natuurlijk is er op het algoritme algoritme SCAN wel wat aan te merken. Het lijkt
bijvoorbeeld erg overbodig om naar de bovenste en onderste etage door te bewegen.
Daarom zijn er andere algoritmes ontwikkeld die de nadelen van SCAN niet hebben.
Beschrijving van het algoritme C-SCAN (Circular Elevator Algorithm)
De lift handelt de verzoeken nu nog maar in één richting af, dus óf naar boven, óf
naar beneden.
Laten we er vanuit gaan dat de lift de verzoeken alleen naar boven afgehandeld.
Zodra de bovenste etage bereikt is beweegt de lift weer helemaal naar beneden
zonder te stoppen.
Daarna beweegt de lift weer omhoog. Dan worden ook weer reizigers afgezet of
opgepikt.
Als je van de derde etage naar de tweede etage moet gaat dat dus op een vreemde
manier:
 eerste beweeg je naar de bovenste etage
 dan helemaal naar beneden
 en dan pas omhoog naar de tweede etage
24
Toch heeft het algoritme ook een voordeel: bij SCAN komen de middelste etages
twee keer zo vaak aan bod als de bovenste en onderste etages. Bij C-SCAN worden
alle etages even vaak bediend.
Beschrijving van het algoritme LOOK
Dit algoritme is hetzelfde als SCAN met dit verschil: de lift beweegt nu niet meer door
tot de uiterste etages maar keert de bewegingsrichting meteen om als het laatste
verzoek in de huidige richting afgehandeld is.
Beschrijving van het algoritme C-LOOK
Dit algoritme is hetzelfde als C-SCAN met het volgende verschil: de lift beweegt nu
niet meer door tot de uiterste etages, maar keert de bewegingsrichting om als het
laatste verzoek in de huidige richting afgehandeld is. Er is maar weer één richting
waarin de verzoeken afgehandeld worden.
Opdracht 6.5
Analyseer het C-SCAN algoritme op dezelfde manier als hierboven bij het SCAN-algoritme
gedaan is.
We gaan weer uit van een flat met 100 etages. In de buffer staan de volgende verzoeken:
90, 50, 10, 20, 75. De lift begint op etage 35.
De lift beweegt nu omhoog en handelt alleen in opgaande richting verzoeken af.
Bereken het gemiddelde aantal etages per verzoek.
Doe daarna hetzelfde voor C-LOOK met dezelfde verzoeken in de buffer.
Opdracht 6.6
Verwerk één van de algoritmes: Shortest Seek, SCAN, C-SCAN, LOOK of C-LOOK tot
een NSD
Spreek met je docent vooraf welke van deze vijf je gaat uitwerken.
25
Opdracht 6.7
Schrijf een programma in Small Basic om het Shortest Seek, SCAN, C-SCAN, LOOK of
C-LOOK algoritme te demonstreren.
Maak gebruik van een GraphicsWindow als je een mooie presentatie wilt maken.
Gebruik de volgende variabelen:

etage: om de huidige etage in te bewaren.

min_etage = 0

max_etage: het nummer van de hoogste etage (bijvoorbeeld 100)

richting: is -1 als de lift naar beneden beweegt, +1 als de lift omhoog beweegt en 0
als de lift stil staat
Gebruik een Timer om de lift te laten bewegen, bijvoorbeeld 1 etage per seconde.
Maak gebruik van Math.GetRandomNumber() om op een willekeurige manier verzoeken in
de buffer te kunnen plaatsen.
Bron: http://en.wikipedia.org/wiki/Elevator_algorithm
26
7
Doolhoven
We stellen ons voor dat een doolhof een rechthoek is waarin met muren gangen gemaakt
zijn. Hoe maak je een doolhof?
Slecht algoritme
Stel je voor dat je met een rooster begint waarin alle muren getekend staan. Met een
gum wis je willekeurig wat muren weg.
De kans is groot dat je met een lelijk doolhof eindigt, zoals in de rechter afbeelding
hieronder.
Het rechter doolhof is 'slecht' om dat er (af-)gesloten gebieden in voorkomen en omdat er
pleintjes (onnodig grote ruimtes) in voorkomen.
Met de groene en rode blokjes is het start- en eindpunt van het doolhof gemarkeerd.
Opdracht 7.1
Schrijf een programma in Small Basic dat met het slecht algoritme een doolhof maakt.
Wanneer noemen we een doolhof dan 'goed'?
Definitie van een goed doolhof
Een doolhof is goed als de twee onderstaande voorwaarden allebei gelden:
1
2
je kunt vanuit een hokje elk ander hokje bereiken.
er loopt tussen twee hokjes maar één weg. Je kunt in een goed doolhof dus
geen rondjes lopen.
27
Met het volgende algoritme maak je betere doolhoven:
Beschrijving van het algoritme: Random divide
Pas dit algoritme op roosterpapier met hokjes van 1x1cm toe.
1
Teken de rechthoek waarbinnen het doolhof moet komen.
2
Verdeel een/de rechthoek, door op een willekeurig plek twee loodrechte lijnen te
teken (over de lijnen van het roosterpapier), die de oorspronkelijke rechthoek in
vier rechthoekige kamers verdeeld. Zo ontstaan vier scheidingsmuren.
3
Kies 3 van de 4 muren om op een willekeurige plek een opening te maken. Elke
opening heeft de breedte of hoogte van één hokje.
4
Ga naar stap 2, zolang er een kamer is die een breedte én hoogte heeft van
meer dan 1 hokje.
5
Aan het eind maak je twee gaten in de buitenmuur.
In de afbeeldingen hieronder zie je het algoritme als voorbeeld uitgewerkt.
Tussen de derde en de vierde afbeelding ontbreken een aantal afbeeldingen.
Doordat dit algoritme vaak lange rechte paden levert zoals in het voorbeeld hierboven is
het bij deze doolhoven vaak gemakkelijk bepaalde wegen uit te sluiten.
Het doolhof is pas compleet als er ook een in- en uitgang is. Deze maak je ergens
willekeurig in de buitenmuren.
Opdracht 7.2
Teken een rechthoek van 10 x 10 op ruitjespapier. Pas het Random Divide algoritme toe
om een doolhof te tekenen. Scan je resultaat en mail het voor een micropunt.
28
Opdracht 7.3
In de onderstaande afbeelding zie je een doolhof. Leg uit met welk van de twee algoritmen
dit doolhof gemaakt kan zijn.
De eerste computers hadden alleen tekstschermen (dus alleen een textwindow, maar
geen graphicswindow).
Met alleen een / of een \ was op een eenvoudige manier een doolhof te maken.
We kunnen dat in Small Basic ook doen met het volgende programma:
Algoritme: Een doolhof maken met alleen / en \
GraphicsWindow.Width=600
GraphicsWindow.Height=600
For y=1 To 50
For x=1 To 50
richting=math.GetRandomNumber(2)
If richting=1 Then
TextWindow.Write("/")
Else
TextWindow.Write("\")
endif
EndFor
TextWindow.WriteLine("")
EndFor
29
Dat ziet er helaas niet zo goed uit als op de oude computers:
Dat komt doordat de regels niet op elkaar aansluiten.
Als we lijntjes tekenen op een GraphicsWindow kunnen er er wel voor zorgen dat de
regels aansluiten.
Doolhof met alleen / of \ (Graphics-versie)
GraphicsWindow.Width=600
GraphicsWindow.Height=600
For y=1 To 50
For x=1 To 50
richting=math.GetRandomNumber(2)
If richting=1 Then
GraphicsWindow.DrawLine(x*10,y*10,x*10+10,y*10+10)
Else
GraphicsWindow.DrawLine(x*10,y*10+10,x*10+10,y*10)
endif
EndFor
EndFor
Dit algoritme maakt wel gesloten kamertjes, maar geen pleintjes.
30
Mogelijke verdieping: algoritme van Kruskal of Prim
Bronnen
http://en.wikipedia.org/wiki/Maze_generation_algorithm
meer op: http://www.mazeworks.com/mazegen/mazetut/index.htm
http://www.jamisbuck.org/presentations/rubyconf2011/index.html#title-page
31
8
The Game of Life
Een van de leukste dingen die de combinatie van wiskunde en computers heeft
opgeleverd zijn cellulaire automaten. Het principe ervan is simpel: je hebt een verzameling
hokjes, die elk een cijfer bevatten. Vaak kan dat alleen een 1 of een 0 zijn. Daarnaast heb
je regels die bepalen wat er met de vakjes gebeurt. Bijvoorbeeld: “Als ik een 0 ben en drie
van mijn acht buren zijn een 1, dan word ik ook een 1.
Een bekend voorbeeld is Game of Life, bedacht door James Conway. Bij deze automaat
(een spel is het niet echt) zijn de vakjes met enen en nullen in een vierkant of rechthoek
gerangschikt en gelden de volgende regels:
Beschrijving van het algoritme: Game of Life
per ronde voor alle cellen:
Bij een 1: Als van je 8 buren er twee of drie ook een 1 zijn, blijf je een 1.
Bij een 0: Als je precies drie buren hebt die 1 zijn wordt je een 1.
In alle andere gevallen wordt je een 0.
Let op: als een cel van kleur verandert telt dat pas in de volgende ronde!
Deze regel wordt ook wel samengevat als B3/S23:
Er wordt een nieuwe cel geboren (Birth) bij 3 buren
Een cel overleeft (Survive) bij 2 of 3 buren.
Een cel sterft als ze 0,1 of meer dan 3 buren heeft
Of wel:
een cel wordt een 1 als 3 buren een 1 zijn
Een cel blijft een 1 als 2 of 3 buren een 1 zijn
Een cel die 1 was wordt een 0 als 0,1 of meer dan 3 buren 1 zijn.
32
Als je alle vakjes met een 1 zwart inkleurt krijg je iets dat er als volgt uitziet:
Op het volgende webadres kun je met the Game of Life experimenteren:
http://vanjoolingen.nl/cells/life.html
Let op: je hebt hiervoor een moderne browser zoals Firefox of Chrome nodig.
Druk op Stap om de regels één keer toe te passen. Je ziet het patroon veranderen. Druk
op Start om de regels keer op keer toe te passen. Een beetje afhankelijk van het
beginpatroon kan een wild, dynamisch patroon ontstaan. Probeer ook het volgende eens
uit. Druk op Stop, en dan op Schoon. Klik dan een paar vakjes aan tot je het volgende
patroon ziet:
En druk op Start. Je hebt leven gemaakt: dit patroon schuift in vier stappen schuin naar
boven. Dit patroon heet – niet verrassend – een glider. Gliders zijn kwetsbaar, bij de
kleinste verstoring gaan ze dood. Zet maar eens een zwart vakje op zijn route, of laat er
twee botsen.
Muziektip
Speel ' imitation of life' van REM als je de animatie gebruikt.
http://www.youtube.com/watch?v=0vqgdSsfqPs
33
Stappenplan Game of Life
Hoe kun je nu het eenvoudigste de volgende generatie berekenen bij een spelregel?
voorbeeld
Stap 0
We gaan uit de het onderstaande speelveld.
De spelregel is B2/S23 (let op: er staat B2, niet B3 zoals op de vorige bladzijde).
Stap 1
Eerst bekijk je alle witte cellen. Als ze precies twee buren hebben, zet je er een B in.
B
B B
B
Stap 2
Dan kijken we naar de zwarte cellen. Als er 2 of 3 buren zijn, overleeft de cel. Je zet
er dan een S in. Als er 0, 1 of meer dan 3 buren zijn sterft de cel: zet er dan een –
(minteken) in.
S S
B
S S
B B
B
-
Stap 3
Met zwarte en witte cellen ziet het er dan als volgt uit:
34
Opdracht 8.1
Bepaal hoe de volgende generatie van het voorbeeld er uit ziet door het stappenplan te
volgen.
Opdracht 8.2
Bepaal voor elk van de 25 cellen in de volgende afbeelding de kleur in de volgende
generatie.
Randen en hoeken
Een bijzonder geval doet zich voor aan de randen en in de hoeken. Je kunt deze op twee
manieren verwerken.
Methode 1
Cellen die buiten het zichtbare gebied liggen zijn altijd dood. Een cel in een hoek kan
nooit meer dan 3 buren hebben. Een cel aan de rand maximaal 5.
methode 2
De randen zijn onzichtbaar verbonden met de andere kant. De linkerrand is dus met
de rechterrand verbonden. De bovenkant met de onderkant. Zo hebben ook de cellen
aan de randen en in de hoeken 8 buren.
Opdracht 8.3
In het onderstaande speelveld zijn de randen niet doorverbonden met de andere kant.
Markeer de buren van de zwarte cellen door er een kruisje in te zetten.
35
Opdracht 8.4
In het onderstaande speelveld zijn de randen wel doorverbonden met de andere kant.
Markeer de buren van de zwarte cellen door er een kruisje in te zetten.
Game of Life is interessant, omdat het laat zien dat je met simpele regels ingewikkeld
gedrag kunt krijgen. Als je de wilde patronen zou zien zonder eerst het verhaal te lezen
zou je waarschijnlijk niet vermoeden dat de onderliggende regels zo simpel zijn. Ondanks
die simpele regels blijkt dat de Game of Life veel mogelijkheden biedt. Er zijn patronen die
oneindig uitgroeien, gliders, ruimteschepen. Het is zelfs mogelijk een programmeerbare
computer in Game of Life te maken.
Opdracht 8.5
Schrijf een programma in Small Basic waarmee je Game of Life kunt 'spelen'.
Kies een gebied van bijvoorbeeld 20x20 cellen.
Welke methode gebruik je aan de randen?
Meer voorbeelden kun je in de Engelstalige wikipedia-artikel over Game of Life vinden:
http://en.wikipedia.org/wiki/Conway%27s_Game_of_Life
Op de volgende website vind je een java-applet (werkt dus niet op een tablet):
http://www.mirekw.com/ca/mjcell/mjcell.html
Er bestaat ook een 3D-versie van Game of Life:
https://www.youtube.com/watch?v=xg0PKAvL01Y
Bron: http://vanjoolingen.nl/
36
9
De uitgang vinden van een doolhof
Je wordt ergens in een doolhof neergezet. Hoe vind je de weg naar de uitgang van een
doolhof?
De eerste vraag is natuurlijk: ís er wel een weg naar de uitgang?
Je wilt dus weten of er wel een oplossing is.
We gaan het volgende doolhof bekijken. De ingang is bij I de uitgang is bij U.
I
U
37
Algoritme: CheckMaze
Stap 1
We beginnen bij de uitgang daar zetten we een open bolletje in.
I

o
Nu herhalen we de volgende stappen, tot er geen open bolletjes meer zijn.
Voor alle open bolletjes
Maak het bolletje zwart
Zet een bolletje in alle LEGE aangrenzende hokjes die niet door een muur
afgescheiden zijn.
Stap 2
De volgende stap is dus:
I
o

Stap 3
I
o


Stap 4
I
o


38

Stap 5
I
o




o
Stap 6
I
o
o

o




o

Stap 7
I
o
o
o



o







Stap 8
I
o
o

o




o









o

o



o
Stap 9
I

o



39





  
Stap 10


I
o





o

















Stap 11
o
























Stap 12

























We zijn nu klaar omdat er geen open bolletjes meer zijn.
Er vallen twee dingen op:
– In alle hokjes staat een zwart bolletje. Dat betekent dat je vanuit alle hokjes naar de
uitgang kunt lopen.
– Er staat dus ook een zwart bolletje in het hokje waar we de ingang gemaakt
hebben.
40
Opdracht 9.1
Pas het CheckMaze-algoritme toe op het volgende doolhof.
I
U
De open bolletjes zijn na een aantal stappen altijd op. Maar het hoeft niet zo te zijn dat er
uiteindelijk in elk hokjes een zwart bolletje staat.
Opdracht 9.2
Teken een doolhof van 5x5. Markeer daarin de ingang met een I en de uitgang met een U.
Kies de muren zo dat je niet vanuit alle hokjes naar de uitgang kunt lopen, maar wel vanuit
de ingang.
41
In het algoritme staat nadrukkelijk:
Zet een bolletje in alle LEGE aangrenzende hokjes die niet door een muur
afgescheiden zijn.
Als je de stappen hierboven naloopt zie je dat je NOOIT een aangrenzend hokje
tegenkomt dat niet leeg is.
Zijn er dan situaties waarin je wel een niet-leeg hokje tegenkomt?
We nemen daarvoor het volgende doolhof.
I
U
Stap 1
We beginnen weer bij de uitgang.
I
o
Stap 2
I
o

o
Stap 3
I
o



o
Stap 4
I




o
o

42
Stap 5a
We delen deze stap in twee delen op.
Het eerste bolletje kunnen we wegwerken.
I



a


o

Stap 5b
Als we nu het tweede bolletje willen wegwerken. Komen we echter het hokje tegen waarin
de a staat. Dat hokje is niet leeg.
Hoe komt het dat we nu wél een niet-leeg hokje tegenkomen? We zijn langs twee wegen
bij hetzelfde hokje aangekomen. Dit kan alleen gebeuren als je ergens in het doolhof een
rondje kunt lopen.
I



a


o

Kortom je kunt de algoritme MazeCheck niet alleen gebruiken om te bekijken of er weg
van de ingang naar de uitgang is, maar zelfs om te zien of het een goed doolhof is.
Als het een goed doolhof is, heb je aan het einde:
– In elk hokje een zwart bolletje staan.
– Ben je nooit een niet-leeg hokje tegengekomen.
43
Muurvolg-methode
Er is een verrassend eenvoudige manier die bij sommige doolhoven werkt.
Beschrijving van het algoritme: Muurvolg-methode
• Je kiest een muur die je gaat volgen: de linker muur of de rechter muur.
• Telkens als je een deur in die muur tegenkomt, ga je die in. Je volgt de muur dan
aan de andere kant van de deur verder.
• Als een gang eindigt blijf je de gekozen muur volgen. Als dat de linker muur is draai
je dus naar rechts. Als je de rechter muur volgt draai je naar links.
Opdracht 9.3
Je staat op de plek die met het kruisje gemarkeerd is.
Volg de linker muur naar de uitgang.
Opdracht 9.4
Je staat op de plek die met het kruisje gemarkeerd is.
Volg de rechter muur naar de uitgang.
Als je in een gang staat is het afhankelijk van je kijkrichting wat links en rechts is.
Opdracht 9.5
Wat is voor de persoon die op de plaats van het kruisje staat de linker muur?
44
Daarom markeren we de plek niet met een kruisje maar met een pijltje dat de kijkrichting
aangeeft.
In een slecht doolhof kun je de uitgang vanuit sommige hokjes niet vinden met de
'muurvolg-methode'.
Opdracht 9.6
In het onderstaande doolhof is de uitgang rechts. Wat gebeurt er als je met de muurvolgmethode de uitgang probeert te vinden vanuit het punt dat met een kruisje gemarkeerd is?
Vanuit een afgesloten deel van een slecht doolhof kun je natuurlijk ook nooit bij de uitgang
komen.
x
U
45
Cellulaire automaat
Met cellulaire automaten kunnen we meer dan mooie patronen genereren. Ze vormen een
krachtig gereedschap om allerlei processen en principes mee te simuleren.
Je leert nu een cellulaire automaat waarmee je de weg uit een doolhof kunt vinden. Een
belangrijk verschil met de Game of Life is dat de cellen nu op meerdere manieren gevuld
kunnen zijn. Preciezer, een cel is niet alleen bezet of leeg, maar kan bezet zijn met
verschillende kleuren.
Het model van het doolhof bestaat uit een vierkant van cellen waarin de muren worden
gevormd door bruine cellen. Bij de uitgang is een cel gevuld met groen. De persoon die
probeert de ontsnappen staat in een rode cel.
Net als bij de Game of Life geven we regels hoe de cellulaire automaat gaat veranderen.
Het zal duidelijk zijn dat de verandering alleen zal plaatsvinden rond de rode cel. Daarom
formuleren we de regels vanuit die cel. Alle regels gelden dus alleen als de cel rood is:
Beschrijving van het algoritme: Cellulaire Automaat-methode
• Ga naar de groene cel: Als een van je buurcellen (links, rechts, boven, onder) groen
is: kleur die cel rood, en kleur je eigen cel geel. Stop daarna.
• Ga naar een lege cel: Als een van je buurcellen leeg is, kleur die rood en kleur je
eigen cel geel.
• Volg het spoor terug: Als geen van je buurcellen leeg is, maar er is wel een gele,
kleur die rood, en de cel waar je vandaan komt blauw.
• In alle andere gevallen stop je.
Op het volgende webadres vind je een werkende simulatie:
http://vanjoolingen.nl/cells/maze.html
Als je de simulatie start, zie je dat de rode cel beweegt naar lege cellen. Wanneer er geen
lege cellen in de buurt zijn, volgt hij het gele spoor terug, tot er weer een lege cel is die
geprobeerd kan worden, net zolang tot de uitgang is bereikt. Het gele spoor tekent de
46
gevolgde weg. De gele vakjes zou je daarom ook broodkruimels kunnen noemen. Het
weer teruggaan naar eerder neergelegde broodkruimels noemen we backtracken.
Opdracht 9.7
Onderzoek wat de automaat precies doet als hij bij een kruispunt van wegen aankomt.
In welke volgorde worden de richtingen Links, Rechtdoor en Rechtsaf doorlopen?
Opdracht 9.8
Bekijk het onderstaande doolhof. De zwarte cellen zijn muren.
Je begint in S. De uitgang ligt bij U.
Op een kruispunt wordt de richtingen in de volgende volgorde doorlopen: Links,
Rechtdoor, Rechts.
In welke volgorde worden de cellen 1, 2, 3, 4, 5 en 6 bezocht door de cellulaire automaat?
Noteer de volgorde.
Als een cel meer dan 1 keer bezocht wordt moet je het nummer van die cel bij elk bezoek
opschrijven.
Opdracht 9.9
Ontwerp zelf een doolhof dat minimaal 10x10 cellen groot is.
Muren kleur je zwart.
Markeer het startpunt met S en de uitgang met U.
Markeer kruisingen en eindpunten van gangen met cijfers zoals in de vorige opdracht.
In welke volgorde worden de met cijfers gemarkeerde punten doorzocht door de cellulaire
automaat.
Je kunt voor deze opdracht 3 micropunten verdienen:
47
– een doolhof van minimaal 10x10 cellen
– een kopie van het doolhof met kruisingen en eindpunten gemarkeerd
– een ingekleurde kopie met zwarte, gele, blauwe, groene, rode en witte cellen.
Als je wilt kun je opnieuw beginnen en met muisklikken muren bijmaken of wegbreken.
Onderzoek hoe de route verandert. Of maak een onmogelijk speelveld.
Omdat de regels er voor zorgen dat er altijd maar één rode cel is, is het verleidelijk die een
eigen identiteit te geven. Het is de representatie van de persoon die de uitweg zoekt. De
gekleurde cel wordt een ding, met een eigen gedrag. Zoiets wordt een agent genoemd, en
de simulatie wordt dan, in goed Nederlands, een agent-based simulation. Aardig van deze
simulaties is dat ze laten zien dat je met weinig eenvoudige regels relatief ingewikkeld
gedrag kunt begrijpen.
Opdracht 9.10
Schrijf een programma in Small Basic waarmee je en cellulaire automaat de weg door een
doolhof laat zoeken.
Bron: http://vanjoolingen.nl/
48
10
Levenshtein
Soms verschillen woorden maar heel weinig van elkaar. Het algoritme van Levenshtein
geeft een maat voor de afstand tussen twee strings. Je kunt dit gebruiken bij
spellingscontrole, spraakherkenning, vergelijken van DNA, of het ontdekken van plagiaat.
We kunnen het woord GUMBO veranderen in GAMBOL door de U in een A te veranderen
en een L toe te voegen. Omdat dit twee veranderingen zijn zeggen we dat de afstand
tussen de twee woorden 2 is.
Er zijn drie soorten veranderingen: een letter toevoegen, wissen of veranderen.
Het algoritme berekend de Levenshtein-afstand tussen twee strings s en t,
Beschrijving van het algoritme Levenshtein
Stap
Beschrijving
1
n ← de lengte van s
m ← de lengte van t
// uitzonderingen worden hieronder afgehandeld
als n=0 dan geef n als antwoord en stop
als m=0 dan geef m als antwoord en stop
maak een matrix met de rijen 0..m en de kolommen 0..n
2
Schrijf in de eerste rij de getallen 0..n
Schrijf in de eerste kolom de getallen 0..m
3
Vergelijk elke teken van s (i loopt van 1 tot n)
4
Met elk teken van t (j loopt van 1 tot m)
5
Als s[i]=t[j]
dan kosten=0
anders kosten=1
6
De cel(i,j) krijgt als waarde het minimum van de volgende drie
mogelijkheden:
–
–
–
7
de waarde van de cel erboven +1: d[i,j-1]+1
de waarde van de cel links +1: d[i-1,j]+1
de waarde van de cel linksboven + de kosten die in stap 5 berekend
werden: d[i-1,j-1]+kosten
Nadat de stappen 3,4,5 en 6 compleet doorlopen zijn, staat de afstand in
cel(n,m). Dat is de cel rechtsonder.
49
We gaan dit bekijken aan de hand van een voorbeeld:
voorbeeld
Aan de hand van de woorden GUMBO en GAMBOL zullen we deze stappen nu
doorlopen.
Na stap 1 en 2
0
G
1
A
2
M
3
B
4
O
5
L
6
G
U
M
B
O
1
2
3
4
5
Na stap 3 tot en met 6 voor i=1
G
U
M
B
O
0
1
2
3
4
5
G
1
0
A
2
1
M
3
2
B
4
3
O
5
4
L
6
5
Na stap 3 tot en met 6 voor i=2
G
U
M
B
O
0
1
2
3
4
5
G
1
0
1
A
2
1
1
M
3
2
2
B
4
3
3
O
5
4
4
L
6
5
5
50
Na stap 3 tot en met 6 voor i=3
G
U
M
B
O
0
1
2
3
4
5
G
1
0
1
2
A
2
1
1
2
M
3
2
2
1
B
4
3
3
2
O
5
4
4
3
L
6
5
5
4
Na stap 3 tot en met 6 voor i=4
G
U
M
B
O
0
1
2
3
4
5
G
1
0
1
2
3
A
2
1
1
2
3
M
3
2
2
1
2
B
4
3
3
2
1
O
5
4
4
3
2
L
6
5
5
4
3
Na stap 3 tot en met 6 voor i=5
G
U
M
B
O
0
1
2
3
4
5
G
1
0
1
2
3
4
A
2
1
1
2
3
4
M
3
2
2
1
2
3
B
4
3
3
2
1
2
O
5
4
4
3
2
1
L
6
5
5
4
3
2
Stap 7
De afstand staat nu in de cel rechtsonder: 2. Er zijn dus twee veranderingen nodig
om GUMBO in GAMBOL te veranderen (de U wordt een A en achteraan wordt een L
toegevoegd)
51
Opdracht 10.1
Neem de onderstaande tabel over en bereken zoals in het voorbeeld de Levenshteinafstand tussen de woorden PAAS en HAAS. Dit voorbeeld is zo eenvoudig dat je vooraf al
kunt zien dat de afstand 1 is, maar het gaat om het correct toepassen van het algoritme.
0
P
1
A
2
A
3
S
4
H
A
A
S
1
2
3
4
Opdracht 10.2
Neem de onderstaande tabel over en bereken zoals in het voorbeeld de Levenshteinafstand tussen de woorden HAZEN en HAGEL.
0
H
1
A
2
Z
3
E
4
N
5
H
A
G
E
L
1
2
3
4
5
Opdracht 10.3
Schrijf een programma in Small Basic dat met het algoritme van Levenshtein de afstand
tussen twee woorden berekent.
52
Opdracht 10.4
Pas het programma van 10.3 zo aan dat het twee DNA-codes kan vergelijken.
Een DNA-code is een tekst met de letter C, T, A en G. Een ? Staat voor 1 onleesbare
letter.
Het programma moet twee willekeurige DNA codes met C, T, G, A en vraagtekens maken.
Het programma's moet de afstand met het algoritme van Levenshtein uitrekenen.
Als je deze opdracht te eenvoudig vind: Is het programma zo aan te passen dat een
vraagteken voor 1 of meer onbekende tekens kan staan?
Bron: http://www.merriampark.com/ld.htm
53
11
Meer over kaarten schudden
In hoofdstuk 4 hebben we het schudden van kaarten bekeken. In de uitwerking vond je
een oplossing die als volgt ging.
– bepaal hoeveel rondes doorlopen moeten worden
– kies in elke ronde twee kaarten en verwissel die
Als je heel vaak verwisseld is dat een goede methode. Toch kan dit beter. Het kan toevallig
zo zijn dat een paar kaarten niet gekozen worden!
Je kunt er gegarandeerd voor zorgen dat elke kaart snel een keer aan de beurt komt.
De volgende aanpassing (van Suraya) zorgt ervoor dat na 1 ronde alle kaarten aan de
beurt geweest zijn.
LET OP: Bij de volgende programma's ontbreekt het deel dat het array kaart met getallen
vult. Dat moet je doen voor je de kaarten schudt.
Voor de code moeten dus de volgende regels.
For teller=1 to 52
Kaart[teller]=teller
endfor
Bovendien is er geen code waarmee je kan zien wat er na het schudden in het array staat.
Dat gebeurt met de volgende code:
For teller=1 to 52
textwindow.writeline(Kaart[teller])
endfor
De code moet na de onderstaande code.
Schudden: Suraya
For teller=1 To 52
'kies één willekeurige kaart
'let op: dit kan toevallig dezelfde kaart als teller zijn!
x=math.getrandomnumber(52)
'verwissel de twee kaarten
temp=kaart[teller]
kaart[teller]=kaart[x]
kaart[x]=temp
endfor
Maar wat gebeurt er als in een bepaalde ronde x gelijk is aan teller? De kaart wordt met
zichzelf verwisseld. Dat is op te lossen door te eisen dat x ongelijk moet zijn aan de teller.
Schudden: gegarandeerde verwisseling
For teller=1 To 52
'kies een willekeurige kaart
'ga hiermee door tot x ongelijk aan teller is
54
'maak x gelijk aan teller, dan moeten we zeker nog een keer een
kaart kiezen
x=teller
while x=teller
x=math.getrandomnumber(52)
endwhile
'verwissel de twee kaarten
temp=kaart[teller]
kaart[teller]=kaart[x]
kaart[x]=temp
endfor
Bij opgave 4.3 werd gekeken hoeveel kaarten op dezelfde plek bleven liggen. Het aantal
kaarten dat niet verplaatst is, is een maat voor de kwaliteit van het schud algoritme. Hoe
minder kaarten nog op hun oorspronkelijke plek liggen, des te beter het algoritme zijn werk
deed.
Toch is dat niet voldoende. Bekijk maar eens het volgende voorbeeld:
voorbeeld
Neem een stapel gesorteerde kaarten.
Neem een deel aan de bovenkant van de stapel af, en schuif die aan de onderkant
erbij.
Alle kaarten zijn nu van nu plaats gewisseld, toch vind je waarschijnlijk dat de kaarten niet
goed geschud zijn!
Je kunt dat oplossen door te eisen dat er er geen opeenvolgende nummers in de lijst meer
mogen staan.
Opeenvolgend kan ook betekenen dat de rij afloopt: 8,7,6 is even erg als 6,7,8. Je wilt dus
voorkomen dat het verschil tussen twee opeenvolgende getallen nooit 1 of -1 is.
We doen dus het volgende:
•
•
•
we gebruiken het algoritme schudden met gegarandeerde verwisseling
dan tellen we hoeveel kaarten nog op hun oude plaats liggen en hoeveel
opeenvolgende kaarten er zijn
als beide aantallen nul zijn stoppen we, anders schudden we nog een keer met
gegarandeerde verwisseling
Opdracht 11.1
Schrijf een Small Basic programma dat ervoor zorgt dat de kaarten zó schud worden dat
ze:
55
– niet op hun oorspronkelijke plek meer staan, én
– er geen opeenvolgende kaarten voorkomen (ook niet in aflopende volgorde)
56
12
Priemgetallen
doelen
Weten wat een priemgetal is.
Het algoritme IsPriem kunnen reproduceren.
Het NSD van het algoritme IsPriem kunnen reproduceren.
De Small Basic-code van IsPriem kunnen reproduceren.
Het Small Basic-programma regel voor regel kunnen verklaren.
Weten dat elke getal minimaal 2 delers heeft.
Weten dat een priemgetal precies twee delers heeft.
Weten dat een getal met meer dan twee delers niet priem is.
Weten wat een echte deler is.
Weten hoe je het algoritme IsPriem kunt versnellen door
alleen de even delers te controleren.
alleen de delers tot de helft van n te controleren.
alleen de even delers tot de helft van n te controleren.
te stoppen als je de eerste deler gevonden hebt.
alleen de even delers tot de wortel van n te controleren.
Weten hoe je met het Algoritme van Erathostenes alle priemgetallen in een reeks
vindt.
Je ontdekt in deze paragraaf dat kleine veranderingen enorme verbeteringen kunnen zijn.
We maken een algoritme met een kleine verandering veel sneller (bijvoorbeeld 1000x zo
snel).
Wat is een priemgetal?
Priemgetallen zijn gehele positieve getallen groter dan 1, die alleen 1 en zichzelf als deler
hebben.
voorbeelden
Eén is dus geen priemgetal, want het getal moet groter dan 1 zijn.
Anderhalf is ook geen priemgetal, want het moet een geheel getal zijn.
Vier is dus geen priemgetal, want het is behalve door 1 en 4 ook door 2 deelbaar.
Twee is het enige even priemgetal, omdat alle andere even getallen door twee
deelbaar zijn.
De eerste priemgetallen zijn:
2,3,5,7,11,13,17,19,23,...
Hoe kunnen we een computerprogramma nu laten onderzoeken of een getal priem is?
57
Beschrijving van het Algoritme IsPriem
1
Geef een geheel getal n
2
Neem aan dat het aantal delers 2 is.
3
Neem d=2
4
Controleer of getal d het getal n deelt (de rest na deling is dan nul).
5
Als het getal n wél door n deelbaar is, maken we het aantaldelers één hoger
6
Als d<n-1, ga naar stap 3
7
Controleer het aantal delers: als het aantal twee is, was het getal n priem.
Beschrijving van het Algoritme IsPriem
Geef een geheel getal n
Neem aan dat het aantal delers 2 is.
Doorloop alle getallen d van 2 tot en met n-1.
Controleer of getal d het getal n deelt (de rest na deling is dan nul).
Als het getal n wél door n deelbaar is, maken we het aantaldelers één hoger
Controleer het aantal delers: als het aantal twee is, was het getal n priem.
Het algoritme gaat er vanuit dat elk getal twee delers heeft: 1 en zichzelf.
Voorbeeld
5 is deelbaar door 1 en 5, maar niet door andere gehele getallen
10 is behalve door 1 en 10 ook deelbaar door 2 en 5.
De extra delers noemen we echte delers.
Voorbeeld
De echte delers van 10 zijn 2 en 5.
Als een getal echte delers heeft is het niet priem.
Opdracht 12.1
Verwerk het algoritme IsPriem tot een NSD.
Opdracht 12.2
Schrijf een programma in Small Basic dat met behulp van het algoritme bepaalt of een
getal priem is of niet. Noem dit programma PRIEM1
Gebruik Clock.EllapsedMilliseconds om de tijd te meten die het programma voor een getal
nodig heeft.
Hint:
58
starttijd=Clock.ElapsedMilliseconds
'hier moet de code die het getal test
eindtijd=Clock.ElapsedMilliseconds
TextWindow.WriteLine("De verstreken tijd is:"+(eindtijdstarttijd)/1000+"seconde")
Gelukkig is het niet nodig om alle delers van 2 tot en met getal-1 te onderzoeken.
Er zijn vier eenvoudige verbeteringen mogelijk.
Verbetering 1: Voor alle getallen groter dan 2 kun je alle even delers overslaan.
Twee is het enige even priemgetal. Alle andere priemgetallen zijn dus oneven en niet
deelbaar door even getallen.
In plaats van
for deler=2 to getal -1
kunnen we dus beter het volgende gebruiken
for deler=3 to getal -1 step 2
We moeten dan wel controleren dat getal groter dan 2 is. Dat kan op de volgende manier
If math.remainder(getal,2)=0 then
isPriem=0
else
for deler=3 to getal -1 step 2
' rest van de routine
endfor
endif
Je bespaart hiermee (bijna) de helft van de controles.
Verbetering 2: De grootst mogelijke deler is de helft van het getal.
Als een getal deelbaar is (dus niet priem) heeft het vaak minstens twee echte delers.
Bijvoorbeeld: 46 is deelbaar door 2, maar het heeft dan ook meteen 23 als deler. Immers
2x23=46.
Omdat 2 de kleinste deler is, is
getal
de grootste deler.
2
59
In plaats van
for deler=2 to getal-1
kunnen we dus beter het volgende gebruiken
for deler=2 to getal/2
Je bespaart hiermee weer (bijna) de helft van de controles.
Combinatie van 1 en 2: We kunnen de twee verbeteringen samenvoegen. Je krijgt dan het
volgende:
In plaats van
for deler=2 to getal-1
gebruiken we
for deler=3 to getal/2 step 2
Deze twee verbetering zorgen er samen voor ongeveer driekwart van de mogelijke delers
wordt overgeslagen. Het programma wordt daardoor 4x zo snel.
Verbetering 3: Bij sommige getallen vind je al snel een deler. Je kunt dan meteen stoppen.
In plaats van een for-lus gebruik je een while-lus
IsPriem=1
TextWindow.Write("Geef een getal:")
Getal=TextWindow.ReadNumber()
if Math.Remainder(getal,2)=0 Then
deler=2
IsPriem=0
else
deler=3
While (deler<getal / 2) And (IsPriem=1)
if Math.Remainder(getal,deler)=0 Then
IsPriem=0
endif
deler=deler+2 'alleen oneven delers
endWhile
endif
'wat is het resultaat
If isPriem=1 Then
TextWindow.WriteLine(Getal +" is een priemgetal")
Else
TextWindow.WriteLine(Getal +" is deelbaar door "+deler)
60
endif
61
Verbetering 4: Maar het kan nog veel beter.
Hieronder staan alle vermenigvuldigen die 16 als antwoord geven met getallen groter dan
1:
product
Kleinste deler
2x8
2
4x4
4
8x2
2
De grootste kleinste deler is 4.
Hieronder staan alle vermenigvuldigen die 64 als antwoord geven met getallen groter dan
1:
product
Kleinste deler
2x32
2
4x16
4
8x8
8
16x4
4
32x2
2
De grootste kleinste deler is 8.
Hieronder staan alle vermenigvuldigen die 12 als antwoord geven met getallen groter dan
1:
product
Kleinste deler
2x6
2
3x4
3
4x3
3
6x2
2
De kleinste deler is maximaal 3.
Je hoeft zes niet meer te controleren, omdat je eerder al twee gehad hebt! Hetzelfde geldt
voor vier.
We hoeven voor het getal van 12 alleen 2 en 3 als deler te controleren. Dat scheelt de
helft!
62
Opdracht 12.3
Wat is de grootste van alle kleinste delers die in een product van 36 voorkomt?
Wat is de grootste van alle kleinste delers die in een product van 144 voorkomt?
Wat is de grootste van alle kleinste delers die in een product van 256 voorkomt?
Wat valt op aan deze getallen?
Wat is de grootste van alle kleinste delers delers van 110?
Klopt deze regel die je na de eerste drie getallen vond dus exact?
Maar hoe weet je nu waar je bij grotere getallen kunt stoppen met delen? Je moet stoppen
bij de grootste van alle kleinste delers die in een vermenigvuldiging voor dat getal
voorkomt.
Het was niet toevallig dat 4 de wortel van 16 is en 8 de wortel van 64.
We kunnen dus stoppen als we bij de wortel van het getal zijn.
Als de wortel niet een geheel getal is rond je naar beneden af.
In Small Basic schrijven we de for-lus dan zó:
for deler = 3 to math.Floor(Math.SquareRoot(getal)) Step 2
We gaan er dus vanuit dat het programma eerst kijkt of het getal deelbaar is door 2.
Als dat niet zo is worden alleen oneven delers gecontroleerd.
Opdracht 12.4
Pas het programma PRIEM1 zo aan dat je alleen de oneven delers tot de wortel van het
getal test. Noem dit programma PRIEM2.
Heeft deze verbetering het algoritme veel sneller gemaakt?
Voor het getal 1001 moesten we eerst de delers 2 t/m 1000 testen. Dat zijn 999 getallen.
Nu hoeven we nog maar 2 en de oneven getallen van 3 t/m 31 te testen. Dat zijn 15
999
≈66 keer zo snel geworden.
getallen. De test is daarom ruim
15
Bij grotere getallen is de winst nog veel groter.
Opdracht 12.5
Meet met beide programma's de tijd die het nodig heeft voor de volgende getallen:
10,100,1000,10000,100000,1000000,10000000 (tot tien miljoen).
63
Opdracht 12.6
Pas het programma PRIEM2 zo aan dat je een reeks getallen kunt onderzoeken. Noem dit
programma PRIEM3.
Hint: Gebruik een for-lus om de reeks getallen te doorlopen. Zet de priemtest in een
subroutine.
Hoewel PRIEM2 veel sneller is dan PRIEM1 moet je het niet voor hele grote getallen gaan
gebruiken. Zelfs voor een biljoen duurt het al veel te lang eer je het antwoord berekent
hebt. Gelukkig zijn er nog sneller methode die je wel met succes bij grote getallen kunt
gebruiken. Die methoden zijn echter te ingewikkeld om hier te bespreken.
We kunnen echter wel alle vier de verbeteringen in het programma opnemen:
IsPriem=1
TextWindow.Write("Geef een getal:")
Getal=TextWindow.ReadNumber()
if Math.Remainder(getal,2)=0 Then
deler=2
IsPriem=0
else
deler=3
isPriem=1
While (deler<=Math.SquareRoot(getal)) And (IsPriem=1)
if Math.Remainder(getal,deler)=0 Then
IsPriem=0
endif
deler=deler+2 'alleen oneven delers
endWhile
endif
'wat is het resultaat
If isPriem=1 Then
TextWindow.WriteLine(Getal +" is een priemgetal")
Else
TextWindow.WriteLine(Getal +" is deelbaar door "+deler)
endif
64
Alle priemgetallen in een reeks zoeken
Als je een reeks priemgetallen wilt zoeken is er gelukkig wel een onverslaanbare methode.
Die methode kon de Griekse geleerde Erathostenes meer dan 2000 jaar geleden al.
http://nl.wikipedia.org/wiki/Eratosthenes
Zijn methode heet dan ook de zeef van Erathostenes.
http://nl.wikipedia.org/wiki/Zeef_van_Eratosthenes
Je wilt bijvoorbeeld alle priemgetallen tot 25 vinden.
•
•
•
•
•
•
•
Allereerst streep je 1 af (want 1 is geen priemgetal)
Het eerste niet-doorgestreepte getal is 2. Dit getal omcirkel je. Het getal is priem.
Streep alle veelvouden van 2 af (2,4,6,8,...)
3 is het eerste getal dat niet doorgestreept of omcirkeld is. We omcirkelen drie: het
is priem.
Streep alle veelvouden van 3 af (3,6,9,12,...). Je komt daarbij getallen tegen die je
in stap twee al afgestreept hebt.
Je gaat hiermee door zolang het omcirkelde getal kleiner of gelijk is aan de wortel
van het eindgetal.
Er zijn nu nog getallen over: die zijn allemaal priem.
We kunnen dit algoritme als volgt omschrijven.
Algoritme Erathostenes
Stap beschrijving
1
Bepaal tot welk getal je alle priemgetallen wilt weten. Dit getal noemen we
max.
2
Bereken de wortel van max.
4
Streep 1 af.
5
omcirkel het eerste vrije getal, noem dit getal x.
6
streep alle veelvouden van x af.
7
ga naar stap 5 als x kleiner of gelijk is aan de wortel van max.
8
omcirkel alle overgebleven vrije getallen.
65
Voorbeeld
opdracht: Zoek alle priemgetallen tot en met 12
We maken een tabel met de getallen 1 tot en met 12.
De wortel van 12 is √12≈3,46 .
1
2
3
4
5
6
7
8
9
10
11
12
4
5
6
7
8
9
10
11
12
9
10
11
12
Streep 1 af
2
3
Omcirkel het eerste vrije getal (noem dit x): dit is 2.
2
3
4
5
6
7
8
Streep alle veelvouden van 2 af
2
3
5
7
9
11
7
9
11
Omcirkel het eerste vrije getal: 3
2
3
5
Streep alle veelvouden van 3 af
2
3
5
7
11
7
11
Omcirkel het eerste vrije getal: 5
2
3
5
Omdat vijf groter is dan de wortel van twaalf ( √ 12≈3,46 ) kunnen we stoppen. De
resterende getallen zijn automatisch priem. In dit geval zijn 5, 7 en 11 dus ook priem.
2
3
5
7
66
11
Opdracht 12.6
Gebruik het algoritme van Erathostenes om de priemgetallen van 1 t/m 25 te vinden.
1
2
3
4
5
6
7
8
9
10
11 12
13
14 15
16 17
18
19 20
21 22
23
24 25
Opdracht 12.7
Schrijf een programma in Small Basic dat het algoritme van Erathostenes gebruikt om alle
priemgetallen tot een bepaalde grens te vinden.
Het programma moet de totale tijd en de tijd per priemgetal (=totale tijd gedeeld door het
aantal priemgetallen) op het scherm schrijven.
Noem dit programma PRIEM4.
67
13
Het Traveling Salesman-probleem (TSP)
doelen
Kunnen omschrijven wat het Traveling Salesman-probleem is.
Weten wat een netwerk is.
Weten hoe je n! (n faculteit) berekent.
Weten met welke formulere je het aantal routes in een netwerk berekent.
Weten hoe je berekent hoeveel tijd het kost om alle routes te berekenen, als
gegeven is hoeveel tijd de berekening voor één route kost.
Begrijpen dat het onmogelijk is om alle routes binnen een redelijke tijd te
onderzoeken.
Kunnen beschrijven hoe het Greedy-algoritme een korte route vind.
De Small Basic-code van het Greedy-algoritme kunnen reproduceren.
De Small Basic-code van het Greedy-algoritme egel voor regel kunnen uitleggen.
In een route knelpunten kunnen vinden die op een eenvoudige manier een kortere
route opleveren.
Wat is het Traveling Salesman-probleem?
Een zakenman wil tien steden bezoeken. Hij vertrekt 's morgens thuis en komt aan het
eind van de dag ook weer thuis aan. Tussen elk paar steden is er een weg. In welke
volgorde moet hij de steden bezoeken, als hij een zo kort mogelijke reisafstand wil
hebben?
Elke stad wordt maar één keer bezocht. Omdat er tussen elk paar steden een weg is,
wordt een weg ook niet meer dan één keer gebruikt worden.
Hieronder zie je een voorbeeld van het probleem. De zakenman woont in A en wil B, C en
D bezoeken. Na afloop moet hij weer in A aankomen.
Figuur 13.1
Opdracht 13.1
Vind de kortste route in het netwerk van Figuur 13.1 door alle routes die in A beginnen te
controleren.
68
We noemen dit het handelreiziger-probleem. In het Engels: traveling Salesman-problem.
Dit wordt afgekort als TSP.
Beschrijving: Handelsreizigersprobleem / Travelings Salesman-problem
Een aantal steden zijn met een netwerk van wegen met elkaar verbonden. Tussen
elke paar steden loopt een weg.
Een handelsreiziger wil op één dag vanuit een stad alle andere steden bezoeken.
Daarbij wordt elke stad maar één keer bezocht.
Aan het eind van de dag komt de handelsreiziger weer aan in de stad waar hij
vertrok.
Bepaal de kortste route langs alle steden.
Aantal mogelijke routes door een netwerk
Op hoeveel verschillende manieren kan de zakenman door het netwerk reizen?
Stel dat er 4 punten in het netwerk zijn. De zakenman vertrekt in punt A. Dan zijn er 3
punten die hij moet bezoeken: B, C, D. Er zijn zes manieren waarop je drie punten in na
elkaar kunt bezoeken.
Voor het eerste punt kies je uit de 3 punten B, C en D.
Voor het tweede punt zijn er nog 2 keuzes over.
Voor het derde punt blijft vanzelf nog maar 1 punt over.
Daarom zijn er 3x2x1=6 mogelijkheden.
Er is een wiskundige notatie voor dit soort vermenigvuldigingen: 3x2x1 = 3!
Je spreekt dit uit als drie faculteit.
Voorbeelden
3! = 1 x 2 x 3 = 6
5! = 1 x 2 x 3 x 4 x 5 = 120
0!=1 (afspraak)
Het aantal mogelijke routes in een netwerk neemt snel toe.
Voorbeeld
Bij 11 punten zijn er 10! = 3628800 mogelijke routes.
Bij 21 punten zijn er 20! = 2432902008176640000 mogelijke routes.
De zakenman weet pas zeker dat hij de kortste route heeft gevonden als hij alle mogelijk
(n−1)!
routes doorlopen heeft. Het aantal mogelijke routes bij n punten is
.
2
Voorbeeld
Stel dat je 1000 mogelijke routes per seconde kunt onderzoeken.
Bij 21 punten heb je dan:
69
aantal routes=
(21-1)!/2 = 1216451004088320000
zoektijd=
1216451004088320000 / 1000 =
1216451004088320 s =
1216451004088320 / (3600x24x365) =
38573408,3 jaar
nodig!
Zelfs met 1 miljoen computers heb je nog 38 jaar nodig. Kortom het is onmogelijk
om alle routes te onderzoeken.
Opdracht 13.2
Kun de -1 en /2 in de formules verklaren?
Opdracht 13.3
Nederland heeft 31 steden met meer dan 100.000 inwoners (stand van 2016).
https://nl.wikipedia.org/wiki/Lijst_van_grootste_gemeenten_in_Nederland
Bereken hoe lang het duurt om alle routes tussen de 31 steden te berekenen. Eén route
kost 0,001s. Geef het antwoord in jaren.
70
Een korte route zoeken, maar misschien niet de kortste.
We weten nu dat het heel erg lang duurt om alle routes te onderzoeken. Zelfs als het
aantal punten niet heel erg groot is.
We concluderen dus dat het onmogelijk is om alle routes te onderzoeken.
Is het dan toch mogelijk om een korte route in het netwerk te vinden? We moet accepteren
dat die route waarschijnlijk niet de aller kortste route is.
Er is heel veel onderzoek gedaan naar algoritmen die snel een oplossing voor het TSP
vinden. We bespreken hieronder een mogelijk algoritme. Je zult aan het eind ook zien dat
dit algoritme korte routes vind, die we nog kunnen verbeteren.
Het Greedy-algoritme
Vanuit het startpunt kies je steeds de kortste weg naar een punt waar je nog niet eerder
geweest bent.
Figuur 13.1
Opdracht 13.4
Bepaal de kortste route vanuit punt A met het Greedy-algoritme.
Beschrijving van het algoritme: Greedy
Stap
Beschrijving
1
We kiezen een startpunt, dat is het actuele punt.
2
Het startpunt markeren we als 'gebruikt' of 'bezocht'.
3
Totale afstand = 0
4
Als alle punten bezocht zijn gaan we naar stap 10
5
We gaan steeds vanuit het actuele punt het dichtstbijzijnde punt kiezen dat
71
nog niet 'gebruikt' is.
6
Tel de afstand van het actuele punt naar het nieuwe punt bij de totale
afstand
7
Het nieuwe punt wordt het actuele punt
8
Markeer het actuele punt als 'bezocht'
9
Ga naar stap 4
10
Einde
Opdracht 13.5
Verwerk dit algoritme in een NSD.
Opdracht 13.6
Bedenk een reden waarom dit algoritme misschien toch niet zo goed is al het in eerste
instantie lijkt.
Omdat je steeds probeert om grote afstanden te vermijden, heet dit algoritme het Greedy
algoritme (greedy=gierig).
Opdracht 13.7
Schrijf dit algoritme in Small Basic. Laat het programma 10 willekeurige punten kiezen.
Het onderstaande codefragment kun je als start voor jouw uitwerking gebruiken.
' zet de punten in een lijst
GraphicsWindow.width=450
GraphicsWindow.height=450
aantal=10
For teller=1 To aantal
x[teller]=math.GetRandomNumber(400)
y[teller]=math.GetRandomNumber(400)
alinroute[teller]=0
GraphicsWindow.DrawEllipse(x[teller]-1,y[teller]-1,3,3)
endfor
72
Het Greedy algoritme geeft wel eens hele mooie oplossingen zoals in de afbeelding
hieronder.
Figuur 13.2
Maar het gaat ook wel eens mis:
Figuur 13.3
opdracht 13.8
Hoe kun je aan de route in figuur 13.3 zien dat dit zeker niet de kortste route is?
73
Als je het Greedy algoritme in een groot netwerk uitprobeert, geeft het best wel redelijke
oplossingen, al zie al snel dat het veel beter moet kunnen. Er zitten onnodige lussen in de
route en soms moet de handelsreiziger ineens naar een punt een heel eind verder op.
Figuur 13.4: Het Greedy algoritme voor een netwerk met 200 punten
Intermezzo: De langste route
Stof tot nadenken: het is heel erg gemakkelijk om een heel erg lange route te
vinden. Je gaat vanuit elk punt naar een punt dat het verst ligt (en nog niet bezocht
is). Maar geeft dat ook gegarandeerd de langste route?
74
14
Worteltrekken
We vinden het heel normaal dat je met een rekenmachine kunt worteltrekken. Maar hoe
werkt dat eigenlijk? Is worteltrekken net zo eenvoudig als optellen of vermenigvuldigen?
Stel dat je de wortel van een getal al weet, dan geldt het volgende:
wortel ×wortel =getal
Bijvoorbeeld: vier is de wortel van zestien, want 4×4=16
Je kunt dit ook schrijven als:
getal
=wortel
wortel
(1)
Dit is een bijzonder geval van:
getal
=uitkomst
deler
(2)
In het formule (1) zijn deler en uitkomst gelijk aan elkaar, in formule (2) hoeft dat niet zo te
zijn.
Stel nu dat je de wortel van 12 wilt berekenen. Neem dan voor deler een willekeurig getal.
De waarde van de uitkomst is dan als volgt te berekenen.
Voorbeeld
getal = 12, deler = 2 → uitkomst =
getal 12
= =6
deler 2
Zowel 2 als 6 zijn niet de juiste waarde van de wortel, maar we weten wel dat de waarde
van de wortel tussen 2 en 6 in ligt. Daarom nemen we als nieuwe gok voor de deler het
gemiddelde van 2 en 6.
Voorbeeld
getal = 12, deler =uitkomst =
2+6
=4 →
2
uitkomst =
getal 12
= =3
deler 4
Ook 3 en 4 zijn niet de waarde van de wortel van 12. Maar de wortel van 12 ligt wél tussen
3 en 4 in.
De volgende benadering van de wortel van 12 is weer het gemiddelde van 3 en 4.
75
De eerste stappen kunnen we als volgt in een tabel samenvatten:
Stap
deler
uitkomst
1
2
6
2
4
3
3
3,5
3,43
4
3,46
...
Je ziet misschien al dat de waarde van getal in de vierde stap al dicht is de buurt van de
werkelijke waarde van √ 12 komt: 3,4641016151377545870548926830117.
Nu zijn er twee belangrijke vragen:
– Maakt het uit met welk getal je begint?
Je zou dit eigenlijk moeten bewijzen, maar wij zijn nu tevreden met het antwoord:
Je mag beginnen met elk getal, zolang je maar met een positief getal begint. Nul en
negatieve getallen mag je niet als startgetal gebruiken. Delen door nul kan immers
niet en de afspraak is dat de wortel van een getal altijd positief is.
– Wanneer kunnen we stoppen?
In principe kun je dat zelf bepalen, maar je kunt de nauwkeurigheid vastleggen door
te stoppen als het absolute verschil van getal en uitkomst kleiner is dan een kleine
macht van 10.
We stoppen dus bijvoorbeeld als: ∣uitkomst−deler∣< 0,000001 of als
∣uitkomst∗uitkomst− getal∣< 0,000001
De strepen || betekenen dat je van de waarde tussen de strepen de positieve
waarde neemt: je laat het minteken weg.
In plaats van 0,00001 kun je ook een andere macht van 10 nemen: 0,1 – 0,01 –
0,001 etc.
In het onderstaande Small Basic-programma is dat uitgewerkt.
'worteltrekken
TextWindow.Write("Geef het getal waarvan de wortel: ")
getal= TextWindow.ReadNumber()
'kies een willekeurige deler (in dit voorbeeld 1)
deler=1
uitkomst = getal / deler
stap=1
'als het verschil kleiner is dan Grens, vinden we de benadering nauwkeruig
genoeg
' 10^-10 --> Math.Power(10,-10)
grens = Math.Power(10,-10)
While math.Abs(uitkomst*uitkomst - getal) > grens
'Haal hieronder de apostrofe weg als je de tussenstappen wilt bekijken.
'TextWindow.WriteLine(uitkomst)
stap=stap+1
deler = (deler+uitkomst)/2
uitkomst = getal / deler
76
endwhile
'schrijf de exacte waarde van Small Basic (dit is natuurlijk ook een
benadering)
textwindow.WriteLine("de wortel is
: "+Math.SquareRoot(getal))
'schrijf waarde die onze routine berekende
TextWindow.WriteLine("de benadering is : "+uitkomst)
'schrijf het verschil
verschil=Math.SquareRoot(getal) - uitkomst
TextWindow.WriteLine("het verschil is : "+ verschil)
'schrijf het aantal benodige stappen
TextWindow.WriteLine("aantal stappen
TextWindow.Pause()
: "+stap)
opdracht 14.1
Onderzoek aan de hand van het voorbeeldprogramma of het inderdaad niets uitmaakt
welk startgetal je voor de deler neemt (zolang het groter dan nul is).
opdracht 14.2
Kies een getal tussen 100 en 1000000.
Neem 1 als startwaarde voor de deler.
Doorloop het algoritme handmatig tot een nauwkeurigheid van
Maak de tabel indien nodig zelf langer.
stap
deler
10−6
uitkomst
77
opdracht 14.3
Kies 5 verschillende getallen tussen 0 en 1 miljard.
Kies een nauwkeurigheid (bijvoorbeeld 10−6 ).
Pas de nauwkeurigheid in het programma aan (of schrijf een extra regel die om de
nauwkeurigheid vraagt).
Bepaal hoeveel stappen het algoritme voor elke getal nodig heeft.
Vul de gekozen getallen en het aantal stappen in de onderstaande tabel in. De laatste
kolom heb je bij opdracht 14.4 nodig.
Getal
Nauwkeurigheid
Aantal stappen
Aantal eenvoudige
bewerkingen
Hoe complex is worteltrekken vergeleken met eenvoudige rekenkundige bewerkingen
zoals +-*/?
De kern van het voorbeeld-programma staat hieronder.
uitkomst = getal / deler
stap=1
'als het verschil kleiner is dan Grens, vinden we de benadering nauwkeruig
genoeg
' 10^-10 --> Math.Power(10,-10)
grens = Math.Power(10,-10)
While math.Abs(uitkomst*uitkomst - getal) > grens
'Haal hieronder de apostrofe weg als je de tussenstappen wilt bekijken.
'TextWindow.WriteLine(uitkomst)
stap=stap+1
deler = (deler+uitkomst)/2
uitkomst = getal / deler
endwhile
In plaats van Math.Power(10,-10) hadden we ook gewoon 0.0000000001 kunnen
schrijven.
Je hóeft hier dus geen macht uit te rekenen. In plaats van
While math.Abs(uitkomst*uitkomst - getal) > grens
Kun je ook
While math.Abs(uitkomst - deler) > grens
78
Schrijven.
Voor de lus staat 1 deling (uitkomst=getal/deler)
In de controle van de lus staat een vermenigvuldiging (uitkomst*uitkomst-getal>grens) of
aftrekking (uitkomst-deler > grens).
In de lus is de regel stap=stap+1 alleen nodig als je later wilt kunnen zeggen hoeveel
stappen er nodig waren. Voor de berekening van de wortel is deze regel niet nodig.
De andere twee regels zijn wel essentieel. In deze regels staat een optelling en twee
delingen.
Het aantal rekenkundige bewerkingen is dus:
aantal = 1 + stappen*4, als math.Abs(uitkomst - deler) > grens
Of zelfs
aantal = 1 + stappen*5, als math.Abs(uitkomst * uitkomst- getal) > grens
Afhankelijk van het aantal stappen zijn er 1, 5, 9, 13, of meer eenvoudige
rekenkundige bewerkingen nodig om de wortel te berekenen.
Een wortelberekening duurt dus 'lang' vergeleken met een eenvoudige
optelling.
Voor de berekening van y=2⋅x⋅x +6⋅x−3 zijn 5 eenvoudige bewerkingen nodig.
Dat is vaak nog minder dan 1 wortelberekening.
opdracht 14.4
Hoe complex is worteltrekken vergeleken met eenvoudige bewerkingen (+-*/)?
Vul in de tabel van opgave 14.3 het aantal eenvoudige rekenkundige
bewerkingen in dat nodig was om wortel van elk getal te berekenen.
79
15
Eerlijk delen
Al sinds de prehistorie is er een manier bekend waar men een hoeveelheid eerlijk kon
verdelen tussen twee mensen. De één verdeelt de hoeveelheid, de ander mag als eerste
een helft kiezen.
http://nl.wikipedia.org/wiki/Kiezen_of_delen
Degene die deelt zal zo precies mogelijk delen, omdat hij weet dat de ander zeker de
grootste portie zal kiezen.
Met drie personen gaat het op déze manier niet goed. Persoon A deelt in drie delen.
Persoon B kiest als eerste een deel. Het kan dan zijn dat hij het stuk neemt dat persoon C
het liefste had. C krijgt misschien zelfs een stuk dat minder dan een-derde deel is.
Toch is er wel een oplossing. We leggen dat uit aan de hand van een cake. Persoon A
beweegt het mes over de cake tot iemand 'stop' roept. Diegene vindt blijkbaar dat het mes
1/3e cake gaat afsnijden. De twee overgebleven personen verdelen het overgebleven deel
daarna met 'kiezen of delen'.
Opdracht 15.1
Waarom zijn de twee personen die het laatste deel verdelen tevreden met het deel dat
persoon A krijgt?
{todo: n>3}
Bron:
http://www-i1.informatik.rwth-aachen.de/~algorithmus/algo43.php
Verder lezen (in het Engels)
http://www.cs.cmu.edu/~arielpro/mfai_papers/BT95.pdf
80
16
Quicksort
Snel kunnen sorteren is een zegen. Het kan dus geen kwaad om nóg maar eens naar een
sorteer-algoritme te kijken.
Aan de hand van de volgende ongesorteerde rij leggen we uit hoe het algoritme werkt.
8
5
1
6
3
7
2
4
We kiezen – willekeurig – een spil (Engels: pivot). In dit voorbeeld kiezen we steeds het
laatste getal van een rij als spil.
We kiezen dus 4 als spil.
8
5
1
6
3
7
2
[4]
Nu gaan we kijken welke getallen kleiner zijn dan de spil, en welke getallen groter zijn dan
de spil.
We doorlopen de getallen voor de spil van voor naar achter. Als het getal groter dan de
spil is, zetten we het achteraan in de rij. Als het kleiner dan de spil is, laten we het staan.
De volgorde van de getallen verandert daarbij niet.
Als alle getallen doorlopen zijn hebben we de volgende rij.
1
3
2
[4]
8
5
6
7
Er zijn nu twee deelrijen ontstaan. Voor de [4] staan de getallen kleiner dan 4, na de [4]
staan de getallen groter dan 4.
We kunnen deze twee deelrijen nu onafhankelijk van elkaar verder sorteren. Van elke
deelrij nemen we weer het laatste getal als spil (hoewel je in principe elk getal als spil mag
kiezen).
1
3
[2]
[4]
8
5
6
[7]
[7]
8
Nu sorteren we de twee deelrijen en krijgen:
1
[2]
3
[4]
5
6
We hebben nu vier deelrijen over {1}, {3}, {5,6} en {8}. Ook die sorteren we door het laatste
getal als spil te kiezen.
Met {1} zijn we snel klaar. Als we 1 als spil kiezen is er verder niets meer te sorteren.
Ook met {3} zijn we zo klaar: er hoeft niets meer te gebeuren.
Hetzelfde geldt voor {8}.
In de rij {5,6} kiezen we 6 als spil. Aangezien 5 kleiner is dan [6] verandert er niets aan
deze rij.
[1]
[2]
[3]
[4]
5
[6]
[7]
81
[8]
Na het sorteren van {5,6} is alleen 5 nog niet aan bod geweest. Omdat het een rijtje van 1
getal is, zijn we klaar.
[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
Nu weten we zeker dat de rij gesorteerd is.
Dit algoritme heet Quicksort.
Zonder alle uitleg erbij kunnen we stappen van hierboven op de volgende manier kort
noteren:
stap 0:
stap 1:
stap 2:
stap 3:
8
1
1
[1]
5
3
[3]
[3]
1
[2]
[2]
[2]
6
[4]
[4]
[4]
3
8
5
[5]
7
5
[6]
[6]
2
6
8
[8]
4
[7]
[7]
[7]
In elke stap worden de nieuwe spillen met haken genoteerd. We zijn al na 3 stappen klaar!
AFSPRAAK
Bij het voeren van Quicksort moet een spil gekozen worden. We spreken af dat je aan het
begin zeg hoe je de spil kiest. Dat doe je daarna in elke stap op de afgesproken manier.
In het voorbeeld hierboven nemen we het laatste getal in een (deel-)rij als spil. Maar je
mag ook het eerste nemen. Het middelste getal bestaat natuurlijk niet altijd: als er een
even aantal getallen in een (deel-)rij staan, is er geen middelste. Er zijn wel oplossingen te
verzinnen:
spil=⌊
1+ aantal
⌋
2
aantal is natuurlijk het aantal getallen in de rij.
Voorbeeld 1
1+8
1+7
⌋=⌊
⌋=⌊4⌋=4
2
2
In een rij van 7 getallen is het 4e getal het middelste getal.
spil=⌊
Als er 7 getallen in de rij staan kies je
8 2 5 [2] 9 1 3
Voorbeeld 2
1+8
1+7
⌋=⌊
⌋=⌊4⌋=4
2
2
In een rij van 10 getallen is het 5e getal het getal links van het midden.
Als er 10 getallen in de rij staan kies je
7 3 5 2 [1] 4 8 6 9 10
82
spil=⌊
Opdracht 16.1
Sorteer de volgende rij getallen met quicksort:
8 9 14 16 11 12 13 10 20 1 2 3 17 18 19 4 7 5 6 15
Opdracht 16.2
Maak met het programma schudden met garantie een rij met de getallen 1 tot en met 25
die door elkaar staan.
Neem de getallen over op papier en sorteer de de getallen met Quicksort.
Stuur deze uitwerking naar [email protected].
Hoeveel stappen zijn het in het ergste geval nodig voor Quicksort?
Als de rij al gesorteerd is ontstaat er in elke stap 1 deelrij:
stap 0:
stap 1:
stap 2:
stap 3:
stap 4:
stap 5:
stap 6:
stap 7:
stap 8:
1
1
1
1
1
1
1
1
[1]
2
2
2
2
2
2
2
[2]
[2]
3
3
3
3
3
3
[3]
[3]
[3]
4
4
4
4
4
[4]
[4]
[4]
[4]
5
5
5
5
[5]
[5]
[5]
[5]
[5]
6
6
6
[6]
[6]
[6]
[6]
[6]
[6]
7
7
[7]
[7]
[7]
[7]
[7]
[7]
[7]
8
[8]
[8]
[8]
[8]
[8]
[8]
[8]
[8]
Er zijn dan dus 8 stappen nodig. Als de rij n getallen lang is, zijn er in dat geval n stappen
nodig.
Opvallend is dus, dat er veel stappen nodig zijn als de rij al gesorteerd is.
Opdracht 16.3
In welke andere situatie zijn er bij n getallen ook n stappen nodig?
83
In welke situatie zijn de minste stappen nodig?
Als in elke stap elke deelrij in twee gelijke deelrijen opgesplitst wordt zijn we het snelste
klaar:
Opdracht 16.4
Schrijf een quicksort-programma in Small Basic of Lazarus.
In de voorbeelden hierboven hebben we steeds getallenrijen bekeken, waarin elk getal
maar een keer voorkomt. Quicksort werkt met elke rij, we spreken af dat we getallen die
even groot zijn als de spil vóór de spil zetten.
1112191
1 1 1 2 1 9 [1]
1 1 1 [1] [1] 2 [9]
1 1 [1] [1] [1] [2] [9]
1 [1] [1] [1] [1] [2] [9]
[1] [1] [1] [1] [1] [2] [9]
84
17
Cellulaire automaten voor speelwerelden
Cellulaire automaten kunnen ook worden gebruikt om willekeurig speelwerelden te laten
maken.
Eerst maken we elke cel met een bepaalde kans blauw. Als we 35% kans op blauw nemen
krijgen ze een plaatje zoals hieronder.
85
Daarna berekenen we 5 generaties met de regel B45678/S34.
We krijgen dan een plaatje zoals hieronder. De blauwe gebieden kunnen bergen, meren of
bossen zijn.
Als je de kans op blauw in de eerste stap een beetje vergroot tot 45%, ontstaan er na 5
generaties grotten.
Tussen de blauwe rotsen zie je witte gangen.
86
De code ziet er als volgt uit:
sizeX = 75
sizeY = 75
startKans = 35 '35-> blauw zijn bergen of meren 45-> wit zijn grotten
GraphicsWindow.width= sizeX*5+40
GraphicsWindow.height= sizeY*5+40
While "true"
Initieer()
For teller=1 to 5
'haal in de volgende regel het aanhalingsteken weg als je elke stap van
'de berekening wilt zien
teken()
VolgendeRonde()
EndFor
teken()
Program.Delay(1000)
endwhile
' Vul de tabel met blauwe cellen
Sub Initieer
For x=1 To sizeX
For y=1 To sizeY
getal=math.GetRandomNumber(100)
If getal<startKans Then
cel[x][y]=1
Else
cel[x][y]=0
endif
EndFor
EndFor
EndSub
'Bereken de volgende generatie
Sub VolgendeRonde
For x=2 To sizeX-1
For y=2 To sizeY-1
' Tel het aantal blauwe cellen rond deze cel
aantal=0
For dx=-1 To 1
For dy=-1 To 1
If (dx=0) And (dy=0) Then
'doe niets
else
aantal=aantal+cel[x+dx][y+dy]
endif
EndFor
EndFor
newcel[x][y]=0
'survive or birth
If ((cel[x][y]=1) and (aantal>4)) Or ((cel[x][y]=0) and (aantal>5))
Then
newcel[x][y]=1
endif
'birth
EndFor
endfor
For x=1 To sizeX
87
For y=1 To sizeY
cel[x][y]=newCel[x][y]
EndFor
EndFor
EndSub
' Teken het veld op het scherm
Sub teken
For x=1 To sizeX
For y=1 To sizeY
If cel[x][y]=1 Then
GraphicsWindow.BrushColor="blue"
GraphicsWindow.FillRectangle(20+x*5,20+y*5,5,5)
Else
GraphicsWindow.BrushColor="white"
GraphicsWindow.FillRectangle(20+x*5,20+y*5,5,5)
endif
EndFor
EndFor
EndSub
88
18
De som is...
Je wilt een paar getallen hebben die samen 50 zijn. Hoe zou je dat aan kunnen pakken?
Allereerst moet je bedenken uit welke getallen je mag kiezen.
We kiezen voor de getallen 1 tot en met 10.
Een eerste poging ziet er als volgt uit:
// dit programma werkt niet correct
totaal:=0;
repeat
getal:=randomrange(1,11);
writeln(getal);
totaal:=totaal+getal;
until totaal=50;
Het probleem is dat je maar getallen bij elkaar blijft tellen en hoopt dat je op 50 uitkomt.
Dat is geen probleem, zolang het totaal kleiner dan 50 is. Maar als het totaal groter dan 50
geworden is, stopt het programma nooit meer...
We moeten dus controleren of het totaal niet te groot wordt. Alleen als het nieuwe totaal
(=totaal+getal) kleiner of gelijk aan 50 is, voegen we het getal toe
// dit programma werkt WEL correct
totaal:=0;
repeat
getal:=randomrange(1,11);
if totaal+getal<=50 then begin
writeln(getal);
totaal:=totaal+getal;
end;
until totaal=50;
We zorgen er dus voor dat het programma aan het eind getallen kiest die niet te groot zijn.
We schrijven de getallen meteen op het scherm, maar het programma weet achteraf niet
meer welke getallen er gekozen werden.
Dat kunnen we wel toevoegen aan het programma.
// dit programma onthoudt de gekozen getallen
totaal:=0;
aantal:=0;
repeat
getal:=randomrange(1,11);
if totaal+getal<=50 then begin
aantal:=aantal+1;
rij[aantal]:=getal;
totaal:=totaal+getal;
end;
until totaal=50;
89
Nu het programma de getallen onthoudt, kunt achteraf iets met de getallen doen. Je kunt
ze gewoon op het scherm schrijven, maar dat kon het eenvoudige programma ook al.
Nu je de getallen onthouden hebt, kun je de getallen ook eerst in volgorde zetten voor je
ze op het scherm schrijft.
// dit programma onthoudt de gekozen getallen
totaal:=0;
aantal:=0;
repeat
getal:=randomrange(1,11);
if totaal+getal<=50 then begin
aantal:=aantal+1;
rij[aantal]:=getal;
totaal:=totaal+getal;
end;
until totaal=50;
// sorteer oplopend
for I:=1 to aantal-1 do
for J:=I+1 to aantal do
if rij[I]>rij[J] then begin
T:=rij[I];
rij[I]:=rij[J];
rij[J]:=T;
end;
// schrijf de getallen op het scherm
for I:=1 to aantal do
writeln(rij[I]);
We hebben er eerder voor gezorgd dat het programma getallen kiest die niet te groot zijn.
Dat kan ook op een andere manier.
De functie min geeft het minimum van twee getallen (de laagste waarde):
function min(a, b:integer):integer;
begin
if a<b then
min:=a
else
min:=b;
end;
90
We kunnen de functie min gebruiken om het programma op een andere manier te
schijven.
// dit programma onthoudt de gekozen getallen
totaal:=0;
aantal:=0;
repeat
// hoeveel mag er nog maximaal bij (wat is over)?
over:=50-totaal;
// als er minder dan 10 bij mag, passen we de bovengrens aan
getal:=randomrange(1,min(over+1,11)) );
aantal:=aantal+1;
rij[aantal]:=getal;
totaal:=totaal+getal;
end;
until totaal=50;
We gaan het programma nu aanpassen, zodat het alleen getallen kiest uit een
verzameling die we vooraf bepalen. We willen bijvoorbeeld alleen de getallen (1,3,9 en 11
gebruiken).
We vullen eerst een array met de getallen die gebruikt mogen worden.
// vullen van een array met keuzes
keuze[1]:=1;
keuze[2]:=3;
keuze[3]:=9;
keuze[4]:=11;
aantalkeuzes:=4;
Bij het kiezen van een getal moeten we er nu voor zorgen dat een getal uit het array keuze
gekozen wordt.
// dit programma onthoudt de gekozen getallen
totaal:=0;
aantal:=0;
repeat
// kies een van de vier getallen
welke :=randomrange(1,5);
getal:=keuze[welke];
if totaal+getal<=50 then begin
aantal:=aantal+1;
rij[aantal]:=getal;
totaal:=totaal+getal;
end;
until totaal=50;
91
19
Vuur en vlam
Je kunt een cellulaire automaat ook gebruiken om vlammen te imiteren.
Omdat het berekenen van de kleuren in de vlam heel veel rekenwerk kost, laat het
voorbeeldprogramma in Small Basic een hele kleine vlam zien. We gebruiken daarvoor
een rechthoekig vlak met cellen.
In andere programmeertalen gaat het rekenwerk sneller. Een Lazarus-programma kun je
ook op de website vinden.
Je kunt de grootte van de vlam aanpassen voor de volgende variabelen een groter getal in
te vullen.
breedte=50
hoogte=50
Elke cel heeft een waarde, minimaal 0 maximaal 255.
Bij elk getal hoort een kleur. Er zijn dus 256 kleuren.
Bij het tekenen wordt een pallet (kleurtabel) gebruikt: 0 is zwart, 255 is wit. Daartussen
verloopt de kleur langzaam: van zwart → (32) → blauw → (32) → rood → (32) → geel →
(160) → wit
Tussen haakjes staat het aantal stappen dat voor het kleurverloop gebruikt wordt.
voorbeeld
Er worden 32 stapjes gebruik voor het kleurverloop van zwart naar blauw.
92
Je kunt de complete kleurentabel hieronder zien. Er staan 256 kleuren naast elkaar.
Waarschijnlijk herken je hierin de kleuren van een vlam al!
Sub MaakKleuren
For i=1 To 32
kleur[i]=GraphicsWindow.GetColorFromRGB(0,0,i*2)
kleur[i+32]=GraphicsWindow.GetColorFromRGB(i*8,0,64-(i*2))
kleur[i+64]=GraphicsWindow.GetColorFromRGB(255,i*8,0)
kleur[i+96]=GraphicsWindow.GetColorFromRGB(255,255,i*4)
kleur[i+128]=GraphicsWindow.GetColorFromRGB(255,255,64+i*2)
kleur[i+160]=GraphicsWindow.GetColorFromRGB(255,255,128+i*4)
kleur[i+192]=GraphicsWindow.GetColorFromRGB(255,255,192+i)
kleur[i+224]=GraphicsWindow.GetColorFromRGB(255,255,224+i)
endfor
endsub
De vlam is een animatie. Voor elk beeldje wordt voor elk pixel een nieuwe kleurwaarde
berekend.
De cellen van de vlam worden vanaf de tweede regel tot de op een na laatste regel van
links naar rechts doorlopen.
'bereken volgende stap van de vlam
For y=2 To hoogte-1
For x=1 To breedte
vuur[x][y-1]=Math.Round((vuur[x][y]+vuur[x][y-1]+vuur[x][y+1])/3)
graphicswindow.SetPixel(x,y,kleur[vuur[x][y]])
EndFor
EndFor
De volgende regel bepaalt de nieuwe kleur:
vuur[x][y-1]=Math.Round((vuur[x][y]+vuur[x][y-1]+vuur[x][y+1])/3)
De nieuwe kleurwaarde is het gemiddelde van de drie cellen die boven elkaar liggen.
Round zorgt dat het getal afgerond wordt.
vuur[x][y-1]
vuur[x][y]
vuur[x][y+1]
93
voorbeeld
Stel dat de drie kleurwaarden 100, 110 en 150 zijn.
Dan wordt de nieuwe kleurwaarde: 100+110+150 = 360 → 360/3 = 120.
We hoeven nu toevallig niet af te ronden.
100
110
150
Als we alleen dit zouden doen zou de vlam zwart blijven. Alle cellen hebben aan het begin
van het programma namelijk de waarde 0.
Daardoor is de waarde in de volgende stap ook weer 0 ( 0+0+0 = 0 → 0/3=0).
We lossen dat op door de vlam van onderaf aan te wakkeren. Precies zoals dat bij een
echte vlam ook gebeurt.
De cellen op de onderste regel worden willekeurig met witte en zwarte cellen te vullen. Dit
zijn de kleurwaarden 255 en 0.
'zorg dat er onderin de vlam vuur blijft om de vlam aan te wakkeren
v=255
For x=1 To breedte
r=math.GetRandomNumber(14)
If r>10 Then
If v=0 Then
v=255
Else
v=0
endif
endif
vuur[x][hoogte]=v
graphicswindow.SetPixel(x,y,kleur[vuur[x][y]])
endfor
94
Het is bij een animatie beter om het tekenen uit te stellen tot de hele afbeelding berekend
is.
Dat moet door aan het eind alle pixels van de volgende afbeelding te tekenen.
'teken de vlam
For y=1 To hoogte
For x=1 To breedte
graphicswindow.SetPixel(x,y,kleur[x][y])
vuur[x][y-1]=math.round(vuur[x][y]+vuur[x][y-1]+vuur[x][y+1])/3
EndFor
EndFor
Als je echter aandachtig gelezen hebt, heb je gezien dat bij het berekenen van de kleur en
bij het aanwakkeren van de vlam, meteen een pixels getekend wordt. Dat gebeurt in de
twee regels die vetgemaakt zijn.
We kleuren de pixels meteen, omdat het berekenen van de vlam zo lang duurt. We
hoeven ook niet te wachten tot 40ms voorbij is, omdat het berekenen van de volgende
afbeelding veel langer dan 40ms duurt.
Als je deze cellulaire automaat vergelijkt met de Life vallen een paar verschillen op.
Life
Vlammen
Start
Er worden in het begin een
paar cellen aangezet.
In elke ronde worden een
paar cellen op de onderste
regel willekeurig wit of zwart.
Berekenen nieuwe waarde
cel
Alle cellen rond de cel
worden gebruikt voor de
berekening.
Alleen de cellen boven en
onder de cel worden in de
berekening gebruikt.
Kleur op het scherm
Er zijn maar twee waarden
mogelijk 0=wit, 1=zwart
Er zijn 256 verschillende
kleurwaarden mogelijk. De
'vertaling' van een getal naar
een kleur gebeurt door
middel van een pallet (tabel
die aan een getal een
kleurcode koppelt).
95
Bijlagen
NRC, 27 november 2012
wie het eerst drukt, het eerst de lift
Jeroen de Jong ergert zich allang aan kantoren waar ’s ochtends alle liften helemaal van
de bovenste verdieping moeten komen, terwijl iedereen de werkdag beneden begint. Hij
onderzocht hoe liften hun route het best kunnen plannen, ervan uit gaande dat de route
tussentijds toch weer verandert. Woensdag 28 november promoveert De Jong, inmiddels
werkzaam bij radarfabrikant Thales, aan de TU Delft.
Hoe komt iemand terecht in de liftwiskunde?
„Ik ben zo iemand die zich opwindt over wachttijden bij stoplichten en liften. ‘Laat mij maar even achter de
knoppen’, denk ik dan. Na een studie Kunstmatige Intelligentie wilde ik iets praktisch doen, dus heb ik stage
gelopen bij de Zwitserse liftfabrikant Schindler. Daar is dit onderzoek uit voortgekomen.”
Hoe moeilijk kan het zijn? Je drukt op de knop, en de lift komt.
„Het is een extreem lastig probleem. Stel, jij drukt op de begane grond. Een lift van boven gaat onderweg.
Maar intussen willen zes mensen op de derde verdieping naar de tiende. De lift is daar dichterbij en zij zijn
met zijn zessen, dus het is efficiënter om eerst die mensen naar boven te brengen. Maar dan wil ook iemand
van de tiende omhoog. Dat zou ook weer efficiënter zijn, maar intussen sta jij dan al een onaanvaardbaar
lange tijd te wachten.
„Algoritmen in de liftbesturing, die vaak meerdere liften bedienen, moeten dus de beste route kiezen. Met
maar een paar liften en een paar wachtenden in een beetje kantoorgebouw explodeert het aantal mogelijke
routes al snel. Alle routes doorrekenen kost miljarden jaren, terwijl een lift in een seconde een beslissing
moet nemen.
„Bovendien is het ook nog eens een dynamisch probleem. Terwijl de lift bezig is, bestellen mensen nieuwe
ritten. Hoe meer je een oude taak uitstelt omdat het even niet handig is, hoe groter de kans is dat er in de
tussentijd nog weer iets bijkomt.”
Wat valt eraan te doen?
„Ik heb verschillende liftalgoritmen grondig vergeleken, en uitgetest op een databestand dat ik van Schindler
gekregen heb: elfduizend echte passagiersbewegingen op een drukke doordeweekse dag in een onbekend
gebouw in Parijs.
„Het blijkt toch te lonen om de planningshorizon zo kort mogelijk te houden, en tot op zekere hoogte eerst te
doen wat het eerst komt. Dat mag op de korte termijn wel eens onhandig uitpakken, zoals in het eerdere
voorbeeld, maar de kans dat er intussen iets lastigs tussendoorkomt neemt wel weer af.
„Nóg een optie is om op grond van eerdere ervaring te schatten hoe groot de kans op tussentijdse
veranderingen is, en daar dan ook weer rekening meer te houden. Dat scheelt zo’n drie procent in reis- en
wachttijd. Dat lijkt heel weinig, maar is best veel in de liftenwereld, waar het er echt om gaat het theezakje
helemaal uit te knijpen.”
Dus binnenkort werken alle liften zo?
„Nou, misschien nu al. De liftenbranche is een nogal gesloten wereld, waar een paar fabrikanten de dienst
uitmaken, en nauwelijks informatie naar buiten brengen. Zelfs om het gegevensbestand van mijn oude
stage-adres Schindler heb ik bijna een jaar moeten zeuren.
„Alle fabrikanten hebben flinke onderzoeksafdelingen, dus ik denk eerlijk gezegd dat ik dubbel werk heb
zitten doen. Maar voorzover ik kon nagaan is dit wel de eerste keer dat het ook openlijk beschreven is.”
Wat voor andere liftinnovaties kunnen we nog verwachten?
„Een systeem waarin je meteen je bestemmingsverdieping kiest, terwijl je liftnummer pas op het laatst
bekend wordt, kan nog eens 10 tot 15 procent wachttijd schelen. Zoiets kan vrij gemakkelijk ingevoerd
worden.
„En Schindler heeft een systeem aangekondigd waarbij liften ook opzij bewegen, met hulp van magnetische
motoren zoals magnetische zweeftreinen die ook gebruiken. Dan zou je, net als op een snelweg, de
binnenste schachten gebruiken voor de korte ritten, en de buitenste voor de snelle lange afstanden. Maar
het is nog wel afwachten of liftpassagiers opzij bewegen niet te eng vinden.”
96
Uitwerkingen
opdracht 1.1
Appeltaart [2]
1
Schil de appels en snijd ze in parten. Besprenkel ze met citroensap en bestrooi ze
met vanillesuiker en kaneel naar smaak. Vet de bakvorm in en verwarm de oven
voor op 175-180 graden (hetelucht 160).
2
Mix de roomboter op lage stand romig. Voeg de eieren en appeltaartmix toe en
meng voorzichtig. Mix het geheel dan 2 minuten op de hoogste stand.
3
Giet het beslag in de vorm en strijk het glad. Verdeel de appelparten er overheen
(zorg dat ze de rand niet raken!) en eventueel nog rozijnen of nootjes. Zet de vorm
onder het midden van de oven en bak de appeltaart in ca. 55 minuten. Controleer
daarna of de taart gaar is, haal hem meteen uit de vorm en laat hem afkoelen.
Lekker met een toef slagroom!
Bron: http://appeltaarten.web-log.nl/
97
opdracht 2.1
doen
opdracht 2.2
doen
opdracht 2.3
TextWindow.Writeline("We beginnen vanuit (0,0) te lopen over een rooster")
textwindow.writeline("Voer de bestemming (x,y) in.")
TextWindow.Write("Geef x:")
eindx=textwindow.ReadNumber()
TextWindow.Write("Geef y:")
eindy=textwindow.ReadNumber()
' omdat we met stappen van lengte 10 lopen nemen we 10*x en 10*y als eindpunt
eindx=eindx*10
eindy=eindy*10
GraphicsWindow.width=300
GraphicsWindow.height=300
' markeer de bestemming met een stip en een rondje
GraphicsWindow.DrawEllipse(100+eindx-3,100+eindy-3,7,7)
GraphicsWindow.SetPixel(100+eindx,100+eindy,"red")
stappen=0
x=0
y=0
While "true"
hoek=Math.GetRandomNumber(4)*90
'math.round is nodig om de hoeken op hele getallen af te ronden.
nieuwx=x+Math.Round(10*math.Cos(Math.GetRadians(hoek)))
nieuwy=y+Math.Round(10*math.Sin(Math.GetRadians(hoek)))
GraphicsWindow.title=math.round(nieuwx)+","+math.round(nieuwy)
GraphicsWindow.DrawLine(100+x,100+y,100+nieuwx,100+nieuwy)
x=nieuwx
y=nieuwy
stappen=stappen+1
98
If (x=eindx) And (y=eindy) Then
GraphicsWindow.ShowMessage("klaar in "+stappen+" stappen","")
Program.End()
endif
Program.Delay(1)
endwhile
opdracht 3.1
Voor jaar=2014 vind je de volgende waarden:
G=1
C=21
X=3
Y=1
Z=2504
E=29
N=45
P=51
P>31, dus Pasen valt op 51-31=20 april 2014
opdracht 3.2
TextWindow.WriteLine("Bereken de Paasdatum met de methode van Gauss")
TextWindow.WriteLine("=============================================")
TextWindow.Write("Voor welk jaar wil je de datum van Pasen berekenen? ")
jaar=textwindow.ReadNumber()
'gulden getal
g=math.Remainder(jaar,19)+1
TextWindow.WriteLine("g="+g)
'eeuwdeel
c=math.floor(jaar/100)+1
TextWindow.WriteLine("c="+c)
'correctie voor schrikkeljaren
x=math.Floor(c*3/4)-12
TextWindow.WriteLine("x="+x)
'maancorrectie
y=(c*8+5)/25 - 5
'LET OP: hier moet blijkbaar omlaag afgerond worden
'anders komt er in 1991 1,6 uit
99
y=math.Floor(y)
TextWindow.WriteLine("y="+y)
'zoek de zondag
z=math.Floor(jaar*5/4)-x-10
TextWindow.WriteLine("z="+z)
'epacta
e=Math.remainder(11*g+20+y-x,30)
TextWindow.WriteLine("e="+e)
'volle maan
n=44-e
If n<21 Then
n=n+30
endif
TextWindow.WriteLine("n="+n)
'bepaal de zondag na de volle maan
p=n+7-math.Remainder(z+n,7)
TextWindow.WriteLine("p="+p)
'bepaal de datum
If p>31 Then
p=p-31
TextWindow.WriteLine("Pasen valt op "+p+" april in "+jaar)
Else
TextWindow.WriteLine("Pasen valt op "+p+" maart in "+jaar)
endif
100
opdracht 4.1
inleveropdracht voor een micropunt
opdracht 4.2
'schudden
TextWindow.Writeline("Demonstratieprogramma: Schudden")
'zet de getallen 1 tot en met 52 in een array
for teller=1 To 52
kaart[teller]=teller
endfor
'lees in hoe vaak de kaarten verwissel moeten worden
TextWindow.Write("Hoevaak moeten de kaarten verwisseld worden? ")
aantalverwisselingen=textwindow.ReadNumber()
'we verwisselen aantalverwisselingen keer twee willekeurige kaarten
For teller=1 To aantalverwisselingen
'kies twee willekeurige kaarten
'let op: dit kan toevallig 2x dezelfde kaart zijn!
x=math.getrandomnumber(52)
y=math.getrandomnumber(52)
'verwissel de twee kaarten
temp=kaart[x]
kaart[x]=kaart[y]
kaart[y]=temp
endfor
opdracht 4.3
Voeg bijvoorbeeld de volgende code na de code van 3.2 in.
'4.3
'tellen hoeveel kaarten niet verwisseld zijn
aantal=0
For teller=1 To 52
'als de kaart niet op een andere plek staat maken we 'aantal' één hoger
If kaart[teller]=teller Then
aantal=aantal+1
EndIf
endfor
TextWindow.WriteLine("Er staan nog "+aantal+" kaarten op dezelfde plek")
TextWindow.WriteLine("Het aantal kaarten dat niet verplaatst werd is:"+aantal+" (dat is
"+aantal*100/52+"%)")
101
opdracht 5.1
doen: micropunt voor een video-opname
opdracht 5.2
inleveropdracht: micropunt
opdracht 5.3
Na het aanklikken van (1,1) staat alleen deze cel in de lijst
L
B
De lijst bevat (1,1).
We nemen (1,1) uit de lijst
De aangrenzende cellen van (1,1) bevatten 0 bommen.
We zetten (1,2), (2,2) en (2,1) in de lijst.
(1,1) markeren we met een 0.
0
L
L
L
B
De lijst bevat (1,2), (2,2), (2,1)
We nemen (2,1) uit de lijst
De aangrenzende cellen van (2,1) bevatten 0 bommen.
We zetten (3,1), (3,2) en (2,1) in de lijst.
(2,1) markeren we met een 0.
0
0
L
L
L
L
B
De lijst bevat (1,2), (2,2), (3,1), (3,2)
We nemen (3,2) uit de lijst
De aangrenzende cellen van (3,2) bevatten 1 bom
(3,2) markeren we met een 1.
102
0
0
L
L
L
1
B
De lijst bevat (1,2), (2,2), (3,1)
We nemen (3,1) uit de lijst
De aangrenzende cellen van (3,1) bevatten 0 bommen
(3,1) markeren we met een 0.
0
0
0
L
L
1
B
De lijst bevat (1,2), (2,2)
We nemen (2,2) uit de lijst
De aangrenzende cellen van (2,2) bevatten 1 bom
(2,2) markeren we met een 1.
0
0
0
L
1
1
B
De lijst bevat (1,2)
We nemen (1,2) uit de lijst
De aangrenzende cellen van (1,2) bevatten 0 bommen
We zetten (1,3) en (2,3) in de lijst.
(1,2) markeren we met een 0.
0
0
0
0
1
1
L
L
B
De lijst bevat (1,3) en (2,3)
We nemen (2,3) uit de lijst
De aangrenzende cellen van (2,3) bevatten 1 bom
(2,3) markeren we met een 1.
103
0
0
0
0
1
1
L
1
B
De lijst bevat (1,3)
We nemen (1,3) uit de lijst
De aangrenzende cellen van (1,3) bevatten 0 bommen
Er zijn geen aangrenzende cellen van (1,3) meer die niet eerder aan de beurt
kwamen.
(1,3) markeren we met een 0.
0
0
0
0
1
1
0
1
B
De lijst is nu leeg. Daarom kunnen we stoppen
opdracht 5.4
a (1,2), (2,1) en (2,2)
b
0
1
0
2
0
1
1
2
B
B
B
B
B
opdracht 5.5
Toon jouw werkende code voor een micropunt. De code van de docent krijg je tijdens de
les waarop je deze moest inleveren.
104
opdracht 6.1
Aan het begin is de buffer gevuld met: 10,35,40,42,45,49,50,51,65
De lift begint op de 50e etage.
Resterende buffer: 10,35,40,42,45,49,50,51,65
49 en 51 zijn even ver. Kies willekeurig een etage. We kiezen 49.
Resterende buffer: 10,35,40,42,45,51,65
De dichtstbijzijnde etage is 51.
Resterende buffer: 10,35,40,42,45,65
De dichtstbijzijnde etage is 45.
Resterende buffer: 10,35,40,42,65
De dichtstbijzijnde etage is 42.
Resterende buffer: 10,35,40,65
De dichtstbijzijnde etage is 40.
Resterende buffer: 10,35,65
De dichtstbijzijnde etage is 35.
Resterende buffer: 10,65
De dichtstbijzijnde etage is 10.
Resterende buffer: 65
Er is nog maar één verzoek in de buffer.
Dus reizen we nu naar etage 65.
Nu is de buffer leeg.
Ook als we in eerst naar etage 51 waren gegaan, was etage 65 pas als laatste aan de
beurt gekomen.
We kunnen dit lange verhaal korter opschrijven door in de eerste regel van een tabel de
buffer te noteren in de tweede regel noteren we de volgorde.
buffer
10
volgorde 8
35
40
42
45
49
50
51
65
7
6
5
4
2
1
3
9
De enige alternatieve volgorde is:
buffer
10
volgorde 8
35
40
42
45
49
50
51
65
7
6
5
4
3
1
2
9
opdracht 6.2
In de buffer zit 10,65. De lift staat op etage 50.
De dichtstbijzijnde etage is dus 65. Terwijl de lift naar boven gaat druk iemand op de etage
45.
Er staat dan 10,45,65 Als de lift op de 65e etage geweest is staat er 10,45 in de buffer.
De lift reist naar etage 45. Ondertussen drukt iemand op etage 65. Er staat dan weer
10,45, 65 in de buffer. Als de lift op etage 45 is geweest staat er 10,65 in de buffer. Dus
reist de lift weer naar etage 65. Op deze manier kan etage 10 eeuwig in de buffer blijven
staan.
105
Opdracht 6.3
De lift beweegt vanuit etage 80 omhoog.
Eerst beweegt de lift naar etage 90. In de buffer staat dan nog: 10,30,50,70.
Daarna beweegt de lift naar etage 100, ook al staat die niet in de buffer. Nu draait de
richting van de lift om. Hij gaat nu naar beneden.
De resterende etage worden nu in de volgende volgorde afgehandeld: 70,50,30,10.
Met een tabel is dit ook overzichtelijk aan te geven. Omdat SCAN ook de onderste en
bovenste etage aan kan doen moet je deze ook in de tabel opnemen.
De asterisk bij etage 100 geeft aan de dat lift hier niet stopt.
etage
0
10
30
50
70
90
100
volgorde
niet
6
5
4
3
1
2*
Opdracht 6.4
De lift handelt altijd eerst de verzoeken in één richting af. Als er dan nog verzoeken over
zijn keert de lift om (via de bovenste of onderste etage). Elke etage komt dus een keer aan
de beurt.
Niemand staat dus eeuwig te wachten.
Opdracht 6.5
Eerst sorteren we de buffer: 10,20,50,75,90
De lift begint op etage 35 en handelt alleen in opgaande richting verzoeken af.
50,75,90,100,0,10,20. De honderd en nul horen erbij omdat C-SCAN altijd doorgaat tot de
uiterste etages.
We bewegen dus eerst van 35 → 100: 65 etages.
Dan naar de begane grond 100 → 0: 100 etages
Dan 0 → 20 : 20 etages
In totaal zijn er dus 65+100+20=173 etages bezocht. Dat is 175/5 = 34,8 etages per
verzoek.
C-LOOK zou dat zuiniger doen: 50,75,90,10,20
We bewegen dus eerst van 35 → 90: 55 etages.
Dan naar de begane grond 90 → 10: 80 etages
Dan 10 → 20 : 10 etages
samen: 55+80+10 = 145 etage. Dat is 29 etages per verzoek.
106
Opdracht 6.6
inleveropdracht voor een micropunt
Opdracht 6.7
inleveropdracht voor een micropunt
opdracht 7.1
GraphicsWindow.Show()
' teken een rooster
for waarde=0 To 100 step 10
GraphicsWindow.DrawLine(10,10+waarde,110,10+waarde)
GraphicsWindow.DrawLine(10+waarde,10,10+waarde,110)
endfor
' veeg nu willekeurige lijnstukjes uit binnen het rooster
For teller=1 To 200
' kies een richting: 1=horizontaal, 2=verticaal
richting=math.GetRandomNumber(2)
' kies een x en y waar het lijnstuk begint
x=(math.GetRandomNumber(10))*10
y=(math.GetRandomNumber(10))*10
' teken een witte lijn van x,y in de gekozen richting
GraphicsWindow.PenColor="white"
If richting=1 Then
GraphicsWindow.DrawLine(x,y,x+10,y)
Else
GraphicsWindow.DrawLine(x,y,x,y+10)
endif
endfor
opdracht 7.2
inleveropdracht voor een micropunt
opdracht 7.3
Dit doolhof kan toevallig het resultaat zijn van het slechte algoritme.
Het kan niet het resultaat zijn van random divide. Random divide maakt immers altijd 3
openingen in de vier scheidingsmuren. Daardoor kunnen er niet twee afgesloten gebieden
107
ontstaan zoals in het getekende doolhof.
Opdracht 8.1
Eerst kijken we welke cellen er geboren worden:
B
Daarna zoeken we cellen die sterven en overleven
S
-
-
-
S
-
B
S
-
Het plaatje wordt dus:
S
-
-
-
S
-
B
S
-
Opdracht 8.2
108
Opdracht 8.3
In het speelveld zijn de randen niet doorverbonden met de andere kant.
x
x
x
x
x
x
x
x
x
x
x
Opdracht 8.4
De randen zijn doorverbonden met de andere kant van het speelveld.
Daardoor zijn in dit kleine speelveld alle andere cellen een buur van de zwarte cel.
x
x
x
x
x
x
x
x
opdracht 8.5
inleveropdracht voor een micropunt
Opdracht 9.1
Je staat op de plek die met het kruisje gemarkeerd is.
Volg de linker muur naar de uitgang.
109
Opdracht 9.2
Je staat op de plek die met het kruisje gemarkeerd is.
Volg de rechter muur naar de uitgang.
Opdracht 9.3
Dat is afhankelijk van de kijkrichting.
Als de neus naar links staat, is de onderste lijn de linker muur.
opdracht 9.4
Je blijft steeds de binnenmuren volgen. Je komt nooit bij de deur waarmee je het doolhof
kunt verlaten.
Opdracht 9.5
Bij een kruispunt gaat de automaat eerst rechtdoor. Als de agent later terugkomt gaat hij
links (gezien vanuit de oorspronkelijke aanlooprichting) en tenslotte rechts.
110
Opdracht 9.6
Bekijk het onderstaande doolhof. De zwarte cellen zijn muren.
Je begint in S. De uitgang ligt bij U.
Op een kruispunt wordt de richtingen in de volgende volgorde doorlopen: Links,
Rechtdoor, Rechts.
In welke volgorde worden de cellen 1,2, 3, 4, 5 en 6 bezocht door de cellulaire automaat?
Noteer de volgorde.
Als een cel meer dan 1 keer bezocht wordt moet je het nummer van die cel bij elk bezoek
opschrijven.
De met cijfers gemarkeerde cellen worden in deze volgorde bezocht: 2 1 2 4 5 6. 3 wordt
niet bezocht omdat na 6 al de uitgang gevonden wordt.
Opdracht 9.7
inleveropdracht voor maximaal drie micropunten
Opdracht 9.8
inleveropdracht
111
Opdracht 10.1
Neem de onderstaande tabel over en bereken zoals in het voorbeeld de Levenshteinafstand tussen de woorden PAAS en HAAS. Dit voorbeeld is zo eenvoudig dat je vooraf al
kunt zien dat de afstand 1 is, maar het gaat om het correct toepassen van het algoritme.
H
A
A
S
0
1
2
3
4
P
1
1
2
3
4
A
2
2
1
2
3
A
3
3
2
1
2
S
4
4
3
2
1
De cel rechtsonder bevat de Levenshtein-afstand: 1
Er is inderdaad één wijziging nodig: de P verandert in een H.
Opdracht 10.2
Neem de onderstaande tabel over en bereken zoals in het voorbeeld de Levenshteinafstand tussen de woorden HAZEN en HAGEL.
H
A
G
E
L
0
1
2
3
4
5
H
1
0
1
2
3
4
A
2
1
0
1
2
3
Z
3
2
1
1
2
3
E
4
3
2
2
1
2
N
5
4
3
3
2
2
De cel rechtsonder bevat de Levenshtein-afstand: 2.
Dat is ook wel te begrijpen. Als je van HAZEN het woord HAGEL wilt maken, vervang je de
Z door een G en de N door een L. Dat zijn twee wijzigingen.
112
Opdracht 10.3
TextWindow.WriteLine("Levenshtein DEMO")
TextWindow.WriteLine("")
' lees eerste de twee woorden in
TextWindow.Write("Voer a.u.b. het eerste woord in:")
woord1=textwindow.Read()
TextWindow.Write("Voer a.u.b. het tweede woord in:")
woord2=textwindow.Read()
' zet de tekens van het eerste woord in de eerste rij
For i=1 To Text.getlength(woord1)
d[i][0]=i
endfor
' zet de tekens van het tweede woord in de eerste kolom
For i=1 To Text.getlength(woord2)
d[0][i]=i
endfor
For i=0 To Text.GetLength(woord1)
For j=0 To Text.GetLength(woord2)
' bepaal de kosten
If Text.GetSubText(woord1,i,1)=Text.GetSubText(woord2,j,1) Then
cost=0
else
cost=1
endif
'bepaal het minimum voor d[i][j], dat gaat een beetje omslachtig
min=d[i-1][j]+1 'wissen van een letter
If d[i][j-1]+1<min Then
min=d[i][j-1]+1 'toevoegen van een letter
EndIf
If d[i-1][j-1]+cost<min Then
min=d[i-1][j-1]+cost 'wijzigen van een letter
EndIf
d[i][j]=min
endfor
endfor
' Zet de tabel d nu op het scherm
TextWindow.Write(" ")
TextWindow.WriteLine(woord2)
For i=0 To Text.GetLength(woord1)
if i=0 then
TextWindow.Write(" ")
else
TextWindow.Write(Text.GetSubText(woord1,i,1))
EndIf
For j=0 To Text.GetLength(woord2)
TextWindow.Write(d[i][j])
EndFor
TextWindow.WriteLine("")
EndFor
113
Opdracht 10.4
De kern van het programma blijft hetzelfde. Dat de tekst uit CTAG en vraagtekens bestaat
is niet anders dan dat de tekst alleen uit letters bestaat.
De enige aanpassing is nu dat er in plaats van invoer voor een willekeurige reeks van
CTAG en vraagtekens gezorgd wordt.
In deze code heb ik ervoor gekozen om de DNA reeks maximaal 10 tekens te laten zijn.
Als je veel langere reeksen maakt kan de afstand groter dan 10 worden. Je hebt dan 2
plaatsen in de tabel nodig per cel.
TextWindow.WriteLine("Levenshtein DNA DEMO")
TextWindow.WriteLine("")
' maak twee willekeurige strings met CTAG?
maakDNA()
woord1=s
maakDNA()
woord2=s
TextWindow.WriteLine("De twee DNA reeksen die vergeleken worden zijn:")
TextWindow.Writeline("Reeks 1: "+woord1)
TextWindow.Writeline("Reeks 2: "+woord2)
' zet de tekens van het eerste woord in de eerste rij
For i=1 To Text.getlength(woord1)
d[i][0]=i
endfor
' zet de tekens van het tweede woord in de eerste kolom
For i=1 To Text.getlength(woord2)
d[0][i]=i
endfor
For i=0 To Text.GetLength(woord1)
For j=0 To Text.GetLength(woord2)
' bepaal de kosten
If Text.GetSubText(woord1,i,1)=Text.GetSubText(woord2,j,1) Then
cost=0
else
cost=1
endif
'bepaal het minimum voor d[i][j], dat gaat een beetje omslachtig
min=d[i-1][j]+1
If d[i][j-1]+1<min Then
min=d[i][j-1]+1
EndIf
If d[i-1][j-1]+cost<min Then
min=d[i-1][j-1]+cost
EndIf
d[i][j]=min
endfor
endfor
' Zet de tabel d nu op het scherm
114
TextWindow.Write(" ")
TextWindow.WriteLine(woord2)
For i=0 To Text.GetLength(woord1)
if i=0 then
TextWindow.Write(" ")
else
TextWindow.Write(Text.GetSubText(woord1,i,1))
EndIf
For j=0 To Text.GetLength(woord2)
TextWindow.Write(d[i][j])
EndFor
TextWindow.WriteLine("")
EndFor
'deze routine maakt een sting met CTAG? en met deze in s
Sub maakDNA
s=""
t="CTGA?"
For teller=1 To 10
x=Math.GetRandomNumber(5)
s=s+Text.GetSubText(t,x,1)
EndFor
endsub
115
opdracht 11.1
'schudden
TextWindow.Writeline("Demonstratieprogramma: Schudden")
'zet de getallen 1 tot en met 52 in een array
for teller=1 To 52
kaart[teller]=teller
endfor
aantalopplek=52
aantalopeenvolgend=51
While aantalopplek>0 or aantalopeenvolgend>0
'we verwisselen aantalverwisselingen keer twee willekeurige kaarten
For teller=1 To 52
'kies een willekeurige kaart
'ga hiermee door tot x ongelijk aan teller is
'maak x gelijk aan teller, dan moeten we zeker nog een keer een kaart
kiezen
x=teller
while x=teller
x=math.getrandomnumber(52)
endwhile
'verwissel de twee kaarten
temp=kaart[teller]
kaart[teller]=kaart[x]
kaart[x]=temp
endfor
'tellen hoeveel kaarten niet verwisseld zijn
aantalopplek=0
For teller=1 To 52
'als de kaart niet op een andere plek staat maken we 'aantalopplek' één
hoger
If kaart[teller]=teller Then
aantalopplek=aantalopplek+1
EndIf
endfor
'tellen hoeveel kaarten naast een buur staan
'we beginnen bij 2
aantalopeenvolgend=0
For teller=2 To 52
'als de kaart niet op een andere plek staat maken we 'aantalopplek' één
hoger
verschil=kaart[teller]-kaart[teller-1]
If (verschil=1) or (verschil=-1) Then
aantalopeenvolgend=aantalopeenvolgend+1
EndIf
endfor
endwhile
' schrijf de lijst ter controle
116
For teller=1 To 52
TextWindow.Write(kaart[teller]+",")
EndFor
TextWindow.WriteLine("")
117
Opdracht 12.1
inleveropdracht voor een micropunt
Opdracht 12.2
' PRIEM1
TextWindow.WriteLine("PRIEM 1: onderzoek van een getal of het priem is")
TextWindow.Write("Welk getal wil je onderzoeken:")
getal=textwindow.ReadNumber()
starttijd=Clock.ElapsedMilliseconds
aantaldelers=2
For deler=2 To getal-1
rest=math.Remainder(getal,deler)
If rest=0 Then
aantaldelers=aantaldelers+1
endif
endfor
eindtijd=Clock.ElapsedMilliseconds
TextWindow.WriteLine("De verstreken tijd is:"+(eindtijdstarttijd)/1000+"seconde")
If aantaldelers=2 Then
TextWindow.WriteLine(getal+" is priem")
Else
TextWindow.WriteLine(getal+" is NIET priem")
endif
118
Opdracht 12.3
36
144
256
2x18 → 2
3x12 → 3
4x9 → 4
6x6 → 6
9x4 –> 4
12x3 → 3
18x2 → 2
2x72
3x48
6x24
9x16
12x12
16x9
24x6
48x3
72x2
…
…
16x16
…
...
De grootste van alle kleinste delers zijn dus respectievelijk: 6, 12, 16.
Dit is steeds de wortel van het getal.
De grootste van alle kleinste deler van 110 is 10. Dat is niet exact de wortel van 110, maar
wel het grootste gehele getal dat kleiner (of gelijk aan) de wortel van 110 is.
Opdracht 12.4
Vervang de regel:
For deler=2 To getal-1
door
For deler=2 To Math.SquareRoot(getal)
Opdracht 12.5
Hieronder staan de tijden in seconden
10
100
1000
10000
100000
1000000
10000000
PRIEM1
0
0
0
0,08
0,45
4,2
41,48
PRIEM2
0
0
0
0
0
0
0,05
Bij PRIEM2 gaat berekening zelfs voor grote getallen al zo snel dat we pas bij 10 miljoen
een enigszins meetbare vertraging kunnen meten. Voor 1 miljard had de door mij
gebruikte computer 0,2 seconde nodig.
119
opdracht 12.5
De 25 getallen staan in de tabel.
De wortel van 25 is exact 5.
Streep 1 af.
2 is het eerste priemgetal, de tweevouden erna strepen we af.
3 is het volgende priemgetal, de drievouden erna strepen we af.
5 is nog niet groter dan de wortel. 5 is dus het volgende priemgetal. De vijfvouden erna
strepen we af.
De getallen groter dan 5 die nu nog in de tabel staan zijn ook priem: 7,11,13,17,19,23.
2
3
5
7
11
13
17
19
23
Opdracht 12.6
' PRIEM3
TextWindow.WriteLine("PRIEM 3: onderzoek van een reeks getallen of ze priem
zijn")
start:
TextWindow.Write("startgetal:")
startgetal=textwindow.ReadNumber()
TextWindow.Write("eindgetal:")
eindgetal=textwindow.ReadNumber()
starttijd=Clock.ElapsedMilliseconds
For getal=startgetal To eindgetal
aantaldelers=2
For deler=2 To Math.SquareRoot(getal)
rest=math.Remainder(getal,deler)
If rest=0 Then
aantaldelers=aantaldelers+1
endif
endfor
If aantaldelers=2 Then
TextWindow.WriteLine(getal+" is priem")
Else
TextWindow.WriteLine(getal+" is NIET priem")
endif
120
endfor
eindtijd=Clock.ElapsedMilliseconds
TextWindow.WriteLine("De totale verstreken tijd is:"+(eindtijdstarttijd)/1000+"seconde")
TextWindow.WriteLine("De verstreken tijd per getal is:"+(eindtijdstarttijd)/((eindgetal-startgetal)*1000)+"seconde")
Op de website staat een tweede versie van dit programma dat zo aangepast is dat ze
alleen het aantal getallen telt.
121
Opdracht 12.7
' PRIEM4
TextWindow.WriteLine("PRIEM 4: De zeef van Erathostenes")
TextWindow.Write("Tot welk getal wil je zoeken:")
eindgetal=textwindow.ReadNumber()
TextWindow.WriteLine("Klaarzetten van alle getallen.")
'maak een rij getallen klaar
' dat dit zo lang duurt ligt aan small basic...
rij[1]=0
For teller=2 To eindgetal
rij[teller]=1
TextWindow.CursorLeft=1
TextWindow.Write(teller)
endfor
TextWindow.WriteLine("Alle getallen staan klaar, begin met zeven")
'we beginnen pas te tellen als alle getallen klaar staan.
starttijd=Clock.ElapsedMilliseconds
aantalpriemgetallen=0
getal=2
While getal<math.SquareRoot(eindgetal)
'zoek het eerste vrije getal
While rij[getal]=0
getal=getal+1
endwhile
TextWindow.Write(getal+", ")
' getal is nu het eerste vrije getal, dat is priem
aantalpriemgetallen=aantalpriemgetallen+1
'streep alle veelvouden weg
weg=getal+getal
While weg<=eindgetal
rij[weg]=0
weg=weg+getal
EndWhile
getal=getal+1
EndWhile
'tel nog alle vrije getallen
while getal<=eindgetal
If rij[getal]=1 Then
aantalpriemgetallen=aantalpriemgetallen+1
endif
getal=getal+1
EndWhile
TextWindow.WriteLine("Klaar met zeven")
eindtijd=Clock.ElapsedMilliseconds
122
TextWindow.WriteLine("De verstreken tijd is:"+(eindtijdstarttijd)/1000+"seconde")
TextWindow.WriteLine("Het aantal gevonden priemgetallen
is:"+aantalpriemgetallen)
TextWindow.WriteLine("Hier volgen alle gevonden priemgetallen:")
For teller=1 To eindgetal
If rij[teller]=1 Then
TextWindow.Write(teller+", ")
endif
endfor
opdracht 13.1
opdracht 13.2
opdracht 13.3
inleveropdracht
opdracht 13.4
Het algoritme moet zijn 'inhaligheid' bekopen door later onnodig een punt verderop te
moeten kiezen, omdat de punten dichtbij al aan de beurt zijn geweest.
opdracht 13.5
Uitwerking TS
' zet de punten in een lijst
GraphicsWindow.width=450
GraphicsWindow.height=450
aantal=10
For teller=1 To aantal
x[teller]=math.GetRandomNumber(400)
y[teller]=math.GetRandomNumber(400)
alinroute[teller]=0
GraphicsWindow.DrawEllipse(x[teller]-1,y[teller]-1,3,3)
endfor
x[aantal+1]=x[1]
y[aantal+1]=y[1]
totaleafstand=0
aantalpunteninroute=1
punt[1]=1
laatstepunt=1
While aantalpunteninroute<=aantal
minafstand=99999999999
For teller =1 to aantal
dx=x[teller]-x[laatstepunt]
dy=y[teller]-y[laatstepunt]
123
afstand=math.SquareRoot(dx*dx+dy*dy)
If (afstand<minafstand) and (alinroute[teller]=0) Then
minafstand=afstand
dichtstbijzijndepunt=teller
endif
endfor
totaleafstand=totaleafstand+minafstand
GraphicsWindow.DrawLine(x[laatstepunt],y[laatstepunt],x[dichtstbijzijndepun
t],y[dichtstbijzijndepunt])
aantalpunteninroute=aantalpunteninroute+1
punt[aantalpunteninroute]=dichtstbijzijndepunt
laatstepunt=dichtstbijzijndepunt
alinroute[dichtstbijzijndepunt]=1
Program.Delay(10)
endwhile
GraphicsWindow.DrawLine(x[laatstepunt],y[laatstepunt],x[1],y[1])
GraphicsWindow.DrawBoundText(10,410,450,totaleafstand)
opdracht 13.6
In de gevonden oplossing zitten kruisende wegen. Met rood is hieronder aangegeven hoe
de kortere route loopt.
opdracht 14.1
doen
opdracht 14.2
Als uitwerking het volgende voorbeeld:
We nemen als getal 12345 en als startwaarde voor de deler 1.
Met het voorbeeld-programma kunnen we de stappen snel controleren.
124
opdracht 14.3
geen standaard antwoord
opdracht 15.1
Persoon A stopt het mes al persoon B of persoon C 'stop' roept. Neem aan dat
persoon B 'stop' roept. Persoon B is dan tevreden, hij riep immers 'stop'. Persoon C
is ook tevreden, omdat hij van plan was om later 'stop' te roepen. Het deel dat
overblijft – nadat persoon A zijn deel afgesneden heeft – is groter dan persoon C in
gedachten had.
Opdracht 16.1
8 9 14 16 11 12 13 10 20 1 2 3 17 18 19 4 7 5 6 15
8 9 14 16 11 12 13 10 20 1 2 3 17 18 19 4 7 5 6 [15]
8 9 14 11 12 13 10 1 2 3 4 7 5 6 [15] 16 20 17 18 19
8 9 14 11 12 13 10 1 2 3 4 7 5 [6] [15] 16 20 17 18 [19]
1 2 3 4 5 [6] 8 9 14 11 12 13 10 7 [15] 16 17 18 [19] 20
maak de resterende stappen zelf af.
Opdracht 16.2
geen standaard antwoord
125
opdracht 16.3
Als de rij aflopende gesorteerd is, bijvoorbeeld:
54321
opdracht 16.4
doen
126
BIJLAGE: Levenshtein
Deze bijlage is geen onderdeel van de toetsstof
Het algoritme van Levenshtein geschreven in Java:
public class Distance {
//****************************
// Get minimum of three values
//****************************
private int Minimum (int a, int b, int c) {
int mi;
mi = a;
if (b < mi) {
mi = b;
}
if (c < mi) {
mi = c;
}
return mi;
}
//*****************************
// Compute Levenshtein distance
//*****************************
public int LD (String s, String t) {
int d[][]; // matrix
int n; // length of s
int m; // length of t
int i; // iterates through s
int j; // iterates through t
char s_i; // ith character of s
char t_j; // jth character of t
int cost; // cost
// Step 1
n = s.length ();
m = t.length ();
if (n == 0) {
return m;
}
if (m == 0) {
return n;
}
d = new int[n+1][m+1];
// Step 2
for (i = 0; i <= n; i++) {
d[i][0] = i;
}
for (j = 0; j <= m; j++) {
d[0][j] = j;
}
127
// Step 3
for (i = 1; i <= n; i++) {
s_i = s.charAt (i - 1);
// Step 4
for (j = 1; j <= m; j++) {
t_j = t.charAt (j - 1);
// Step 5
if (s_i == t_j) {
cost = 0;
}
else {
cost = 1;
}
// Step 6
d[i][j] = Minimum (d[i-1][j]+1, d[i][j-1]+1, d[i-1][j-1] + cost);
}
}
}
// Step 7
return d[n][m];
}
128
BIJLAGE: een ander algoritme om goede doolhoven te maken
Deze bijlage is geen onderdeel van de toetsstof
create a CellStack (LIFO) to hold a list of cell locations
set TotalCells = number of cells in grid
choose a cell at random and call it CurrentCell
set VisitedCells = 1
while VisitedCells < TotalCells
find all neighbors of CurrentCell with all walls intact
if one or more found
choose one at random
knock down the wall between it and CurrentCell
push CurrentCell location on the CellStack
make the new cell CurrentCell
add 1 to VisitedCells
else
pop the most recent cell entry off the CellStack
make it CurrentCell
endIf
endWhile
129
TODO's
Langton's mier
https://www.youtube.com/watch?v=1X-gtr4pEBU
Hoe algoritmes onze wereld bepalen
https://www.ted.com/talks/kevin_slavin_how_algorithms_shape_our_world?language=nl
artikel volkskrant
https://www.volkskrant.nl/nieuws-achtergrond/wie-is-er-bang-voor-hetalgoritme~b06c4c02/
artikelenserie NRC
steen papier schaar automaat
https://www.youtube.com/watch?v=M4cV0nCIZoc
http://www.gamedev.net/blog/844/entry-2249737-another-cellular-automaton-video/
https://www.mediawijsheid.nl/video/zo-bepalen-algoritmes-jouw-wereldbeeld/
pancakesort
https://www.youtube.com/watch?v=oDzauRFiWFU
boids
https://www.youtube.com/watch?v=M028vafB0l8
eerlijk delen (zie jaar van het algoritme)
cirkel tekenen met lossen pixels
toevalsgetallen (al klaar)
verbeteringen van TS (punten verwisselen en tabu-search)
hashing
weg zoeken in doolhof (observable gamma=1, partial observable breadth first, clusteren
steden
130
131
Download