Ga naar hoofdinhoud

Bouw zelf — tel het aantal swaps

Leerdoel: je past selection sort aan om het werk te meten, en je ziet hoeveel het algoritme doet bij verschillende soorten input.

Opdracht​

Schrijf selection_sort_telt(lijst) die het aantal swaps en het aantal vergelijkingen returnt tijdens het sorteren. De vergelijkingen telt de startcode al; de swaps zijn aan jou.

Tel alleen echte swaps (i ≠ min_index). Verwacht:

  • Op [3, 1, 2] → 2 echte swaps.
  • Op [1, 2, 3] (al gesorteerd) → 0 echte swaps (elke ronde is i == min_index, dus geen ruil nodig).
  • Op [3, 2, 1] (omgekeerd) → 1 echte swap (alleen ronde 0 ruilt de 3 en de 1; daarna staat alles al goed).

De startcode test daarna op drie soorten lijsten van 50 getallen: al gesorteerd, omgekeerd en willekeurig.

Python
Code-omgeving wordt voorbereid…
Tip

Zet een if i != min_index: vóór de swap. Alleen in dat geval swap je echt, en alleen dan doe je swaps += 1.

Antwoord
def selection_sort_telt(lijst):
n = len(lijst)
swaps = 0
vergelijkingen = 0
for i in range(n):
min_index = i
for j in range(i, n):
vergelijkingen += 1
if lijst[j] < lijst[min_index]:
min_index = j
if i != min_index:
lijst[i], lijst[min_index] = lijst[min_index], lijst[i]
swaps += 1
return swaps, vergelijkingen

print(selection_sort_telt([3, 1, 2])) # (2, 6)
print(selection_sort_telt([1, 2, 3])) # (0, 6)
print(selection_sort_telt([3, 2, 1])) # (1, 6)

Wat zie je?​

Interpretatie

Voor 50 getallen:

LijstSwapsVergelijkingen
al gesorteerd01275
omgekeerd251275
willekeurigmeestal 43 tot 491275
  • Vergelijkingen zijn voor alle drie precies gelijk: selection sort kijkt altijd door het hele ongesorteerde stuk, wat er ook staat. De binnenste lus begint bij j = i, dus elk element wordt ook één keer met zichzelf vergeleken: 50 + 49 + … + 1 = 1275.
  • Swaps verschillen wel. Al gesorteerd: nul, want i == min_index in elke ronde. Omgekeerd: 25, want elke swap zet twee getallen tegelijk goed en na de helft van de rondes staat alles. Willekeurig: bijna elke ronde één, want de kleinste van de rest staat zelden al vooraan.

Dat is het karakter van selection sort: voorspelbaar veel vergelijkingen, maar hooguit n − 1 swaps. Bij dure swaps (objecten ruilen op een trage opslag) is dat een voordeel.

Uitdaging (optioneel)​

Pas selection sort aan zodat hij in elke ronde tegelijk de kleinste én grootste vindt. Zet de kleinste vooraan en de grootste achteraan — zo sorteer je in n / 2 rondes in plaats van n. (Variant: double-ended selection sort.)

Tip

Houd twee grenzen bij, links en rechts, en zoek in elke ronde tussen die twee zowel min_index als max_index. Swap eerst de kleinste naar links. Let op: stond de grootste toevallig óp links, dan is hij door die swap verhuisd naar min_index. Pas dan max_index aan voordat je de grootste naar rechts swapt.

Antwoord
def selection_sort_dubbel(lijst):
links = 0
rechts = len(lijst) - 1
while links < rechts:
min_index = links
max_index = links
for j in range(links, rechts + 1):
if lijst[j] < lijst[min_index]:
min_index = j
if lijst[j] > lijst[max_index]:
max_index = j
lijst[links], lijst[min_index] = lijst[min_index], lijst[links]
if max_index == links:
max_index = min_index # de grootste is net verhuisd
lijst[rechts], lijst[max_index] = lijst[max_index], lijst[rechts]
links += 1
rechts -= 1
return lijst

print(selection_sort_dubbel([5, 2, 8, 1, 4])) # [1, 2, 4, 5, 8]
print(selection_sort_dubbel([3, 1, 4, 1, 5, 9, 2, 6])) # [1, 1, 2, 3, 4, 5, 6, 9]

Haal de regel met max_index = min_index weg en test op [5, 2, 8, 1, 4]: de 5 stond op links, verhuist door de eerste swap naar index 3, en zonder correctie swapt de tweede regel het verkeerde getal naar achteren.

Door naar veelgemaakte fouten.