Ga naar hoofdinhoud

Bouw zelf — vergelijk lineair en binair

Leerdoel: je zet beide algoritmes naast elkaar op dezelfde data en telt het werk. De theorie wordt nu meetbaar.

Opdracht

Schrijf beide algoritmes zo dat ze het aantal vergelijkingen tellen. De startcode laat ze los op gesorteerde lijsten van 10 tot 100.000 getallen, in het slechtste geval: het doel komt niet voor.

Python
Code-omgeving wordt voorbereid…
Tip

Voor lineair_tellen: doorloop de lijst, hoog elke ronde een teller op, returnt aan het eind die teller — ook als je niets vond.

Voor binair_tellen: kopieer binair_zoek en vervang de return midden en return -1 door return vergelijkingen. Hoog vergelijkingen op binnen de while-lus.

Antwoord
def lineair_tellen(lijst, doel):
vergelijkingen = 0
for waarde in lijst:
vergelijkingen += 1
if waarde == doel:
return vergelijkingen
return vergelijkingen

def binair_tellen(lijst, doel):
laag = 0
hoog = len(lijst) - 1
vergelijkingen = 0
while laag <= hoog:
vergelijkingen += 1
midden = (laag + hoog) // 2
if lijst[midden] == doel:
return vergelijkingen
elif lijst[midden] < doel:
laag = midden + 1
else:
hoog = midden - 1
return vergelijkingen

print(lineair_tellen(list(range(1000)), -1)) # 1000
print(binair_tellen(list(range(1000)), -1)) # 9

Wat zie je?

Interpretatie
n lineair binair
10 10 3
100 100 6
1000 1000 9
10000 10000 13
100000 100000 16
  • Lineair: tien keer zo'n lange lijst kost tien keer zoveel werk. Het groeit even hard als n.
  • Binair: tien keer zo'n lange lijst kost drie of vier vergelijkingen extra. Elke vergelijking halveert wat er nog over is, dus het werk groeit met het aantal keer dat je n kunt halveren.

Op een lijst van een miljard zou het verschil nog groter zijn: een miljard vergelijkingen tegenover ongeveer 30.

Uitdaging (optioneel)

Pas binair zoeken aan zodat het bij duplicaten de eerste voorkomende index returnt. Hint: stop niet bij de eerste match — blijf doorzoeken in de linker helft.

  • binair_zoek_eerste([1, 2, 2, 2, 3], 2)1
  • binair_zoek_eerste([1, 2, 2, 2, 3], 4)-1
Tip

Bewaar een match in een variabele gevonden in plaats van meteen te returnen, en verklein daarna het zoekgebied naar links met hoog = midden - 1. Elke nieuwe match links van de vorige overschrijft gevonden. Als de lus stopt, staat daar de eerste.

Antwoord
def binair_zoek_eerste(lijst, doel):
laag = 0
hoog = len(lijst) - 1
gevonden = -1
while laag <= hoog:
midden = (laag + hoog) // 2
if lijst[midden] == doel:
gevonden = midden
hoog = midden - 1 # blijf links zoeken
elif lijst[midden] < doel:
laag = midden + 1
else:
hoog = midden - 1
return gevonden

print(binair_zoek_eerste([1, 2, 2, 2, 3], 2)) # 1
print(binair_zoek_eerste([1, 2, 2, 2, 3], 4)) # -1

De lus stopt nog steeds gegarandeerd: ook bij een match krimpt het zoekgebied, want hoog gaat omlaag.

Door naar veelgemaakte fouten.