Zoek- en sorteeralgoritmen en efficiëntie

Leer hoe computers gegevens zoeken en ordenen en hoe je een snelle methode van een langzame onderscheidt. Doorloop lineair zoeken, binair zoeken en drie klassieke sorteeralgoritmen, bubblesort, selectionsort en mergesort, op concrete lijsten. Tel de stappen die elk algoritme uitvoert, bekijk hoe die aantallen groeien als de hoeveelheid gegevens groeit en voorspel uitvoeringstijden. Onderbouw vervolgens dat een algoritme altijd het juiste antwoord geeft, test het met randgevallen en kies het algoritme dat bij een situatie past.

BeginnerLes 4 van 7 in Cursus Informatica: hoe computers, data, algoritmen en internet werken

Wat je leert

  1. Vind een zoekwaarde in een lijst met lineair zoeken en, als de lijst gesorteerd is, met binair zoeken. Doorloop beide methoden op concrete lijsten en tel hoeveel elementen ze controleren.
    • Doorzoek een willekeurige lijst met lineair zoeken
      Controleer elementen vanaf het begin één voor één, geef de positie van de eerste overeenkomst terug of meld dat de zoekwaarde ontbreekt, en tel de vergelijkingen in het beste en slechtste geval.
    • Doorzoek een gesorteerde lijst met binair zoeken
      Gebruik de gesorteerde volgorde om bij elke controle het resterende deel van de lijst te halveren, volg de onder-, boven- en middenpositie en leg uit waarom ongeveer log₂ n controles voldoende zijn.
  2. Geef aan wat een correcte sortering moet opleveren, doorloop daarna bubblesort en selectionsort doorgang voor doorgang en tel hun vergelijkingen en verwisselingen.
    • Beschrijf hoe een correct gesorteerde lijst eruitziet
      Definieer gesorteerde volgorde, sorteersleutels, verwisselingen, stabiliteit, in-place sorteren en inversies, zodat je sorteeralgoritmen kunt doorlopen, controleren en vergelijken.
    • Doorloop bubblesort en tel de bewerkingen
      Sorteer een lijst door buren die in de verkeerde volgorde staan herhaaldelijk te verwisselen, volg het proces doorgang voor doorgang, stop vroegtijdig als een doorgang geen verwisselingen oplevert en tel de vergelijkingen en verwisselingen.
    • Doorloop selectionsort en tel de bewerkingen
      Sorteer een lijst door steeds het kleinste overgebleven element te selecteren en op zijn plaats te verwisselen, volg het proces doorgang voor doorgang en vergelijk de aantallen vergelijkingen en verwisselingen met die van bubblesort.
  3. Voeg twee gesorteerde lijsten samen tot één, doorloop hoe mergesort een lijst splitst en de delen weer samenvoegt en leg uit waarom het ongeveer n log₂ n vergelijkingen nodig heeft.
    • Voeg twee gesorteerde lijsten samen tot één
      Combineer twee al gesorteerde lijsten door steeds het kleinere eerste ongebruikte element te nemen, tel de vergelijkingen en leg uit waarom het resultaat gesorteerd is.
    • Doorloop mergesort en leg de groei volgens n log n uit
      Sorteer door een lijst in tweeën te splitsen tot afzonderlijke elementen overblijven en de delen op volgorde weer samen te voegen. Gebruik een basisgeval om het splitsen te stoppen en tel de vergelijkingen per niveau.
  4. Tel de basisstappen van een algoritme als functie van de invoergrootte, vergelijk logaritmische, lineaire, n log n- en kwadratische groei en gebruik groeisnelheden om uitvoeringstijden te voorspellen.
    • Tel de stappen die een algoritme uitvoert
      Meet een algoritme aan het aantal basisbewerkingen dat het uitvoert voor invoer met grootte n, bepaal het aantal voor lussen en geneste lussen en onderscheid het beste, slechtste en gemiddelde geval.
    • Vergelijk hoe het aantal stappen groeit met de invoergrootte
      Herken constante, logaritmische, lineaire, n log n-, kwadratische en exponentiële groei, bekijk wat er gebeurt als de invoer verdubbelt, lees de informele O-notaties en leg uit waarom groeisnelheid bij grote invoer belangrijker is dan constante factoren.
    • Voorspel en meet uitvoeringstijden
      Gebruik een bekende uitvoeringstijd en een groeisnelheid om de tijd voor grotere invoer te schatten, controleer een groeisnelheid door een programma bij verdubbelende groottes te timen en geef voorspellingen als schattingen.
  5. Laat zien dat een algoritme stopt en voor elke geldige invoer het juiste antwoord geeft, test het met randgevallen en weeg tijd, geheugen en andere factoren af om een algoritme voor een situatie te kiezen.
    • Onderbouw dat een algoritme correct is
      Geef aan wat een algoritme moet bereiken, leg uit waarom testen alleen niet kan bewijzen dat het correct is en gebruik lusinvarianten en een terminatieargument om te laten zien dat zoek- en sorteeralgoritmen werken.
    • Test algoritmen met randgevallen
      Kies invoer op de grenzen van wat een algoritme verwerkt, voorspel de verwachte uitvoer voordat je het uitvoert en vind fouten van één te veel of te weinig en oneindige lussen die gewone invoer niet aan het licht brengt.
    • Kies een algoritme dat bij de situatie past
      Vergelijk zoek- en sorteeralgoritmen op tijd, geheugen, stabiliteit en hun eisen aan de gegevens en bepaal welk algoritme bij een bepaalde taak past, ook wanneer eerst sorteren loont.

Zoek- en sorteeralgoritmen: uitleg

5 hoofdstukken

1

Doorzoek een lijst stap voor stap

Eén waarde vinden begint met een eenvoudige vraag: welke posities moeten nog worden gecontroleerd? Volg lineair zoeken door een gewone lijst en gebruik daarna de gesorteerde volgorde om met binair zoeken grote delen van een lijst uit te sluiten. Je houdt posities en waarden apart bij, ziet hoe elke zoekmethode meldt dat de waarde ontbreekt en telt hoeveel werk gemakkelijke en moeilijke invoer kost. Korte doorlopen laten ook zien waarom de laatst overgebleven positie nog moet worden gecontroleerd.

1.1 Doorzoek een willekeurige lijst met lineair zoeken

Bij zoeken vraag je of een zoekwaarde voorkomt en waar die voorkomt. Een lijst heeft genummerde posities die indexen heten. Python telt vanaf nul: in [7, 3, 9, 3, 5] staat op index 0 de waarde 7 en op index 4 de waarde 5. De waarde en de index beantwoorden verschillende vragen.

Controleer één element tegelijk

Lineair zoeken, ook sequentieel zoeken genoemd, begint bij het eerste element en vergelijkt elk element met de zoekwaarde. Zodra de methode een overeenkomst vindt, geeft ze de index terug. Als de lijst eindigt zonder overeenkomst, geeft ze -1 terug, ons signaal voor “niet gevonden”.

def linear_search(items, target):
    for i in range(len(items)):
        if items[i] == target:
            return i
    return -1

len(items) geeft het aantal elementen. range(len(items)) levert elke geldige index, vanaf 0 tot net vóór de lengte. items[i] leest de waarde op de huidige index. Een return beëindigt de functie, dus een latere overeenkomst vervangt nooit de eerste.

Zoeken naar 3 in de lijst hierboven verloopt als volgt:

  1. Vergelijk op index 0 de waarde 7 met 3. Ze verschillen, dus ga verder.
  2. Vergelijk op index 1 de waarde 3 met 3. Ze komen overeen, dus geef index 1 terug.

Het resultaat is index 1 na twee vergelijkingen. De tweede 3, op index 3, wordt nooit gecontroleerd. Als je in plaats daarvan naar 4 zoekt, controleert de methode 7, 3, 9, 3 en 5 en geeft ze na vijf vergelijkingen -1 terug. Een teruggegeven index van 1 betekent het tweede element, niet een element met de waarde 1.

Tel het werk

Laat n het aantal elementen zijn. Voor een niet-lege lijst is het beste geval één vergelijking: het eerste element komt overeen. Het slechtste geval is n vergelijkingen: de zoekwaarde staat achteraan of ontbreekt. Een lege lijst heeft nul vergelijkingen nodig en geeft -1 terug.

Als een zoekwaarde één keer voorkomt en even waarschijnlijk op elke positie kan staan, is het gemiddelde aantal vergelijkingen

1+2+⋯+nn=n+12.

Voor 1.000 elementen zijn dat 500,5 vergelijkingen. Een gemiddelde kan een breuk zijn, ook al maakt elke afzonderlijke zoekopdracht een geheel aantal vergelijkingen. Dit gemiddelde beschrijft die specifieke verdeling van succesvolle zoekopdrachten; vaak voorkomende mislukte zoekopdrachten zouden het totale gemiddelde verhogen.

Meld een mislukte zoekopdracht na de lus

Een else: return -1 binnen de lus zou bij de eerste ongelijkheid opgeven. Op [7, 3, 9] zou die een zoekopdracht naar 3 afwijzen na alleen 7 te hebben gezien. Het teruggeven van een mislukt resultaat hoort na de lus, als elke positie is uitgesloten.

Lineair zoeken vereist geen gesorteerde volgorde of voorbereiding. Het werkt met getallen, woorden of records die op een identificatie worden vergeleken. Een versie voor gesorteerde gegevens kan stoppen zodra ze voorbij de mogelijke positie van de zoekwaarde komt: in [2, 5, 8, 12] is het zien van 8 genoeg om 6 uit te sluiten. De code hierboven blijft bewust zoeken tot ze een overeenkomst vindt of het einde bereikt.

Verwante taken gebruiken dezelfde doorloop maar een andere stopregel. Om elk voorkomen te tellen, elke overeenkomende index te verzamelen en een grootste element te vinden, moet je de hele lijst bekijken; bij het zoeken naar de eerste overeenkomst mag je eerder terugkeren.

Belangrijkste punten

  • Lineair zoeken controleert posities op volgorde en geeft de index van de eerste overeenkomst terug.
  • Geef -1 pas terug nadat elke mogelijke overeenkomst is uitgesloten.
  • Een niet-lege lijst vereist in het beste geval één vergelijking en in het slechtste geval n.
  • Het gemiddelde (n+1)/2 gaat uit van één voorkomen van de zoekwaarde, even waarschijnlijk op elke positie.

1.2 Doorzoek een gesorteerde lijst met binair zoeken

Binair zoeken gebruikt de gesorteerde volgorde om posities uit te sluiten zonder ze afzonderlijk te controleren. De invoer moet in oplopende volgorde gesorteerd zijn. Ook moet het mogelijk zijn snel een element op een gekozen index te bereiken, zoals bij een array of Pythonlijst.

Houd een interval met beide grenzen bij

low en high geven de eerste en laatste nog mogelijke positie aan, inclusief beide grenzen. Begin met low = 0 en high = len(items) - 1. Kies de middelste index door naar beneden af te ronden. Als de middelste waarde te klein is, is elke waarde links daarvan ook te klein; verplaats low voorbij het midden. Is die te groot, verplaats high dan tot net vóór het midden.

def binary_search(items, target):
    low = 0
    high = len(items) - 1
    while low <= high:
        mid = (low + high) // 2
        if items[mid] == target:
            return mid
        elif items[mid] < target:
            low = mid + 1
        else:
            high = mid - 1
    return -1

// deelt en rondt naar beneden af. De lus omvat ook het geval low == high, omdat er dan nog één ongecontroleerde positie over is. Als low > high, is het interval leeg.

Doorloop een gevonden en een ontbrekende waarde

Gebruik [3, 8, 12, 17, 23, 31, 42, 56, 64, 77, 85], met indexen 0 tot en met 10. Om 64 te vinden is de eerste middelste index 5, met waarde 31. Omdat 31 te klein is, zet je low = 6. De volgende middelste index is 8, met waarde 64. Geef na twee controles van het middelste element 8 terug.

Zoek je in dezelfde lijst naar 20, dan krijg je:

ControleOndergrensBovengrensMiddenpositieMiddelste waardeBijwerking
1010531bovengrens wordt 4
204212ondergrens wordt 3
334317ondergrens wordt 4
444423bovengrens wordt 3

Nu is het interval leeg, dus geef je -1 terug. Dit is een onderbouwde melding dat de waarde ontbreekt: de gesorteerde volgorde rechtvaardigde elk uitgesloten deel.

Waarom is dit snel?

Na elke controle zonder overeenkomst blijft hoogstens de helft van het interval over. Voor een niet-lege lijst is het maximale aantal controles van een middelste element ⌊log⁡2n⌋+1. De logaritme met grondtal 2 beschrijft herhaald halveren: 210=1.024, dus zijn voor duizend elementen hoogstens 10 controles nodig; voor een miljoen hoogstens 20. Eén controle in deze Pythoncode kan twee vergelijkingen maken, == en <. Controles en vergelijkingen zijn verschillende teleenheden.

Voorwaarden en grensfouten

De gesorteerde volgorde is essentieel. Als je in [9, 2, 7, 4, 1] naar 2 zoekt, ziet de methode eerst 7 en sluit ze het rechterdeel uit. Daarna ziet ze 9 en sluit ze de resterende overeenkomst op index 1 uit. Snelle toegang tot het midden is ook belangrijk: als je schakels moet volgen om het midden van een gekoppelde lijst te bereiken, gaat het snelheidsvoordeel van de arrayversie verloren.

Sluit na een controle zonder overeenkomst altijd het gecontroleerde midden uit. Met low = mid kan [10, 20] bij het zoeken naar 20 oneindig onveranderd blijven. Met while low < high sla je de laatste kandidaat over en faalt het zoeken naar 5 in [5]. Bij een lege lijst is high = -1, zodat de correcte lus nooit begint.

Bij dubbele waarden geeft deze versie een willekeurige overeenkomende index terug. Zoeken naar 4 in [2, 4, 4, 4, 9] geeft direct index 2 terug; om de eerste vindplaats te vinden is een aangepaste zoekmethode nodig die na een overeenkomst links verder zoekt.

Belangrijkste punten

  • Binair zoeken vereist gesorteerde gegevens en snelle toegang tot posities.
  • Houd een interval inclusief beide grenzen bij en ga door zolang low <= high.
  • Sluit na een controle zonder overeenkomst mid uit met mid + 1 of mid - 1.
  • Voor n>0 zijn hoogstens ⌊log⁡2n⌋+1 controles van een middelste element nodig.
  • Een overeenkomende index hoeft niet de eerste vindplaats te zijn.
  • Een leeg interval betekent dat de zoekwaarde ontbreekt.
2

Sorteer een lijst met bubblesort en selectionsort

Een lijst kan op volgorde lijken te staan en toch de verkeerde elementen bevatten. Maak eerst de sorteertaak precies, inclusief herhaalde sleutels en de bijbehorende records. Volg daarna twee methoden die met verwisselingen een volgorde opbouwen: bubblesort zet het grootste resterende element rechts op zijn plaats, terwijl selectionsort het kleinste resterende element links plaatst. De doorlopen laten zien dat ze verschillende aantallen vergelijkingen en verplaatsingen kosten.

2.1 Beschrijf hoe een correct gesorteerde lijst eruitziet

Sorteren zet elementen op volgorde volgens een gekozen sleutel. Bij getallen in oplopende volgorde is elk element kleiner dan of gelijk aan het volgende. “Niet-dalend” laat ruimte voor gelijke buren: [2, 5, 5, 9] is gesorteerd. Aflopende volgorde keert de richting om.

Controleer twee voorwaarden

Een correcte sortering moet uitvoer opleveren die op volgorde staat en precies de oorspronkelijke elementen bevat, met behoud van elke herhaling. [3, 1, 2] sorteren tot [1, 2, 3] lukt. Zowel [1, 2] als [1, 1, 3] is fout, ook al staan de buren op volgorde. De eerste lijst verliest een element; de tweede verandert welke elementen voorkomen. Een herschikking die alle voorkomens behoudt, heet een permutatie.

Bij een record is de sleutel alleen het veld dat voor de vergelijking wordt gebruikt. Steden kun je sorteren op naam of inwonertal. Het hele stadsrecord beweegt mee als de sleutel met het inwonertal beweegt; als je de naam ervan losmaakt, raken de gegevens beschadigd.

Tekstvolgorde hangt af van de sleutel

Een gewone tekstvergelijking volgt tekencodes, één teken tegelijk. In ASCII en Unicode gebruiken A–Z de codes 65–90 en a–z de codes 97–122. Daardoor komt “Zebra” bij een gewone vergelijking vóór “appel”. Door kopieën in kleine letters te vergelijken kun je voor deze voorbeelden een volgorde krijgen die geen onderscheid maakt tussen hoofdletters en kleine letters.

Ook cijfers in tekst gedragen zich als tekens. “10” komt bij het sorteren vóór “9”, omdat het eerste teken “1” is. Een numerieke sleutel zet die tekenreeksen om in de getallen die ze voorstellen, zodat 9 vóór 10 komt. Bepaal wat de gegevens betekenen voordat je de vergelijking kiest.

Verplaats waarden zonder ze te verliezen

Een verwisseling wisselt twee posities om. Een tijdelijke variabele beschermt de waarde die anders zou worden overschreven:

temp = items[i]
items[i] = items[j]
items[j] = temp

Python staat ook items[i], items[j] = items[j], items[i] toe. Als je de twee toekenningen schrijft zonder de eerste waarde te bewaren, verandert [4, 7] in [7, 7]. Bubblesort en selectionsort verplaatsen elementen door te verwisselen; mergesort kopieert elementen naar samengevoegde uitvoer.

Stabiliteit en geheugen

Een stabiele sortering behoudt de oorspronkelijke volgorde van records met gelijke sleutels. Begin met Ana 85, Ben 92, Cara 85, in alfabetische volgorde. Een stabiele aflopende sortering op score levert Ben 92, Ana 85, Cara 85 op. Ana blijft vóór Cara staan. Zo kan een latere sortering op score een eerdere alfabetische volgorde binnen elke scoregroep behouden. De ingebouwde sorteerfunctie van Python is stabiel.

Een in-placesortering herschikt de bestaande lijst met slechts enkele extra variabelen, onafhankelijk van de lengte. De hier behandelde methoden bubblesort en selectionsort werken in place. De gebruikelijke mergesort heeft extra opslag nodig die evenredig is met het aantal elementen. Stabiliteit en geheugen beschrijven verschillende eigenschappen: een sortering kan in place werken en toch instabiel zijn.

Meet wanorde met inversies

Een inversie is een paar posities waarbij de sleutel van een eerder element groter is dan die van een later element. In [5, 1, 4, 2, 8] zijn de inversies (5, 1), (5, 4), (5, 2) en (4, 2): vier in totaal. De elementen van een paar hoeven geen buren te zijn.

Een gesorteerde lijst heeft nul inversies. Een omgekeerde lijst met n verschillende elementen heeft er n(n−1)/2, het maximum. Gelijke sleutels vormen nooit een inversie: [3, 3, 1] heeft er twee, één voor elke 3 vóór 1. Inversies tellen is nuttig bij het uitleggen van het aantal verwisselingen bij bubblesort.

Belangrijkste punten

  • Een correcte sortering behoudt elk oorspronkelijk element en zet de sleutels op volgorde.
  • Een sorteersleutel bepaalt welk veld of welke waarde de volgorde bepaalt.
  • Stabiliteit behoudt de volgorde van records met gelijke sleutels.
  • Een in-placesortering heeft slechts een vaste hoeveelheid extra werkruimte nodig.
  • Een inversie is een paar dat in de verkeerde volgorde staat, ook als de elementen geen buren zijn.

2.2 Doorloop bubblesort en tel de bewerkingen

Bubblesort vergelijkt aangrenzende elementen van links naar rechts. Als het linker element groter is, verwisselt het algoritme het paar. Elke keer door de lijst gaan is een doorgang. Een groot element kan tijdens één doorgang herhaaldelijk naar rechts bewegen, totdat het grootste resterende element zijn definitieve positie bereikt.

Volg de eerste doorgang

Begin met [5, 1, 4, 2, 8]:

  1. Vergelijk 5 en 1; verwissel ze voor [1, 5, 4, 2, 8].
  2. Vergelijk 5 en 4; verwissel ze voor [1, 4, 5, 2, 8].
  3. Vergelijk 5 en 2; verwissel ze voor [1, 4, 2, 5, 8].
  4. Vergelijk 5 en 8; laat ze staan.

De doorgang maakt vier vergelijkingen en drie verwisselingen. Het grootste element, 8, staat op zijn plaats. De rest is nog ongesorteerd: 4 staat vóór 2. Eén doorgang garandeert de definitieve positie van het maximum, niet de volgorde van elk element.

Doorgang 2 heeft alleen de eerste vier posities nodig. Vergelijk 1 met 4, verwissel daarna 4 met 2 en vergelijk vervolgens 4 met 5. Dat geeft [1, 2, 4, 5, 8] na nog eens drie vergelijkingen en één verwisseling. Doorgang 3 vergelijkt 1 met 2 en 2 met 4. Er zijn geen verwisselingen, wat bevestigt dat de lijst gesorteerd is. Totaal: negen vergelijkingen en vier verwisselingen.

Verkort elke doorgang en stop vroegtijdig
def bubble_sort(items):
    n = len(items)
    for p in range(n - 1):
        swapped = False
        for j in range(n - 1 - p):
            if items[j] > items[j + 1]:
                items[j], items[j + 1] = items[j + 1], items[j]
                swapped = True
        if not swapped:
            break

Doorgangsteller p begint bij nul. De grootste waarde van j is n - 2 - p, zodat j + 1 binnen het ongesorteerde deel blijft. Zet swapped aan het begin van elke doorgang terug; een verwisseling in een eerdere doorgang zegt niets over de vraag of in de huidige doorgang een element is verplaatst.

Na elke doorgang staat één positie extra aan de rechterkant definitief vast. Een doorgang zonder verwisselingen bewijst dat alle vergeleken buren op volgorde staan. Het al vaststaande einddeel staat ook op volgorde, dus kan het algoritme stoppen. De bevestigende doorgang telt nog steeds mee als werk.

Vergelijk het beste en slechtste geval

Voor een gesorteerde niet-lege lijst maakt de versie met vroegtijdige beëindiging n−1 vergelijkingen en nul verwisselingen. Op een omgekeerde lijst met verschillende elementen maakt ze

(n−1)+(n−2)+⋯+1=n(n−1)2

vergelijkingen en evenveel verwisselingen. Vijf elementen vereisen er 10; 1.000 elementen vereisen er 499.500. Zonder vroegtijdig stoppen zou het uitgewerkte voorbeeld met vijf elementen ook de laatste doorgang met één vergelijking uitvoeren en op 10 vergelijkingen uitkomen.

Elke verwisseling verwijdert precies één inversie. De vier inversies in de oorspronkelijke uitgewerkte lijst verklaren de vier verwisselingen. Gelijke buren blijven staan omdat de test > gebruikt. Daardoor is deze versie stabiel en werkt ze in place. Als je > vervangt door >=, kan de volgorde van records met gelijke sleutels omkeren.

De positie is ook van belang als er weinig inversies zijn. [5, 1, 2, 3, 4] gebruikt vier verwisselingen en een tweede doorgang om de volgorde te bevestigen, voor zeven vergelijkingen. [2, 3, 4, 5, 1] heeft ook vier verwisselingen nodig, maar de 1 kan per doorgang maar één keer naar links bewegen. Er zijn alle vier de doorgangen en 10 vergelijkingen nodig.

Belangrijkste punten

  • Elke doorgang zet het grootste resterende element rechts op zijn plaats.
  • Na een doorgang zonder verwisselingen kun je vroegtijdig stoppen.
  • Tel de doorgang die de volgorde bevestigt mee, ook als die geen verwisselingen maakt.
  • In het slechtste geval zijn er n(n−1)/2 vergelijkingen; gesorteerde invoer vereist er n−1.
  • Door alleen buren te verwisselen die strikt in de verkeerde volgorde staan, blijft de stabiliteit behouden.
  • Het aantal verwisselingen is gelijk aan het aantal inversies in de invoer.

2.3 Doorloop selectionsort en tel de bewerkingen

Selectionsort bouwt links een gesorteerd begindeel op. Elke doorgang vindt het kleinste element in het resterende einddeel en verwisselt dat met het element op de volgende positie van het begindeel. De doorloop vindt de positie; de verwisseling gebeurt na de doorloop.

Onthoud een index

Begin op positie i met smallest = i. Vergelijk elk later element met items[smallest]. Werk smallest alleen bij als er een kleiner element verschijnt. Wissel na afloop van de doorloop items[i] en items[smallest] om.

def selection_sort(items):
    n = len(items)
    for i in range(n - 1):
        smallest = i
        for j in range(i + 1, n):
            if items[j] < items[smallest]:
                smallest = j
        if smallest != i:
            items[i], items[smallest] = items[smallest], items[i]

Als je alleen de kleinste waarde onthoudt, heb je de positie niet voor de verwisseling. Als je de doorloop bij index 0 begint, bekijk je de al geplaatste elementen opnieuw; de verdere zoektocht begint direct na i.

Doorloop de doorgangen

Begin met [29, 10, 14, 37, 13].

DoorgangResterend minimumLijst na plaatsingVergelijkingenVerwisselingen die elementen verplaatsen
110[10, 29, 14, 37, 13]41
213[10, 13, 14, 37, 29]31
314[10, 13, 14, 37, 29]20
429[10, 13, 14, 29, 37]11

Het derde minimum staat al op zijn plaats, dus bewegen er geen waarden. Na vier doorgangen moet het laatste element het grootste zijn; het heeft geen afzonderlijke doorgang nodig. Er zijn 10 vergelijkingen en drie verwisselingen die elementen verplaatsen. De voorwaarde in de code slaat verwisselingen van een positie met zichzelf over; als je die voorwaarde weglaat, blijft het resultaat hetzelfde.

Vergelijkingen en verwisselingen hebben verschillende kosten

Selectionsort maakt altijd n(n−1)/2 vergelijkingen tussen elementen: elke doorgang moet het hele resterende einddeel bekijken om het minimum te vinden. Het totaal is 10 voor vijf elementen of 499.500 voor 1.000, ook bij al gesorteerde invoer. Een doorloop die de onthouden index niet verandert, stelt het minimum van die doorgang vast, maar garandeert in het algemeen niet dat het resterende einddeel op volgorde staat.

Voor een niet-lege lijst is er hoogstens één verwisseling die elementen verplaatst per doorgang, dus hoogstens n−1 in totaal. Lege invoer vereist geen doorgangen of verwisselingen. Bubblesort kan n(n−1)/2 verwisselingen nodig hebben op een omgekeerde lijst met verschillende elementen. Als het verplaatsen van grote records veel meer kost dan het vergelijken van hun sleutels, kan het kleine aantal verwisselingen van selectionsort ondanks de vele vergelijkingen van belang zijn. Als je tijdens de doorloop bij elk nieuw minimum verwisselt, gaat dat voordeel verloren.

Gelijke sleutels kunnen van volgorde veranderen

Deze methode met verwisselingen werkt in place, maar is instabiel. Geef records met gelijke sleutels de labels 4a en 4b zodat hun identiteit zichtbaar is. Begin je met [4a, 4b, 1], dan verwisselt de eerste doorgang 4a en 1, wat [1, 4b, 4a] oplevert. De gelijke sleutels hebben hun oorspronkelijke volgorde omgekeerd, hoewel het algoritme ze nooit rechtstreeks als ongelijk heeft vergeleken. Het eerste minimum dat tijdens een doorloop wordt gevonden behouden, voorkomt niet dat een verwisseling over een grotere afstand een record met gelijke sleutel passeert.

Belangrijkste punten

  • Selectionsort zoekt een minimum en verwisselt daarna één keer na de doorloop.
  • Na elke doorgang krijgt het gesorteerde begindeel er één element bij.
  • Het aantal vergelijkingen is altijd n(n−1)/2.
  • Voor niet-lege invoer gebruikt het algoritme hoogstens n−1 verwisselingen die elementen verplaatsen en een vaste hoeveelheid extra opslag.
  • De gebruikelijke versie met verwisselingen over grotere afstand is instabiel.
3

Sorteer sneller met mergesort

Een grote lijst sorteren door die herhaaldelijk volledig te doorzoeken wordt kostbaar. Mergesort gebruikt een ander plan: verdeel de lijst in kleinere delen, sorteer die delen en combineer de resultaten. Leer eerst waarom je twee gesorteerde lijsten kunt samenvoegen door alleen naar hun eerste ongebruikte elementen te kijken. Volg daarna de recursieve splitsingen en samenvoegingen en houd het werk per niveau en het extra geheugen voor de uitvoer bij.

3.1 Voeg twee gesorteerde lijsten samen tot één

Samenvoegen combineert twee al gesorteerde lijsten tot één gesorteerde uitvoer. Houd bij elke invoerlijst een positiemarkering bij het eerste ongebruikte element. Vergelijk die twee elementen, kopieer het kleinste en schuif alleen de bijbehorende markering op.

Kies het volgende kleinste element

Voeg [3, 27, 38, 43] samen met [9, 10, 82]:

Vergelijking van eerste ongebruikte elementenKopieer naar uitvoerUitvoer tot nu toe
3 en 93[3]
27 en 99[3, 9]
27 en 1010[3, 9, 10]
27 en 8227[3, 9, 10, 27]
38 en 8238[3, 9, 10, 27, 38]
43 en 8243[3, 9, 10, 27, 38, 43]

De linker lijst is nu leeg. Kopieer de resterende 82 zonder nog een vergelijking tussen elementen. Het resultaat is [3, 9, 10, 27, 38, 43, 82], met zes vergelijkingen. Het resterende lijstdeel kopiëren hoort bij het algoritme: als je stopt wanneer de vergelijkingen stoppen, gaan gegevens verloren.

Waarom kan de methode elementen achter de eerste ongebruikte elementen negeren? Elk eerste ongebruikte element is het kleinste resterende element van de eigen lijst. Het kleinste van die twee is daarom het kleinste resterende element in totaal. Deze redenering blijft gelden nadat een markering opschuift, omdat de ongebruikte delen gesorteerd blijven.

Lees de code voor samenvoegen
def merge(left, right):
    result = []
    i = j = 0
    while i < len(left) and j < len(right):
        if left[i] <= right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            j += 1
    result.extend(left[i:])
    result.extend(right[j:])
    return result

append voegt één element toe. Een deel zoals left[i:] bevat het ongebruikte resterende lijstdeel en extend kopieert de elementen daarvan. Als de lus eindigt, kan hoogstens één ongebruikt resterend lijstdeel niet leeg zijn. Als een invoerlijst leeg begint, maakt de lus nul vergelijkingen en wordt de andere lijst intact gekopieerd.

Tel vergelijkingen en opslag

Voor twee niet-lege lijsten met lengtes m en k zijn hoogstens m+k−1 vergelijkingen van eerste ongebruikte elementen nodig. Elke vergelijking plaatst één element en minstens het laatste element kan zonder vergelijking worden gekopieerd. Het kleinst mogelijke aantal vergelijkingen bij het samenvoegen is de lengte van de kortste lijst: [1, 2, 3] samenvoegen met [4, 5, 6] kost drie vergelijkingen voordat de linkerkant leeg is.

Het totale kopieerwerk groeit met m+k, ook als de vergelijkingen vroeg eindigen, omdat elk element in de uitvoer moet voorkomen. De nieuwe lijst heeft ook ruimte nodig voor m+k elementen. Het aantal vergelijkingen, het kopieerwerk en de extra opslag meten verschillende delen van dezelfde bewerking.

Ga om met gelijke sleutels en invoervolgorde

De test <= neemt bij gelijke sleutels het element van links. Als de twee invoerlijsten uit opeenvolgende delen van een oorspronkelijke lijst komen, blijft zo het eerdere record vóór het latere record met gelijke sleutel staan. Dit is de regel voor gelijke sleutels die nodig is voor stabiele mergesort.

De eis dat de invoer gesorteerd is, staat vast. Als je [5, 1] samenvoegt met [3], kopieert de methode 3 en daarna het resterende lijstdeel [5, 1], wat [3, 5, 1] oplevert. De regel voor het eerste ongebruikte element faalde omdat 5 niet de kleinste resterende waarde in de eigen invoerlijst was.

Belangrijkste punten

  • Samenvoegen vergelijkt de eerste ongebruikte elementen van twee gesorteerde invoerlijsten.
  • Kopieer het ongebruikte resterende lijstdeel als één kant leeg is.
  • Twee niet-lege invoerlijsten vereisen hoogstens m+k−1 vergelijkingen van eerste ongebruikte elementen.
  • Bij gelijke sleutels eerst van links nemen behoudt de volgorde tussen de twee helften.

3.2 Doorloop mergesort en leg de groei volgens n log n uit

Mergesort lost een grote sorteertaak op door die in kleinere sorteertaken te splitsen. Dit is verdeel en heers: verdeel de lijst, sorteer elke helft en voeg de gesorteerde helften samen.

Stop met splitsen bij het basisgeval

Een functie is recursief als ze zichzelf aanroept. Mergesort roept zichzelf aan op kortere lijsten. Het basisgeval geeft een lijst met nul of één element ongewijzigd terug, omdat zo’n lijst al gesorteerd is.

def merge_sort(items):
    if len(items) <= 1:
        return items
    mid = len(items) // 2
    left = merge_sort(items[:mid])
    right = merge_sort(items[mid:])
    return merge(left, right)

De delen verdelen de invoer zonder elementen te verliezen. Bij een oneven lengte heeft de rechterkant één element meer: [6, 2, 9, 4, 1, 7, 3] splitst in [6, 2, 9] en [4, 1, 7, 3]. Beide helften zijn korter, dus bereiken recursieve aanroepen uiteindelijk het basisgeval. Een basisgeval alleen zou terminatie niet garanderen als een aanroep de oorspronkelijke lijst steeds aan zichzelf bleef doorgeven.

Bouw de gesorteerde lijst weer op

Doorloop [38, 27, 43, 3, 9, 82, 10, 15]. De lijst splitst in twee helften van vier elementen, daarna vier delen van twee elementen en daarna afzonderlijke elementen. Splitsen herschikt de waarden niet. De volgorde wordt tijdens het samenvoegen opgebouwd:

  1. Voeg afzonderlijke elementen samen tot [27, 38], [3, 43], [9, 82] en [10, 15]. Elk paar kost één vergelijking: vier in totaal.
  2. Voeg paren samen tot [3, 27, 38, 43] en [9, 10, 15, 82]. Elk kost drie vergelijkingen: zes in totaal.
  3. Voeg die helften samen tot [3, 9, 10, 15, 27, 38, 43, 82]. Dit kost zeven vergelijkingen.

De volledige sortering kost 4+6+7=17 vergelijkingen tussen elementen. Tel niet alleen de laatste samenvoeging: de gesorteerde helften moesten eerst worden gemaakt. Het voorbeeld met zeven elementen hierboven wordt met deze splitsingsregel in 14 vergelijkingen gesorteerd tot [1, 2, 3, 4, 6, 7, 9].

Tel niveaus en werk per niveau

Voor acht elementen bereik je door halveren na drie niveaus afzonderlijke elementen, omdat 23=8. In het algemeen zijn er ongeveer log⁡2n samenvoegingsniveaus. Over een volledig niveau verwerken de samenvoegingen gezamenlijk alle n elementen. Elk niveau doet dus werk dat evenredig is met n, wat leidt tot groei als nlog⁡2n.

Voor acht elementen is nlog⁡2n=24 een nuttige ruime bovengrens voor vergelijkingen, niet het exacte aantal. De uitgewerkte lijst gebruikte er 17; een al gesorteerde of omgekeerde lijst met acht elementen gebruikt er 12. De invoervolgorde verandert wanneer bij een samenvoeging één kant opraakt, maar elke lijst doorloopt nog steeds de niveaus van splitsen en samenvoegen. De groei van het aantal vergelijkingen blijft bij deze versie in zowel het beste als het slechtste geval nlog⁡n.

Voor een miljoen elementen geven ongeveer 20 niveaus ongeveer 20 miljoen vergelijkingen als schatting van de bovengrens. Selectionsort gebruikt er ongeveer 500 miljard. Het verschil groeit snel met de invoergrootte.

Houd rekening met de extra uitvoerruimte

De gewone mergesort met lijsten heeft extra opslag nodig die evenredig is met n. Door bij gelijke sleutels het eerste ongebruikte element van links te kiezen, is de sortering stabiel.

Belangrijkste punten

  • Mergesort sorteert recursief twee kortere helften en voegt die daarna samen.
  • Lijsten met nul of één element vormen het basisgeval.
  • Oneven lengtes splitsen in helften die één element verschillen.
  • Elk van de ongeveer log⁡2n niveaus doet werk dat evenredig is met n.
  • Exacte aantallen vergelijkingen hangen af van de volgorde; de groeifamilie is nlog⁡n.
  • De gebruikelijke versie is stabiel als bij gelijke sleutels eerst links wordt genomen en heeft evenredig veel extra opslag nodig.
4

Tel stappen en vergelijk hoe uitvoeringstijden groeien

Een stopwatch vertelt hoe één programma één keer draaide; een aantal stappen helpt verklaren wat er gebeurt als de invoer groeit. Kies een bewerking om te tellen, zet herhaald werk om in een formule en vergelijk groeipatronen van één opzoeking tot het proberen van elke selectie. Gebruik die patronen om uitvoeringstijden en tijdsbudgetten te schatten en ontwerp daarna metingen die kunnen controleren of de voorspellingen bij echte uitvoeringen passen.

4.1 Tel de stappen die een algoritme uitvoert

Een gemeten uitvoeringstijd hangt af van de machine en het programma. Een aantal stappen beschrijft het werk van het algoritme met behulp van een gekozen herhaalde bewerking. De invoergrootte, geschreven als n, is het aantal elementen dat wordt verwerkt.

Kies de bewerking voordat je telt

Bij zoeken en sorteren zijn vergelijkingen tussen elementwaarden meestal een bruikbare basisbewerking. Tel verwisselingen apart als het verplaatsen van gegevens van belang is. Bij binair zoeken is het handig om controles van een middelste element te tellen. In de Pythonversie toetst een controle zonder overeenkomst zowel op gelijkheid als op kleiner zijn en kan die dus twee vergelijkingen tussen elementen omvatten.

MethodeTeleenheidAantal in het slechtste geval
Lineair zoekenVergelijkingen van element met zoekwaarden
Binair zoeken, niet-lege invoerControles van middelste element⌊log⁡2n⌋+1
BubblesortVergelijkingen van burenn(n−1)/2
SelectionsortVergelijkingen van sleutelsn(n−1)/2
MergesortVergelijkingen van eerste ongebruikte elementenHoogstens ongeveer nlog⁡2n

Deze aantallen beschrijven herhaalde bewerkingen, geen regels code. Eén regel binnen een lus kan een miljoen keer worden uitgevoerd; verschillende regels voor de voorbereiding worden misschien elk één keer uitgevoerd.

Onderscheid gevallen bij dezelfde grootte

Het beste geval kost de minste stappen van alle geldige invoer met grootte n. Het slechtste geval kost de meeste en biedt een garantie. Het gemiddelde geval neemt het gemiddelde over een bepaalde verdeling van invoer.

Voor een niet-lege lijst heeft lineair zoeken één vergelijking nodig als de zoekwaarde vooraan staat en n als die achteraan staat of ontbreekt. Het gemiddelde voor een succesvolle zoekopdracht is (n+1)/2 als de eerste positie met een overeenkomst even waarschijnlijk elke positie kan zijn. Het woord “gemiddelde” vereist die verdeling; het betekent niet voor elk algoritme het midden tussen het beste en slechtste aantal.

Zet lussen om in aantallen

Eén vergelijking per element bij een volledige doorloop geeft n vergelijkingen. Twee volledig geneste lussen die elk n keer worden uitgevoerd, voeren hun gemeenschappelijke inhoud n2 keer uit. Een ander patroon met geneste lussen vergelijkt elk paar slechts één keer:

for i in range(n):
    for j in range(i + 1, n):
        compare(items[i], items[j])

De eerste herhaling van de buitenste lus vergelijkt het element met n−1 latere elementen. De volgende vergelijkt met n−2 en de laatste vergelijkt met geen enkel element. Het totaal is

1+2+⋯+(n−1)=n(n−1)2.

Schrijf de som vooruit en achteruit. Elk paar boven elkaar telt op tot n en er zijn n−1 van die paren. Hun totaal is n(n−1), tweemaal de oorspronkelijke som. Voor vijf elementen is 4+3+2+1=10; voor 1.000 is het aantal paren 499.500.

Herken herhaald halveren

Een lus die het resterende werk halveert, kost ongeveer log⁡2n herhalingen. Eén miljoen komt na 20 halveringen voor het eerst op 1 of lager uit: 219=524.288 en 220=1.048.576 liggen aan weerszijden van een miljoen. Dit verschilt sterk van telkens maar één element verwijderen.

Houd bij groeivergelijkingen de snelst groeiende term over. Het aantal paren is n2/2−n/2. Bij n=1.000 zijn de termen 500.000 en 500, dus bepaalt de kwadratische term de groei. Door de kleinere term en de constante factor weg te laten beschrijf je de groeifamilie; het exacte aantal van 499.500 wordt daardoor geen exact aantal van één miljoen.

Belangrijkste punten

  • Benoem de bewerking die je telt en druk het aantal uit met de invoergrootte.
  • Aantallen in het slechtste geval zijn garanties; gemiddelden hangen af van een invoerverdeling.
  • Volledig geneste doorlopen geven n2 uitvoeringen van de lusinhoud.
  • Elk paar verschillende elementen één keer vergelijken geeft n(n−1)/2 vergelijkingen.
  • Herhaald halveren levert logaritmische groei op.

4.2 Vergelijk hoe het aantal stappen groeit met de invoergrootte

Een groeisnelheid beschrijft hoe de hoeveelheid werk verandert als de invoer groter wordt. Ze helpt algoritmen te vergelijken buiten de specifieke grootte die in één doorloop is gebruikt.

Lees de gebruikelijke families
GroeifamilieVoorbeeldEffect van verdubbeling van de invoergrootte
Constant, 1Een bekende arraypositie lezenEvenveel werk
Logaritmisch, log⁡2nBinair zoekenOngeveer één extra controle
Lineair, nVolledige doorloopTweemaal zoveel werk
nlog⁡nMergesortIets meer dan tweemaal zoveel
Kwadratisch, n2SelectionsortOngeveer viermaal zoveel
Exponentieel, 2nElke selectie van elementen proberenHet aantal wordt gekwadrateerd

Bij exponentiële groei verdubbelt het aantal al als er maar één element bijkomt: 2n+1=2×2n. De invoer verdubbelen is een veel grotere verandering dan er één element aan toevoegen.

Vergelijk groottes met getallen

Voor invoer van 10, 100, 1.000 en één miljoen elementen is log⁡2n ongeveer 3,3; 6,6; 10 en 20. De bijbehorende waarden van nlog⁡2n zijn ongeveer 33, 664, 9.966 en 20 miljoen. De kwadratische waarden zijn 100, 10.000, één miljoen en één biljoen.

Logaritmische groei is niet “de helft van de lijst”. Voor een miljoen elementen kost herhaald halveren slechts ongeveer 20 stappen. Als de invoer stijgt van 1.000 naar 2.000, stijgt nlog⁡2n van ongeveer 9.966 naar 21.932, een factor van ongeveer 2,2. De logaritme verandert ook, dus groeit het aantal iets sneller dan lineair.

Exponentieel werk wordt snel groot. 210=1.024 en 220 is ongeveer een miljoen. Bij een miljard stappen per seconde kosten 250 stappen nog steeds ongeveer 13 dagen. Eén extra element verdubbelt die tijd.

Gebruik informele O-notaties

Programmeurs duiden deze families doorgaans aan met O(1), O(log⁡n), O(n), O(nlog⁡n), O(n2) en O(2n). Hier geven ze de gebruikelijke groeifamilie voor het slechtste geval aan. Spreek O(n2) uit als “orde n kwadraat”. De notatie laat constante factoren en termen van lagere orde weg: n(n−1)/2 vergelijkingen behoort tot de kwadratische familie.

Een O-notatie is geen exacte uitvoeringstijd of aantal seconden. Ze beweert ook niet dat elke invoer het aantal van het slechtste geval gebruikt. Bubblesort met vroegtijdige beëindiging heeft een lineair beste geval, maar een kwadratisch slechtste geval.

Vind het omslagpunt

Stel dat methode A 100n stappen kost en methode B n2. Bij 50 elementen kost A 5.000 stappen en B 2.500. Bij 100 kosten beide 10.000. Bij 1.000 kost A 100.000 en B één miljoen. De langzamere groei van A overwint uiteindelijk de grotere constante factor.

De snelheid van hardware verandert de schaal zonder de familie te veranderen. Een computer die 100 keer zo snel is, geeft een kwadratische methode ruimte voor slechts 10 keer zoveel elementen in dezelfde tijd, want (10n)2=100n2. Een lineaire methode zou er 100 keer zoveel kunnen verwerken. Bij een kleine taak kunnen voorbereiding en constanten overheersen; bij grote invoer wordt de groei doorslaggevend.

Belangrijkste punten

  • Verdubbeling van de invoer verdubbelt lineair werk ongeveer en verviervoudigt kwadratisch werk.
  • Logaritmisch werk neemt bij verdubbeling van de invoer met ongeveer één stap toe.
  • Mergesort combineert lineair werk per niveau met een logaritmisch aantal niveaus.
  • Eén element toevoegen verdubbelt een aantal stappen volgens 2n.
  • Groeinotaties laten constanten weg; werkelijke aantallen bepalen de omslagpunten bij kleine invoer.

4.3 Voorspel en meet uitvoeringstijden

Met een bekende uitvoeringstijd en een groeipatroon kun je voorspellen hoe een grotere taak zich gedraagt. De voorspelling is een schatting, omdat de werkelijke tijd ook afhangt van de implementatie, het geheugen, de hardware en ander werk op de machine.

Schaal eerst de invoer en dan de tijd

Bij lineair werk vermenigvuldig je de tijd met de verhouding tussen de invoergroottes. Als een zoekopdracht in het slechtste geval bij één miljoen elementen 3 milliseconden kost, kosten tien miljoen elementen onder vergelijkbare omstandigheden ongeveer 30 milliseconden.

Bij kwadratisch werk kwadrateer je de verhouding. Stel dat selectionsort 2 seconden kost voor 50.000 elementen. Verdubbeling naar 100.000 elementen geeft

2×(100.00050.000)2=2×4=8

seconden. Bij een toename naar 500.000 elementen wordt de invoergrootte met 10 vermenigvuldigd, dus is de geschatte tijd 2×102=200 seconden. De tijd met 10 vermenigvuldigen zou het algoritme ten onrechte als lineair behandelen.

Bij groei volgens nlog⁡2n gebruik je de verhouding tussen beide volledige uitdrukkingen. Verdubbeling van grote invoer kost iets meer dan tweemaal zoveel, omdat de logaritme met het aantal elementen meegroeit.

Reken terug vanuit een tijdsbudget

Gebruik dezelfde kwadratische referentiemeting: 2 seconden voor 50.000 elementen. Een budget van 32 seconden staat 16 keer zoveel tijd toe. De invoer kan groeien met de wortel van 16, dat is 4, tot ongeveer 200.000 elementen. Controleer de richting door in te vullen: viermaal zoveel invoer voorspelt 16 keer zoveel tijd, van 2 seconden naar 32 seconden.

Vergelijk algoritmen bij een grote invoer

Voor één miljoen elementen maakt selectionsort 499.999.500.000 vergelijkingen, ongeveer 500 miljard. Mergesort heeft er hoogstens ongeveer 20 miljoen nodig. Bij een hypothetisch miljard vergelijkingen per seconde komen die aantallen overeen met ongeveer 500 seconden tegenover 0,02 seconden, een verschil van grofweg een factor 25.000. Deze berekening gaat uit van gelijke kosten per vergelijking om het effect van het algoritme afzonderlijk te bekijken; gemeten programma’s besteden ook tijd aan verplaatsingen en geheugentoegang.

Test een voorspelling van de groei

Meet de uitvoeringstijd van hetzelfde programma bij verschillende groottes die steeds verdubbelen, zoals 10.000, 20.000 en 40.000 elementen. Deel elke tijd door de vorige tijd. Verhoudingen rond 2 wijzen op lineaire groei; iets boven 2 wijst op nlog⁡n; rond 4 wijst op kwadratische groei.

Houd de machine en meetmethode gelijk. Herhaal de metingen en gebruik groottes waarbij de opstarttijd niet overheerst. Vergelijk gelijke gevallen: bij de ene grootte op de eerste positie zoeken en bij een andere tevergeefs zoeken zou invoereffecten met groei vermengen. Voor bubblesort hebben gesorteerde en omgekeerde lijsten verschillende gevalsaanduidingen nodig.

Als verdubbelende groottes 0,04, 0,16 en 0,64 seconden opleveren, zijn beide verhoudingen 4. Dat ondersteunt een kwadratisch model over dat bereik. Eén verhouding met veel meetruis is zwakker bewijs dan een herhaald patroon. Aantallen stappen voor een bepaalde doorloop kunnen exact zijn; bij een voorspelling in seconden hoort “ongeveer” en moet je die toetsen aan metingen.

Belangrijkste punten

  • Lineaire tijd schaalt met de invoerverhouding; kwadratische tijd met het kwadraat daarvan.
  • Gebruik bij een kwadratisch tijdsbudget een vierkantswortel om de invoerverhouding te bepalen.
  • Meet verschillende groottes met vergelijkbare invoergevallen en herhaalde uitvoeringen.
  • Verhoudingen tussen gemeten tijden wijzen op groeipatronen; voorspelde seconden blijven schattingen.
5

Onderbouw de correctheid en kies het juiste algoritme

Een snelle doorloop op één lijst laat twee vragen open: werkt het algoritme altijd en past het bij deze taak? Geef aan wat geldige invoer en correcte uitvoer betekenen, gebruik een lusinvariant om uit te leggen waarom de methode voortgang bewaart en laat zien waarom ze stopt. Kies daarna tests die grensfouten aan het licht brengen en vergelijk algoritmen aan de hand van wat de taak werkelijk vereist aan tijd, geheugen, stabiliteit en toegang tot de gegevens.

5.1 Onderbouw dat een algoritme correct is

Een correct algoritme stopt bij elke geldige invoer en produceert de vereiste uitvoer. Een specificatie vermeldt de invoervoorwaarde en het beloofde resultaat. Bij binair zoeken is de preconditie een oplopende gesorteerde volgorde. De postconditie is een overeenkomende index, of -1 precies als de zoekwaarde ontbreekt.

Gebruik een invariant om stappen te verbinden

Een lusinvariant is een uitspraak die geldt voordat de lus begint en door elke herhaling behouden blijft. Een correctheidsargument controleert of de invariant aan het begin geldt, behouden blijft en wat die bij het einde van de lus impliceert. Een afzonderlijk terminatieargument laat zien dat de lus uiteindelijk eindigt.

Gebruik voor selectionsort deze invariant: vóór doorgang i bevatten de eerste i posities de i kleinste elementen op volgorde.

  1. Begin: vóór doorgang 0 is het begindeel leeg. Het voldoet al aan de uitspraak.
  2. Behoud: de doorgang vindt het kleinste resterende element. Door dat daarna te plaatsen, wordt het gesorteerde begindeel één element langer en is het element minstens even groot als elk eerder geplaatst element.
  3. Einde: na hoogstens n−1 doorgangen blijft alleen het grootste element buiten het begindeel over. De hele lijst is gesorteerd.

Elke verplaatsing is een verwisseling, dus blijft ook de multiverzameling van elementen ongewijzigd. Uitvoer die alleen op volgorde staat, zou niet aan de volledige sorteerspecificatie voldoen.

Bubblesort gebruikt een invariant voor het einddeel: na k doorgangen bevatten de laatste k posities de k grootste elementen op volgorde. De volgende doorgang brengt het grootste ongesorteerde element naar dat einddeel. Een doorgang zonder verwisselingen stelt vast dat het hele resterende deel op volgorde staat, waardoor vroegtijdige beëindiging mogelijk is.

Leg de uitgesloten intervallen bij binair zoeken uit

Gebruik de invariant: als de zoekwaarde voorkomt, ligt minstens één voorkomen binnen het interval van low tot en met high. Aanvankelijk omvat dat interval de hele lijst. Als de middelste waarde te klein is, maakt de gesorteerde volgorde elke waarde op of links van mid ook te klein. Door low bij te werken naar mid + 1 blijven alle mogelijke overeenkomsten behouden. De bijwerking naar high = mid - 1 volgt dezelfde redenering voor te grote waarden.

Bij een overeenkomst voldoet de teruggegeven positie direct aan de postconditie. Als het interval leeg raakt, sluit de invariant uit dat de zoekwaarde ergens in de lijst staat. De invoer blijft gesorteerd omdat de zoekmethode die nooit verandert.

Laat zien waarom de uitvoering stopt

Een afnemende niet-negatieve gehele maat bewijst voortgang naar een einde. Bij binair zoeken neemt het aantal mogelijke posities na elke controle zonder overeenkomst strikt af. De gecontroleerde middenpositie wordt uitgesloten, dus kan de lus niet oneindig hetzelfde interval behouden. Bubblesort en selectionsort maken hoogstens n−1 doorgangen bij niet-lege invoer en geen doorgangen bij lege invoer.

Mergesort bereikt lijsten met nul of één element omdat elke recursieve aanroep een kortere lijst gebruikt. Die basisgevallen zijn gesorteerd. Als de kortere aanroepen correct gesorteerde helften teruggeven, neemt de samenvoeging steeds het kleinste resterende element en behoudt ze alle elementen. Zo werkt de correctheid terug tot aan de oorspronkelijke aanroep.

Gebruik tests voor een andere taak

Een test vergelijkt één werkelijk resultaat met één verwacht resultaat. Veel geslaagde gekozen tests geven vertrouwen, terwijl één falende geldige invoer een tegenvoorbeeld is dat correctheid weerlegt. Een algemeen argument met een invariant dekt de onbegrensde familie van mogelijke invoeren die een eindige verzameling voorbeeldtests niet controleert. Testen is vooral nuttig om te controleren of de werkelijke code de methode uit het argument implementeert.

Belangrijkste punten

  • Correctheid vereist het gespecificeerde resultaat en terminatie bij elke geldige invoer.
  • Een invariant moet aanvankelijk gelden, na elke iteratie waar blijven en het resultaat bij het verlaten van de lus onderbouwen.
  • Een argument voor een sortering moet zowel de elementen behouden als de volgorde vaststellen.
  • Een strikt afnemende niet-negatieve maat toont terminatie aan.
  • Eén falende geldige invoer weerlegt correctheid.

5.2 Test algoritmen met randgevallen

Een randgeval is geldige invoer dicht bij een grens waar je gemakkelijk fouten maakt in aannames en lusgrenzen. Bepaal het verwachte resultaat vanuit de specificatie voordat je de code uitvoert. Anders kan de foutieve uitvoer van een programma ongemerkt je verwachte antwoord worden.

Kies lijsten die grenzen beproeven

Test lege lijsten, lijsten met één en met twee elementen, al gesorteerde en omgekeerde lijsten, herhaalde en uitsluitend gelijke waarden, negatieve getallen en oneven en even lengtes. Neem bij zoekopdrachten de eerste positie, de laatste positie en zoekwaarden op die onder, boven en tussen opgeslagen waarden ontbreken.

Binair zoeken in een lege lijst moet -1 teruggeven zonder een element te benaderen: low = 0 en high = -1 maken het begininterval leeg. Het sorteren van [3, 3, 3] moet drie exemplaren van 3 teruggeven. Een uitvoer van [3] zou op volgorde staan, maar niet alle elementen behouden.

Doorloop de kleinste falende invoer

Binair zoeken met while low < high gaat bij [5] met zoekwaarde 5 de lus niet in. De oplossing is low <= high, omdat een interval met één positie nog een kandidaat bevat. Deze test met één element isoleert de grens duidelijker dan een lange lijst.

Met low = mid zet een zoekopdracht naar 20 in [10, 20] de middenpositie op 0, ziet 10 en laat low op 0 staan. Het interval is bij de volgende herhaling onveranderd. Gebruik low = mid + 1 om een al gecontroleerde positie uit te sluiten. Een lijst met twee elementen en de zoekwaarde achteraan legt deze oneindige lus bloot.

Bubblesort vergelijkt items[j] met items[j + 1]. Bij de eerste doorgang is de laatste geldige linker index n - 2. Doorgaan tot n - 1 leest voorbij de lijst en veroorzaakt de Pythonfout IndexError. Zo’n grensfout van één positie heet een fout van één te veel of te weinig.

Controleer beide beloften over de uitvoer

Controleer bij een sortering de volgorde van aangrenzende elementen en het aantal van elk oorspronkelijk element. Gebruik gelabelde records om stabiliteit te testen. Bij Ana 85, Ben 92, Cara 85 moet Ana vóór Cara blijven staan als een stabiele sortering op score de gelijke scores groepeert. Een controle van alleen de scores zou een verwisseling van die twee records missen.

Genereer ook willekeurige lijsten. Vergelijk hun resultaten met een betrouwbare implementatie zoals sorted van Python. Neem dubbele waarden en verschillende lengtes op in de gegenereerde invoer. Als een test faalt, verklein je de invoer terwijl de fout blijft optreden en doorloop je die daarna met de hand. Bewaar het kleine voorbeeld als regressietest, zodat de herstelde fout detecteerbaar blijft.

Houd de berekening van het midden veilig

Gehele getallen in Python groeien waar nodig, dus loopt (low + high) // 2 niet over. Sommige talen gebruiken gehele getallen met een vaste bitbreedte. Voor zeer grote niet-negatieve indexen kan hun som het bereik dat kan worden opgeslagen overschrijden. Gehele optelling in Java loopt cyclisch over; overloop van gehele getallen met teken in C heeft ongedefinieerd gedrag.

Bereken bij geldige indexen en low <= high in plaats daarvan low + (high - low) // 2. Het verschil is niet-negatief en het resultaat blijft tussen de grenzen. Dit berekent dezelfde naar beneden afgeronde middenpositie zonder de mogelijk overlopende som te vormen. Hier vullen grenswaardetesten en redeneren over de getalrepresentatie van de taal elkaar aan.

Belangrijkste punten

  • Schrijf verwachte resultaten op voordat je tests uitvoert.
  • Kleine lijsten leggen ontbrekende laatste controles, onveranderde intervallen en ongeldige toegang tot buren bloot.
  • Controleer behoud van elementen, volgorde en, wanneer vereist, stabiliteit.
  • Verklein falende willekeurige invoer en bewaar die als regressietest.
  • De middenpositie op basis van het verschil voorkomt overloop bij geldige niet-negatieve indexen met vaste bitbreedte.

5.3 Kies een algoritme dat bij de situatie past

Een algoritme past bij een taak als de eisen en kosten aansluiten bij de gegevens en het werk dat wordt gedaan. Denk aan de invoervolgorde, toegang tot posities, het aantal toekomstige bewerkingen, extra geheugen, stabiliteit en de kosten van het verplaatsen van records.

Vergelijk de methoden
MethodePatroon van tijd of stappenExtra opslagStabiel?
Lineair zoekenMaximaal n vergelijkingenVastGeen eigenschap van een sortering
Binair zoekenMaximaal ⌊log⁡2n⌋+1 controles van het middenVastGeen eigenschap van een sortering
Bubblesort met vroegtijdige beëindigingn−1 vergelijkingen indien gesorteerd; kwadratisch slechtste gevalVastJa, bij strikt grotere sleutels verwisselen
Selectionsortn(n−1)/2 vergelijkingen; hoogstens n−1 verwisselingen die elementen verplaatsenVastNee, in de gebruikelijke vorm met verwisselingen
MergesortGroei van vergelijkingen volgens nlog⁡nEvenredig met nJa, bij gelijke sleutels eerst links

Binair zoeken vereist ook een gesorteerde volgorde en snelle toegang tot het midden. Een goede vergelijking bevat die voorwaarden; alleen een klein aantal zoekcontroles laat de kosten weg van het voorbereiden van ongeschikte gegevens.

Bepaal of eerst sorteren loont

Voor één zoekopdracht in een miljoen ongesorteerde elementen kost lineair zoeken hoogstens een miljoen vergelijkingen. Eerst met mergesort sorteren kost ongeveer 20 miljoen vergelijkingen vóór de eerste binaire zoekopdracht, die daarna hoogstens 20 controles van het midden vereist. De voorbereiding overtreft de besparing op die ene zoekopdracht ruimschoots.

Voor 100 zoekopdrachten in een onveranderde lijst kunnen herhaalde lineaire zoekopdrachten 100 miljoen vergelijkingen kosten. Eén keer sorteren en 100 binaire zoekopdrachten uitvoeren kost ongeveer 20 miljoen vergelijkingen plus 2.000 controles van het midden. De voorbereiding wordt hergebruikt, wat het tweede plan aantrekkelijk maakt. Volgens dit globale kostenmodel ligt het omslagpunt rond 20 zoekopdrachten. De schatting hangt af van vergelijkbare bewerkingskosten en doorlopen in het slechtste geval; het is geen universele grens voor het aantal zoekopdrachten.

Weeg volgorde, verplaatsing en geheugen af

Bubblesort kan snel klaar zijn als alleen een paar naburige elementen verkeerd staat. [1, 2, 4, 3, 5] heeft één verwisseling in de eerste doorgang nodig en een tweede doorgang om de volgorde te bevestigen: zeven vergelijkingen. Maar “bijna gesorteerd” garandeert niet dat er weinig doorgangen nodig zijn. Een kleine waarde helemaal rechts kan per doorgang maar één positie naar links bewegen.

De weinige verwisselingen van selectionsort helpen als records verplaatsen kostbaar is. Zowel selectionsort als bubblesort gebruikt slechts een vaste hoeveelheid extra opslag. Mergesort verwerkt grote lijsten met een veel langzamere tijdsgroei, maar de gebruikelijke vorm met lijsten heeft ruimte nodig voor ongeveer nog een lijst vol elementen.

Als records met gelijke sleutels hun eerdere volgorde moeten behouden, kies dan een stabiele methode. Een lijst die al alfabetisch op naam staat, kun je stabiel op cijfer sorteren en tegelijk binnen elke cijfergroep alfabetisch houden. Gewone selectionsort kan die volgorde bij gelijke sleutels verstoren.

Gebruik geteste bibliotheekfuncties

Echte programma’s gebruiken meestal de ingebouwde sorteerfunctie van de taal. De stabiele sortering van Python gebruikt een adaptieve methode op basis van samenvoegen, algemeen bekend als Timsort, die profiteert van volgorde die al in de invoer aanwezig is. sorted(items) geeft een nieuwe lijst terug, terwijl items.sort() de bestaande lijst verandert. Een methode die als API-gedrag een lijst op zijn plaats verandert, kan intern nog steeds tijdelijk werkgeheugen gebruiken; ze hoeft niet te voldoen aan de definitie van vaste extra opslag die voor de eenvoudige sorteringen hierboven wordt gebruikt.

Voor een handvol elementen kan eenvoudige, heldere code belangrijker zijn dan de groei. Vergelijk bij grote verzamelingen het gedrag in het slechtste geval en het geheugen naast de eigenschappen die de taak werkelijk vereist.

Belangrijkste punten

  • Neem de voorbereidingskosten mee als je zoekmethoden op ongesorteerde gegevens vergelijkt.
  • Bij herhaald zoeken kunnen de kosten van één keer sorteren worden terugverdiend.
  • Selectionsort beperkt verwisselingen; mergesort beperkt de tijdsgroei maar heeft extra opslag nodig.
  • Gebruik een stabiele sortering om een eerdere volgorde binnen groepen met gelijke sleutels te behouden.
  • Ingebouwde sorteerfuncties bieden geteste implementaties; bij kleine invoer kan eenvoud de voorkeur hebben.

Zoek- en sorteeralgoritmen: voorbeelden

5 Walkthroughs

1

Vind een zoekwaarde zonder posities uit het oog te verliezen

13 stappen
Doorloop lineair en binair zoeken, tel hun werk en controleer de grenzen van elke zoekmethode.
  1. 1
    IntroductieVind een waarde en geef de positie aan

    Bij een zoekopdracht moet je een opgeslagen waarde onderscheiden van de positie ervan. In [7, 3, 9, 3, 5] lopen de posities van 0 tot en met 4. De waarde 3 staat op indexen 1 en 3.

    We controleren eerst posities op volgorde (lineair zoeken) en gebruiken daarna de gesorteerde volgorde om groepen posities uit te sluiten (binair zoeken). We tellen vergelijkingen om te zien hoeveel werk elke methode doet. Beide methoden geven bij succes een index terug en -1 als de zoekwaarde ontbreekt.

  2. 2
    UitlegSluit met één ongelijkheid één positie uit

    Lineair zoeken vergelijkt de zoekwaarde van links naar rechts met elk element. Een overeenkomst beëindigt de zoekopdracht onmiddellijk; bij een ongelijkheid gaat de methode naar de volgende positie.

    Een ongesorteerde lijst geeft geen reden om latere elementen na een vroege ongelijkheid af te wijzen. Daarom hoort het teruggeven van “niet gevonden” na de lus, zodra elke positie is gecontroleerd.

  3. 3
    VoorbeeldVind de eerste 3

    Zoek in [7, 3, 9, 3, 5] naar 3.

    Op index 0 verschilt 7 van 3, dus ga je verder. Op index 1 is de opgeslagen waarde 3, dus geef je na twee vergelijkingen 1 terug.

    De tweede 3 wordt nooit bekeken. De teruggegeven index is 1, terwijl het aantal vergelijkingen 2 is; die getallen beschrijven verschillende delen van de doorloop.

  4. 4
    UitdagingDoorloop een latere overeenkomst

    Lineair zoeken controleert [19, 4, 11, 4, 8] op 11. Welke index geeft de methode terug en hoeveel vergelijkingen maakt ze?

    ---reveal---

    De methode geeft na drie vergelijkingen index 2 terug: 19, 4 en dan 11. Een mislukt resultaat teruggeven na de eerste ongelijkheid zou ten onrechte bij 19 stoppen. Index 2 is de derde positie omdat posities bij nul beginnen.

  5. 5
    KernpuntLaat de zoekwaarde het stopmoment bepalen

    Voor een niet-lege lijst met n elementen gebruikt lineair zoeken in het beste geval één vergelijking en in het slechtste geval n. Een lege lijst gebruikt er nul.

    Als de zoekwaarde één keer voorkomt en even waarschijnlijk op elke positie staat, is het gemiddelde voor een succesvolle zoekopdracht (n+1)/2. Elk voorkomen tellen verandert de taak: zelfs een vroege overeenkomst kan die volledige doorloop niet beëindigen.

  6. 6
    AnalogieGebruik de volgorde om een kant uit te sluiten

    Bij een woordenboek kun je naar de helft bladeren waarin het gezochte woord staat. Binair zoeken gebruikt hetzelfde idee van ordening met exacte indexgrenzen.

    De preconditie is dat de gegevens gesorteerd zijn. Als een middelste getal te klein is, is elk eerder getal ook te klein. Zonder volgorde geeft een vergelijking met het midden geen dergelijke informatie over de andere posities.

  7. 7
    UitlegHoud beide grensposities als kandidaat

    Begin met low = 0 en high = n - 1. Bereken zolang low <= high geldt mid = (low + high) // 2.

    Geef mid terug als het element overeenkomt. Is de waarde te klein, zet dan low = mid + 1; is die te groot, zet dan high = mid - 1. De gecontroleerde middenpositie wordt na beide soorten ongelijkheid uitgesloten, zodat elke bijwerking het resterende zoekinterval kleiner maakt.

  8. 8
    VoorbeeldControleer het midden en daarna de rechterhelft

    Zoek 64 in [3, 8, 12, 17, 23, 31, 42, 56, 64, 77, 85]. De begingrenzen zijn 0 en 10. Hun naar beneden afgeronde middenpositie is 5, met waarde 31.

    Omdat 31 kleiner is dan 64, zet je low = 6. De resterende grenzen 6 en 10 geven middenpositie 8. De waarde daar is 64, dus geef je na twee controles van het middelste element 8 terug.

  9. 9
    VoorbeeldZie hoe een ontbrekende zoekwaarde het interval leegmaakt

    Zoek in dezelfde gesorteerde lijst naar 20. De gecontroleerde indexen en waarden zijn 5 → 31, 2 → 12, 3 → 17 en 4 → 23.

    Na de bijwerkingen zijn low = 4 en high = 3. Er blijft geen kandidaat over, dus geef je -1 terug. De laatste controle was van belang: gelijke grenzen vlak daarvoor betekenden één kandidaat, geen leeg interval.

  10. 10
    UitdagingBereken de middenpositie na elke bijwerking opnieuw

    Zoek 34 in [2, 7, 13, 21, 34, 55] met grenzen die meetellen en naar beneden afgeronde middenposities. Welke indexen worden gecontroleerd?

    ---reveal---

    Controleer eerst index 2, met waarde 13. Zet low = 3 terwijl high op 5 blijft, zodat de volgende middenpositie 4 is. Daar staat 34: geef na twee controles 4 terug. De positie na 2 was niet automatisch de volgende middenpositie.

  11. 11
    TipControleer de laatste kandidaat en sluit oude kandidaten uit

    low <= high controleert een interval met één positie. Een strikte voorwaarde < mist [5] bij het zoeken naar 5.

    Gebruik mid + 1 en mid - 1 na controles zonder overeenkomst. Met low = mid kan een zoekopdracht naar 20 in [10, 20] de middenpositie 0 oneindig herhalen. Als dubbele waarden overeenkomen, geeft deze versie een overeenkomende index terug, zonder garantie dat het de eerste is.

  12. 12
    UitdagingKies een zoekmethode voor de werkelijke invoer

    Je hebt een ongesorteerde lijst en moet één waarde opzoeken, zonder tijd om een gesorteerde kopie te maken. Welke behandelde zoekmethode kun je direct gebruiken en waarom?

    ---reveal---

    Lineair zoeken is geschikt omdat elke gelijkheidscontrole geldig is zonder enige ordening. Binair zoeken heeft een gesorteerde volgorde nodig om de uitgesloten intervallen te rechtvaardigen. Een klein aantal controles van het midden kan een ontbrekende preconditie niet compenseren.

  13. 13
    SamenvattingGebruik de juiste onderbouwing om te stoppen

    Lineair zoeken stopt bij de eerste overeenkomst of na een volledige doorloop. Binair zoeken gebruikt een gesorteerde volgorde en snelle toegang via indexen om een interval inclusief beide grenzen te verkleinen.

    Noteer de teruggegeven index apart van het aantal bewerkingen. Binair zoeken heeft voor niet-lege invoer hoogstens ⌊log⁡2n⌋+1 controles van het midden nodig, maar elke controle kan in code meer dan één vergelijking bevatten.

2

Bouw een volgorde op met verwisselingen tussen buren en verder gelegen elementen

16 stappen
Controleer de eisen voor sorteren, doorloop daarna bubblesort en selectionsort en vergelijk hun kosten.

Sla deze les op om elke Walkthrough stap voor stap te volgen.

  1. 1
    IntroductieMaak de sorteertaak precies
  2. 2
    UitlegBehoud zowel elementen als volgorde
  3. 3
    VoorbeeldBekijk wat stabiliteit behoudt
  4. 4
    UitdagingControleer beide voorwaarden voor sorteren
  5. 5
    UitlegBreng een groot element naar rechts
  6. 6
    VoorbeeldVolg drie opeenvolgende verwisselingen
  7. 7
    VoorbeeldTel de bevestigende doorgang mee
  8. 8
    UitdagingVergelijk verwisselingen met vergelijkingen
  9. 9
    KernpuntTel inversies om verwisselingen te tellen
  10. 10
    UitlegSelecteer een minimum voordat je het verplaatst
  11. 11
    VoorbeeldPlaats de eerste twee minima
  12. 12
    VoorbeeldRond af zonder een waarde met zichzelf te verwisselen
  13. 13
    UitdagingVind een minimum na het begindeel
  14. 14
    KernpuntEen verwisseling over afstand kan een gelijke sleutel passeren
  15. 15
    UitdagingKies een methode bij kostbare verplaatsingen
  16. 16
    SamenvattingVolg het gebied dat elke methode vastzet
3

Splits een lijst en bouw die met samenvoegingen weer op

13 stappen
Volg de samenvoegbewerking, doorloop daarna een volledige recursieve mergesort en tel de niveaus.

Sla deze les op om elke Walkthrough stap voor stap te volgen.

  1. 1
    IntroductieGebruik de volgorde die al in kleinere delen is opgebouwd
  2. 2
    UitlegVergelijk alleen de eerste ongebruikte elementen
  3. 3
    VoorbeeldVoeg de eerste paar waarden samen
  4. 4
    VoorbeeldKopieer het resterende lijstdeel nadat de vergelijkingen eindigen
  5. 5
    UitdagingVoltooi een samenvoeging en tel de vergelijkingen
  6. 6
    UitlegMaak elk recursief probleem kleiner
  7. 7
    VoorbeeldSplits acht elementen in afzonderlijke elementen
  8. 8
    VoorbeeldBouw paren en helften weer op
  9. 9
    VoorbeeldTel elk samenvoegingsniveau
  10. 10
    UitdagingTel het werk binnen de helften mee
  11. 11
    VerbandBehoud de volgorde bij gelijke sleutels tijdens het opbouwen
  12. 12
    UitdagingPas de methode toe op een grote stabiele sortering
  13. 13
    SamenvattingTel elementen over de niveaus heen
4

Zet herhaald werk om in een tijdschatting

14 stappen
Tel lusuitvoeringen, vergelijk groeifamilies en schat uitvoeringstijden en tijdsbudgetten.

Sla deze les op om elke Walkthrough stap voor stap te volgen.

  1. 1
    IntroductieVraag hoeveel werk de volgende grootte vereist
  2. 2
    UitlegKies wat als stap telt
  3. 3
    VoorbeeldTel elk paar slechts één keer
  4. 4
    UitdagingLees de grenzen voordat je de groei benoemt
  5. 5
    KernpuntVermeld het geval bij het aantal
  6. 6
    UitlegProbeer de verdubbelingstest
  7. 7
    VoorbeeldOnderscheid een groeiaanduiding van een exact aantal
  8. 8
    UitdagingVind een omslagpunt met exacte modellen
  9. 9
    VerbandBekijk de grens van snellere hardware
  10. 10
    VoorbeeldSchaal een gemeten kwadratische tijd
  11. 11
    UitdagingBepaal een invoergrootte vanuit een tijdsbudget
  12. 12
    TipMeet vergelijkbare gevallen herhaaldelijk
  13. 13
    UitdagingGebruik waargenomen verhoudingen voor een voorspelling
  14. 14
    SamenvattingNeem het telmodel mee in de schatting
5

Laat zien dat een algoritme werkt en bij de taak past

15 stappen
Gebruik invarianten, terminatieargumenten, tests met randgevallen en kostenvergelijkingen om een algoritme te beoordelen.

Sla deze les op om elke Walkthrough stap voor stap te volgen.

  1. 1
    IntroductieVraag om een garantie en praktische geschiktheid
  2. 2
    UitlegFormuleer de beloften voordat je ze bewijst
  3. 3
    VoorbeeldBreid de invariant van selectionsort uit
  4. 4
    VoorbeeldBehoud een mogelijke overeenkomst en verklein het interval
  5. 5
    UitdagingOnderscheid behoud van terminatie
  6. 6
    UitlegVoorspel resultaten aan de grens vóór de uitvoering
  7. 7
    VoorbeeldLaat een interval met twee elementen een lus blootleggen
  8. 8
    UitdagingVind de grens van de lus voor buren
  9. 9
    TipBewaar een minimale falende test
  10. 10
    UitlegTel de voorbereidingskosten mee
  11. 11
    VoorbeeldHergebruik de gesorteerde lijst voor zoekopdrachten
  12. 12
    UitdagingBereken of voorbereiding loont
  13. 13
    VerbandKies eigenschappen naast tijdsgroei
  14. 14
    UitdagingVerbind correctheid met een praktische eis
  15. 15
    SamenvattingGebruik argumenten, tests en kosten samen

Zoek- en sorteeralgoritmen: oefenvragen

40 Oefenvragen in 4 sets

Set 1: Vind waarden en volg de algoritmen10 Oefenvragen

Vraag 1 van 10

Lineair zoeken controleert [12, 7, 18, 7] op 7. Welke index geeft de methode terug?
  1. 0
  2. 1
  3. 2
  4. 3
Toon antwoordVerberg antwoord
Antwoord: B
De eerste overeenkomst is het tweede element, dat index 1 heeft omdat indexen bij nul beginnen. Lineair zoeken stopt daar en bereikt de tweede 7 op index 3 dus nooit. Het teruggeven van 2 zou het aantal gecontroleerde elementen verwarren met de teruggegeven positie.

Vraag 2 van 10

Binair zoeken gebruikt grenzen die meetellen en een naar beneden afgeronde middenpositie op [4, 9, 15, 22, 30, 41, 57]. Welke indexen controleert de methode bij het zoeken naar 41?
  1. 3, 4, 5
  2. 3, 6, 5
  3. 4, 5
  4. 3, 5
Toon antwoordVerberg antwoord
Antwoord: D
De eerste middenpositie is index 3, met waarde 22. Omdat 41 groter is, zijn de resterende grenzen 4 en 6, met middenpositie 5, waar 41 wordt gevonden. Als je daarna index 4 controleert, doorloop je het resterende interval vanaf het begin in plaats van de middenpositie te kiezen.

Vraag 3 van 10

Een sorteerfunctie ontvangt [6, 2, 6, 1]. Welke uitvoer voldoet aan de specificatie voor oplopend sorteren?
  1. [1, 2, 6, 6]
  2. [1, 2, 6]
  3. [1, 2, 2, 6]
  4. [6, 6, 2, 1]
Toon antwoordVerberg antwoord
Antwoord: A
De uitvoer moet niet-dalend zijn en elk oorspronkelijk voorkomen behouden, inclusief beide exemplaren van 6. De lijst [1, 2, 6] heeft waarden op volgorde, maar verliest één element. Sorteren herschikt gegevens; het verwijdert geen herhaalde waarden.

Vraag 4 van 10

Welke lijst blijft over na één volledige doorgang van bubblesort van links naar rechts op [8, 3, 6, 2]?
  1. [3, 6, 8, 2]
  2. [2, 3, 6, 8]
  3. [3, 6, 2, 8]
  4. [3, 8, 2, 6]
Toon antwoordVerberg antwoord
Antwoord: C
Bij de vergelijkingen wordt 8 eerst met 3, dan 8 met 6 en daarna 8 met 2 verwisseld, zodat [3, 6, 2, 8] overblijft. De grootste waarde staat nu rechts op zijn plaats. Een volledig gesorteerd resultaat veronderstelt dat één doorgang ook het resterende begindeel ordent; deze doorloop laat zien dat dit niet zo is.

Vraag 5 van 10

Voeg [2, 11] samen met [5, 14, 19]. Welke elementen moeten nog worden gekopieerd nadat 11 is gekopieerd?
  1. 5 en 19
  2. 14 en 19
  3. alleen 19
  4. 11 en 14
Toon antwoordVerberg antwoord
Antwoord: B
Na het kopiëren van 2, 5 en 11 is de linker invoerlijst op en bevat de rechter invoerlijst nog 14 en 19. Beide moeten worden gekopieerd om alle elementen te behouden. Alleen 19 kopiëren zou 14 overslaan, ook al zijn er geen vergelijkingen van eerste ongebruikte elementen meer nodig.

Vraag 6 van 10

Een aanroep van mergesort ontvangt [16]. Wat moet die teruggeven?
  1. direct [16]
  2. direct []
  3. [16, 16] na samenvoegen
  4. [] na splitsen
Toon antwoordVerberg antwoord
Antwoord: A
Een lijst met één element voldoet al aan de voorwaarden voor sorteren, dus geeft het basisgeval [16] ongewijzigd terug. Een lege lijst teruggeven zou het oorspronkelijke element verliezen. Nog een recursieve splitsing is niet nodig omdat het probleem de stopgrootte al heeft bereikt.

Vraag 7 van 10

Een algoritme vergelijkt elk paar verschillende posities één keer in een lijst met 7 elementen. Hoeveel vergelijkingen tussen elementen maakt het?
  1. 49
  2. 42
  3. 28
  4. 21
Toon antwoordVerberg antwoord
Antwoord: D
De opeenvolgende elementen vereisen 6, 5, 4, 3, 2, 1 en 0 vergelijkingen met latere posities, in totaal 21. Je kunt ook 7×6/2 gebruiken. Een aantal van 42 telt elk ongeordend paar twee keer, eenmaal vanuit elke richting.

Vraag 8 van 10

Een methode kost precies n2 stappen. Hoe verandert het aantal als de invoergrootte verdubbelt?
  1. Het verdubbelt.
  2. Het verdrievoudigt.
  3. Het verviervoudigt.
  4. Het blijft gelijk.
Toon antwoordVerberg antwoord
Antwoord: C
Als je n vervangt door 2n, krijg je (2n)2=4n2, dus wordt het aantal vier keer zo groot. Alleen het aantal stappen verdubbelen zou lineaire groei beschrijven. Het kwadraat werkt zowel op de verhouding tussen de groottes als op de oorspronkelijke invoergrootte.

Vraag 9 van 10

Selectionsort heeft de vier kleinste elementen op volgorde op indexen 0–3 geplaatst. Waarom blijft die eigenschap van het begindeel behouden als je het minimum vanaf index 4 kiest en op index 4 verwisselt?
  1. Het resterende einddeel staat al op volgorde.
  2. Het vijfde element blijft bij elke doorloop gelijk.
  3. Het nieuwe element is niet kleiner dan de geplaatste vier.
  4. De eerdere verwisselingen hebben elke inversie verwijderd.
Toon antwoordVerberg antwoord
Antwoord: C
Het resterende minimum is minstens even groot als elk al geplaatst element, dus breidt het toevoegen ervan het geordende begindeel met de kleinste elementen uit. Een volledige doorloop van het einddeel vindt dat minimum. Die stelt niet vast dat het hele einddeel al op volgorde stond; dat zou een sterkere en ongefundeerde bewering zijn.

Vraag 10 van 10

Een binaire zoekmethode met grenzen die meetellen gebruikt per ongeluk while low < high. Welke kleinste niet-lege test legt bloot dat een aanwezige zoekwaarde wordt gemist?
  1. [13], zoekwaarde 13
  2. [13], zoekwaarde 8
  3. [8, 13], zoekwaarde 8
  4. [8, 13], zoekwaarde 20
Toon antwoordVerberg antwoord
Antwoord: A
Bij één element zijn beide grenzen gelijk aan 0, dus slaat de onjuiste strikte voorwaarde de enige kandidaat over en meldt de methode dat zoekwaarde 13 ontbreekt. Een ontbrekende zoekwaarde in dezelfde lijst kan nog steeds het juiste resultaat opleveren. De test heeft een aanwezige zoekwaarde nodig om de ontbrekende laatste controle bloot te leggen.
Set 2: Tel werk en kies praktische methoden10 Oefenvragen

Sla deze les op om elk antwoord te zien en elke set te oefenen.

Vraag 1 van 10

Lineair zoeken meldt dat een zoekwaarde ontbreekt in een ongesorteerde lijst met 9 elementen. Hoeveel vergelijkingen van een element met de zoekwaarde maakt de methode?
  1. 0
  2. 1
  3. 8
  4. 9
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 2 van 10

Binair zoeken gebruikt grenzen die meetellen en een naar beneden afgeronde middenpositie. Wat zijn in [6, 12, 18, 25, 33, 46] de grenzen direct na de eerste controle bij het zoeken naar 30?
  1. ondergrens 2, bovengrens 5
  2. ondergrens 3, bovengrens 5
  3. ondergrens 0, bovengrens 1
  4. ondergrens 0, bovengrens 2
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 3 van 10

Bubblesort met vroegtijdige beëindiging gebruikt kortere doorgangen op [1, 3, 2, 4]. Hoeveel vergelijkingen maakt het voordat het stopt?
  1. 3
  2. 4
  3. 5
  4. 6
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 4 van 10

Standaard selectionsort ontvangt 6 elementen die al gesorteerd zijn. Hoeveel vergelijkingen tussen elementen maakt het?
  1. 15
  2. 5
  3. 0
  4. 30
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 5 van 10

Bij het samenvoegen van [1, 8, 13] en [4, 9] worden alleen vergelijkingen tussen eerste ongebruikte elementen geteld. Hoeveel zijn dat er?
  1. 2
  2. 3
  3. 4
  4. 5
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 6 van 10

Een lus wordt n keer uitgevoerd. Bij elke herhaling wordt een tweede lus precies 4 keer uitgevoerd. Hoe vaak wordt de inhoud van de binnenste lus uitgevoerd?
  1. n+4
  2. n2
  3. 4n
  4. 4n
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 7 van 10

Een kwadratische sorteermethode kost 3 seconden voor 12.000 elementen. Hoe lang zouden 36.000 elementen onder vergelijkbare omstandigheden ongeveer moeten kosten?
  1. 27 seconden
  2. 9 seconden
  3. 18 seconden
  4. 81 seconden
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 8 van 10

Een sorteerfunctie ontvangt [4, 4, -2] en geeft [-2, 4] terug. Aan welke eis heeft die niet voldaan?
  1. Oplopende volgorde van sleutels
  2. Behoud van elk voorkomen
  3. Het kleinste element eerst teruggeven
  4. Negatieve waarden vooraan plaatsen
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 9 van 10

Gebruik voor een onveranderde ongesorteerde verzameling deze kostenschattingen: sorteren kost 9.000 eenheden, elke binaire zoekopdracht 12 eenheden en elke lineaire zoekopdracht 600 eenheden. Welk plan kost minder bij 20 zoekopdrachten en hoeveel minder?
  1. Lineair zoeken, 2.760 eenheden minder
  2. Lineair zoeken, 3.000 eenheden minder
  3. Sorteren en binair zoeken, 3.000 eenheden minder
  4. Sorteren en binair zoeken, 2.760 eenheden minder
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 10 van 10

Een taak mag slechts een vaste hoeveelheid extra werkruimte gebruiken en moet het aantal verwisselingen minimaliseren. Welke keuze uit de behandelde sorteermethoden voldoet het best aan die prioriteiten?
  1. Mergesort, bij gelijke sleutels eerst links
  2. Selectionsort, met één verwisseling per doorgang
  3. Bubblesort, met een indicator voor vroegtijdig stoppen
  4. Mergesort, met één laatste samenvoeging
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Set 3: Leg grenzen uit en vergelijk afwegingen10 Oefenvragen

Sla deze les op om elk antwoord te zien en elke set te oefenen.

Vraag 1 van 10

Binair zoeken gebruikt grenzen die meetellen en naar beneden afgeronde middenposities op [5, 11, 16, 24, 38, 47, 59, 70]. Welke indexen worden gecontroleerd voor de ontbrekende zoekwaarde 41?
  1. 3, 5, 4
  2. 3, 4, 5
  3. 4, 6, 5
  4. 3, 5, 6
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 2 van 10

Records komen binnen als Eli 72, Jo 65, Kai 72, Luz 65. Welke volgorde ontstaat bij een stabiele oplopende sortering op score?
  1. Luz, Jo, Eli, Kai
  2. Jo, Luz, Kai, Eli
  3. Jo, Luz, Eli, Kai
  4. Eli, Kai, Jo, Luz
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 3 van 10

Wat is de lijst na de tweede doorgang van selectionsort op [18, 7, 12, 4, 9]?
  1. [4, 7, 9, 12, 18]
  2. [7, 4, 12, 18, 9]
  3. [4, 9, 12, 18, 7]
  4. [4, 7, 12, 18, 9]
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 4 van 10

Mergesort splitst een lijst met 32 elementen in helften tot afzonderlijke elementen overblijven. Hoeveel samenvoegingsniveaus bouwen de lijst weer op?
  1. 4
  2. 5
  3. 6
  4. 16
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 5 van 10

Methode P gebruikt 60n stappen en methode Q gebruikt n2. Wanneer gebruikt P bij positieve invoergroottes minder stappen?
  1. Als n>60
  2. Als n<60
  3. Als n≥60
  4. Als n≤60
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 6 van 10

Een kwadratisch proces kost 5 seconden voor 8.000 elementen. Hoeveel elementen zijn volgens hetzelfde model ongeveer mogelijk binnen een budget van 45 seconden?
  1. 24.000 elementen na schalen met 9
  2. 72.000 elementen na schalen met 9
  3. 16.000 elementen na schalen met 2
  4. 24.000 elementen na schalen met 3
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 7 van 10

Een binaire zoekmethode met grenzen die meetellen behoudt elke mogelijke overeenkomst binnen het interval, maar zet na een te kleine middelste waarde low = mid. Welk deel van een volledig correctheidsargument kan falen?
  1. De preconditie over de invoervolgorde
  2. De garantie dat de uitvoering stopt
  3. De gelijkheidstest bij een overeenkomst
  4. Het behoud van oorspronkelijke elementen
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 8 van 10

Een lijst staat al op volgorde van binnenkomst. Die moet nu op prioriteit worden gesorteerd met behoud van de volgorde van binnenkomst binnen gelijke prioriteiten. Welke eigenschap van de sortering is nodig?
  1. Vaste extra opslag
  2. Weinig verwisselingen die elementen verplaatsen
  3. Stabiliteit bij gelijke sleutels
  4. Een kwadratische grens voor vergelijkingen
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 9 van 10

Een volledige doorgang van bubblesort maakt geen verwisselingen. Wat stelt dat vast over de hele lijst, inclusief een eventueel al vaststaand einddeel?
  1. De lijst is gesorteerd.
  2. De waarden zijn verschillend.
  3. De lijst heeft één element.
  4. De sleutels zijn gelijk.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 10 van 10

Een stabiele samenvoeging combineert linker records [2A, 6B] en rechter records [2C, 5D] en vergelijkt alleen numerieke sleutels. Wat is de volgorde van de samengevoegde records?
  1. 2C, 2A, 5D, 6B
  2. 2A, 5D, 2C, 6B
  3. 2A, 2C, 5D, 6B
  4. 2A, 2C, 6B, 5D
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Set 4: Analyseer doorlopen en onderbouw beslissingen10 Oefenvragen

Sla deze les op om elk antwoord te zien en elke set te oefenen.

Vraag 1 van 10

Een standaard lineaire zoekmethode maakt 4 vergelijkingen op [14, 6, 21, 6]. Als ze direct bij de eerste overeenkomst stopt, welk paar van zoekwaarde en resultaat is dan mogelijk?
  1. Zoekwaarde 6, teruggegeven 3
  2. Zoekwaarde 21, teruggegeven 2
  3. Zoekwaarde 8, teruggegeven -1
  4. Zoekwaarde 14, teruggegeven 0
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 2 van 10

Een binaire zoekmethode met grenzen die meetellen heeft de grenzen low = 6 en high = 6, en het resterende element is gelijk aan de zoekwaarde. Wat doet de correcte lus?
  1. Controleert index 6 en geeft 6 terug
  2. Geeft -1 terug zonder te controleren
  3. Verplaatst de ondergrens naar 7 vóór controle
  4. Controleert index 5 en geeft 5 terug
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 3 van 10

Hoeveel inversies zijn er in [5, 3, 5, 2, 4, 1]?
  1. 10
  2. 11
  3. 12
  4. 15
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 4 van 10

Selectionsort gebruikt strikt < bij het vinden van elk minimum. Wat is de uiteindelijke volgorde van de records in [7A, 3X, 7B, 2Y, 3Z] als alleen numerieke sleutels worden vergeleken?
  1. 2Y, 3Z, 3X, 7A, 7B
  2. 2Y, 3X, 3Z, 7B, 7A
  3. 2Y, 3Z, 3X, 7B, 7A
  4. 2Y, 3X, 3Z, 7A, 7B
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 5 van 10

Gebruik mergesort met naar beneden afgeronde halveringen op [9, 1, 7, 3]. Hoeveel vergelijkingen tussen elementen maakt de volledige sortering als je alleen vergelijkingen bij samenvoegingen telt?
  1. 2
  2. 3
  3. 4
  4. 5
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 6 van 10

Een algoritme vergelijkt eerst elk paar verschillende elementen één keer en doorloopt daarna alle n elementen één keer, met één vergelijking per bekeken element. Welke formule geldt voor het totale aantal vergelijkingen?
  1. n(n−1)/2
  2. n(n+1)/2
  3. n2+n
  4. n(n−1)
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 7 van 10

Een proces probeert elke selectie van n onafhankelijke elementen, voor 2n selecties. Wat gebeurt er met dit aantal als één element wordt toegevoegd?
  1. Het stijgt met één.
  2. Het wordt gekwadrateerd.
  3. Het verdubbelt.
  4. Het verviervoudigt.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 8 van 10

Herhaalde uitvoeringen bij verdubbelende invoergroottes kosten ongeveer 0,06, 0,24 en 0,96 seconden. Welk groeipatroon ondersteunen deze verhoudingen?
  1. Kwadratische groei
  2. Lineaire groei
  3. Constante groei
  4. Logaritmische groei
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 9 van 10

Een bewijs laat zien dat een sorteerlus stopt met elk paar buren op volgorde. De code overschrijft ook enkele elementen zonder ze te bewaren. Welke aanvullende eigenschap moet worden vastgesteld voordat je de sortering correct kunt noemen?
  1. Elke doorgang gebruikt minder vergelijkingen.
  2. Elk oorspronkelijk element behoudt zijn aantal.
  3. Elke gelijke sleutel behoudt zijn oude positie.
  4. Elke uitvoering gebruikt vast extra geheugen.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 10 van 10

Een willekeurige test vindt een sorteerfout op een lange lijst. Welke volgende stap helpt het best om de fout te vinden en te voorkomen dat die terugkomt?
  1. Bewaar de volledige lijst en doorloop elke positie herhaaldelijk.
  2. Vervang de invoer door een gebruikelijke lijst en bewaar het resultaat.
  3. Genereer meer willekeurige lijsten voordat je de eerste fout onderzoekt.
  4. Verklein de falende invoer en bewaar die als regressietest.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Kom je niet uit je eigen opgave? Huiswerkhulp

Kernbegrippen

62 Kernbegrippen, elk met een definitie

A

aangrenzende elementen
Aangrenzende elementen staan op naburige lijstposities, zodat hun indexen precies één verschillen als beide posities bestaan.
Bubblesort vergelijkt items[j] met items[j + 1], waardoor de verplaatsing afhangt van naburige paren.
afweging
Een afweging is een keuze waarbij verbetering van de ene nuttige eigenschap ten koste kan gaan van een andere, zoals vergelijkingen besparen door meer geheugen te gebruiken.
Mergesort verbetert de tijdsgroei bij grote invoer, maar heeft meestal meer tijdelijke opslag nodig dan selectionsort.

B

basisbewerking
Een basisbewerking is de herhaalde handeling die wordt gekozen om werk te tellen, zoals twee sleutels vergelijken of een middelste element controleren.
Een controle van het midden bij binair zoeken kan twee vergelijkingen tussen elementen uitvoeren, dus moet het aantal controles apart worden aangeduid.
basisgeval
Een basisgeval is invoer die rechtstreeks wordt afgehandeld zonder nog een recursieve aanroep en zo een stoppunt voor de recursie biedt.
Bij mergesort worden zowel een lege lijst als een lijst met één element ongewijzigd teruggegeven omdat ze al gesorteerd zijn.
beste geval
Het beste geval is de geldige invoersituatie die de minste getelde bewerkingen vereist van alle invoer met dezelfde grootte.
Een lineaire zoekopdracht die de zoekwaarde op de eerste positie vindt, maakt op een niet-lege lijst één vergelijking.
binair zoeken
Bij binair zoeken gebruik je de gesorteerde volgorde om een zoekinterval inclusief beide grenzen te verkleinen door de zoekwaarde met een middelste element te vergelijken.
Zoeken in een gesorteerde array met ongeveer een miljoen elementen vereist hoogstens 20 controles van een middelste element.

C

constante groei
Constante groei betekent dat het getelde werk begrensd blijft onafhankelijk van de invoergrootte, zoals bij het lezen van één bekende arraypositie.
Eén opgegeven index opzoeken vereist niet dat je elk eerder arrayelement doorloopt als de lijst groeit.

D

directe toegang
Directe toegang betekent dat je een element via zijn index bereikt zonder eerdere elementen te doorlopen, zoals mogelijk is bij een array of Pythonlijst.
Om via schakels het midden van een gekoppelde lijst te bereiken moet je extra elementen doorlopen, waardoor het voordeel van binair zoeken kleiner wordt.
dominante term
Een dominante term is het snelst groeiende deel van een uitdrukking voor een aantal stappen en bepaalt de groei op lange termijn als de invoergrootte toeneemt.
In n2/2−n/2 overheerst de kwadratische term, hoewel beide termen het exacte aantal vergelijkingen beïnvloeden.
doorgang
Een doorgang is één keer door het actieve deel van een lijst gaan, waarbij een reeks vergelijkingen of plaatsingen wordt uitgevoerd.
Een doorgang van bubblesort zet het grootste resterende element op zijn plaats, maar kan de eerdere posities ongesorteerd laten.

E

eerste ongebruikte element
Het eerste ongebruikte element bij een samenvoeging is het eerste nog niet gebruikte element van één gesorteerde invoerlijst en daarmee het kleinste resterende element daarvan.
Na het nemen van het eerste ongebruikte element links schuif je alleen de linker markering op; het eerste ongebruikte element rechts wacht nog op vergelijking.
exponentiële groei
Exponentiële groei vermenigvuldigt het werk met een vaste factor voor elk extra element; een aantal volgens 2n verdubbelt als één element wordt toegevoegd.
Elke selectie uit 30 elementen proberen vereist twee keer zoveel selecties als elke selectie uit 29 elementen proberen.

F

fout van één te veel of te weinig
Een fout van één te veel of te weinig zorgt ervoor dat een lus- of indexgrens één positie te veel of te weinig omvat, waardoor een element wordt gemist of een ongeldige positie wordt benaderd.
Als je low < high gebruikt voor een binair zoekinterval met grenzen die meetellen, wordt een laatste kandidaat overgeslagen wanneer de grenzen gelijk worden.

G

gemiddelde geval
Het gemiddelde geval beschrijft het gemiddelde werk onder een bepaalde invoerverdeling, zodat de waarde afhangt van welke invoer voorkomt.
Gelijke kansen voor succesvolle posities van de zoekwaarde geven lineair zoeken gemiddeld (n+1)/2 vergelijkingen.
gesorteerd begindeel
Een gesorteerd begindeel is een deel aan het begin van een lijst dat al op volgorde staat; selectionsort vult het eerst met de kleinste elementen.
Na drie doorgangen van selectionsort bevatten de eerste drie posities de drie kleinste elementen op volgorde.
groeisnelheid
Een groeisnelheid beschrijft hoe het werk van een algoritme verandert met de invoergrootte en onderscheidt het groeipatroon van één afzonderlijke uitvoering.
Een korte tijd bij één kleine grootte laat open hoe snel die tijd stijgt als de hoeveelheid gegevens veel groter wordt.

I

in-placesortering
Een in-placesortering herschikt de bestaande lijst met slechts een vaste hoeveelheid extra werkruimte als de invoergrootte groeit.
Bubblesort heeft enkele tellers en een indicator voor verwisselingen nodig in plaats van nog een lijst die evenredig is met de invoer.
index
Een index is een genummerde positie waarmee je toegang krijgt tot een element van een lijst; Pythonindexen beginnen bij nul en eindigen één vóór de lengte van de lijst.
In [8, 13, 21] bevat index 2 de waarde 21, ook al is dat in het dagelijks tellen het derde element.
inversie
Een inversie is een paar posities waarbij een eerder element een grotere sleutel heeft dan een later element; gelijke sleutels leveren geen inversie op.
In [8, 3, 6] vormt 8 een inversie met beide latere waarden, hoewel slechts één paar aangrenzend is.
invoergrootte
De invoergrootte meet hoeveel gegevens een algoritme ontvangt; voor de lijstalgoritmen hier is dat het aantal elementen, n.
Twee lijsten met dezelfde lengte hebben dezelfde invoergrootte, ook als de ene gesorteerd is en de andere omgekeerd.

K

kwadratische groei
Kwadratische groei betekent dat werk evenredig met het kwadraat van de invoergrootte groeit, waardoor bij verdubbeling ongeveer viermaal zoveel werk ontstaat.
Het aantal vergelijkingen van selectionsort, dat lijkt op het tellen van paren, groeit kwadratisch, hoewel het aantal verwisselingen veel langzamer groeit.

L

lineair zoeken
Bij lineair zoeken controleer je elementen vanaf het begin achtereenvolgens en stop je bij de eerste overeenkomst of meld je na de volledige lijst dat de waarde ontbreekt.
Om het laatste element in een lijst met negen elementen met lineair zoeken te vinden, zijn negen vergelijkingen van een element met de zoekwaarde nodig.
lineaire groei
Lineaire groei betekent dat werk recht evenredig met de invoergrootte stijgt, zodat een verdubbeling van het aantal elementen het werk ongeveer verdubbelt.
Een volledige doorloop met één sleutelvergelijking per element maakt 2.000 vergelijkingen als de invoer groeit tot 2.000 elementen.
logaritmische groei
Logaritmische groei ontstaat als herhaald verkleinen met een vaste factor ervoor zorgt dat elke verdubbeling van de invoer slechts een vaste hoeveelheid werk toevoegt.
Door herhaald halveren heeft binair zoeken ongeveer één extra controle van het midden nodig telkens als de lijstgrootte verdubbelt.
lusinvariant
Een lusinvariant is een uitspraak die waar is voordat de lus begint en na elke herhaling waar blijft, en zo een argument voor het resultaat ondersteunt.
De invariant van selectionsort verbindt de lengte van het begindeel met de kleinste elementen die al zijn geplaatst.

M

mergesort
Mergesort sorteert recursief twee kortere helften en voegt de gesorteerde resultaten samen, waardoor het werk evenredig met nlog⁡n groeit.
Een grote lijst doorloopt verschillende splitsingsgroottes voordat de gesorteerde delen weer tot één uitvoer worden opgebouwd.
middenpositie
De middenpositie is de middelste positie die binnen een zoekinterval wordt gekozen, naar beneden afgerond als het gemiddelde van de grenzen geen geheel getal is.
Voor de meetellende grenzen 2 en 7 kiest (2 + 7) // 2 index 4 als volgende middenpositie.

O

omslagpunt
Een omslagpunt is een invoergrootte waarbij twee kostenmodellen gelijk zijn en waar voorbij hun onderlinge volgorde kan veranderen als de invoer groeit.
De modellen 40n en n2 zijn gelijk bij 40 elementen; het lineaire model wordt goedkoper bij grotere positieve groottes.
ongesorteerd einddeel
Een ongesorteerd einddeel is het resterende deel aan het einde waarvan de onderlinge volgorde door het sorteerproces nog niet is vastgesteld.
Selectionsort doorzoekt alleen het ongesorteerde einddeel, zodat het een al geplaatst minimum niet opnieuw selecteert.
overloop van gehele getallen
Overloop van gehele getallen ontstaat als een berekening het bereik van een geheel getal met vaste bitbreedte overschrijdt; het effect hangt af van de taal en de bewerking.
Een middenpositie op basis van het verschil voorkomt een te grote som van de grenzen als de indexen niet-negatief en geldig zijn.

P

permutatie
Een permutatie is een herschikking van dezelfde elementen, waarbij het aantal voorkomens van elke waarde behouden blijft.
[2, 8, 2] en [2, 2, 8] zijn permutaties van elkaar; [2, 8] verliest één voorkomen.
positiemarkering
Een positiemarkering houdt de volgende ongebruikte positie in een invoerlijst bij, zodat een samenvoeging weet welk element ze daarna moet vergelijken of kopiëren.
Afzonderlijke linker en rechter positiemarkeringen voorkomen dat een samenvoeging na een vergelijking een element tweemaal kopieert of overslaat.
postconditie
Een postconditie geeft aan wat waar moet zijn voor het resultaat van een algoritme als het eindigt op invoer die aan de preconditie voldoet.
Een postconditie voor sorteren omvat zowel geordende sleutels als het behoud van de oorspronkelijke voorkomens.
preconditie
Een preconditie is een eis waaraan invoer moet voldoen voordat het beloofde gedrag van een algoritme gegarandeerd is.
Oplopende volgorde is een preconditie voor gewoon binair zoeken, dus maakt ongesorteerde invoer de redenering over het interval ongeldig.

R

randgeval
Een randgeval is geldige invoer op een grens van het gedrag van een algoritme, waar lusgrenzen of een speciale afhandeling kunnen falen.
Met een lege lijst controleer je of binair zoeken de grenzen toetst voordat het probeert een middelste element te benaderen.
recursie
Recursie lost een probleem op via aanroepen van dezelfde procedure voor kleinere instanties, met een stopregel voor eenvoudige gevallen.
Mergesort roept zichzelf aan op elke kortere helft totdat een aanroep hoogstens één element ontvangt.
recursief geval
Een recursief geval brengt het huidige probleem terug tot kleinere instanties en gebruikt hun resultaten om het antwoord op te bouwen.
Het recursieve geval van mergesort sorteert twee helften voordat de teruggegeven gesorteerde lijsten worden samengevoegd.
referentiemeting van de uitvoeringstijd
Een referentiemeting van de uitvoeringstijd is een gemeten uitvoeringstijd bij een bekende invoergrootte die als uitgangspunt dient voor een schatting op basis van de grootte.
Een referentiemeting van 4 seconden bij 20.000 elementen kan een kwadratische schatting voor een grotere vergelijkbare uitvoering onderbouwen.
regressietest
Een regressietest bewaart een controle van eerder correct of hersteld gedrag, zodat latere wijzigingen een terugkerende fout blootleggen.
Bewaar een minimale lijst die ooit een dubbel element verloor om te controleren of toekomstige wijzigingen alle exemplaren behouden.
resterend lijstdeel
Een resterend lijstdeel is het deel van een lijst vanaf een gekozen positie, dat direct wordt gekopieerd als de andere invoerlijst bij het samenvoegen leeg raakt.
Als één invoerlijst bij het samenvoegen leeg raakt terwijl aan de andere kant [14, 20] overblijft, zijn beide elementen van het resterende lijstdeel nog nodig.
resterend zoekinterval
Het resterende zoekinterval is het interval van posities dat de zoekwaarde nog kan bevatten nadat eerdere vergelijkingen de rest hebben uitgesloten.
De grenzen 4 tot en met 7 omvatten vier mogelijke posities en de grenzen 5 tot en met 4 beschrijven een leeg interval.

S

samenvoegen
Bij samenvoegen combineer je twee gesorteerde lijsten door steeds het kleinste eerste ongebruikte element te kopiëren en daarna een eventueel resterend lijstdeel te kopiëren.
Het samenvoegen van [3, 10] en [6, 12] geeft [3, 6, 10, 12] zonder een van beide invoerlijsten opnieuw te sorteren.
selectionsort
Selectionsort doorzoekt herhaaldelijk het ongesorteerde deel naar het minimum en verwisselt dat element met het element op de volgende positie van een gesorteerd begindeel.
Een doorloop van selectionsort onthoudt de index van het kleinste element voordat de enige mogelijke verwisseling die elementen verplaatst wordt uitgevoerd.
slechtste geval
Het slechtste geval is de geldige invoersituatie die de meeste getelde bewerkingen vereist van alle invoer met dezelfde grootte.
Een ontbrekende zoekwaarde dwingt eenvoudig lineair zoeken om elke positie te bekijken, wat het aantal in het slechtste geval oplevert.
sorteerfunctie uit een bibliotheek
Een sorteerfunctie uit een bibliotheek is een geteste sorteerimplementatie van een taal of bibliotheek, met gedocumenteerd gedrag zoals stabiliteit en uitvoervorm.
sorted van Python maakt een nieuwe resultatenlijst, terwijl list.sort() de bestaande lijst herschikt.
sorteersleutel
Een sorteersleutel is de waarde of het recordveld waarmee de volgorde wordt bepaald; de rest van het record blijft eraan gekoppeld als het element verplaatst.
Als je stadsrecords op inwonertal sorteert, gaat elke stadsnaam mee met het bijbehorende inwonertal.
sorteren
Bij sorteren zet je elementen op volgorde van hun sleutel, met behoud van elk oorspronkelijk element en elk herhaald voorkomen.
Een oplopend resultaat dat één dubbel element verliest, voldoet niet aan de correctheidseisen voor sorteren, ook als alle buren op volgorde staan.
specificatie
Een specificatie vermeldt de eisen voor geldige invoer en het resultaat dat een algoritme moet produceren, zodat correctheid controleerbaar wordt.
Een zoekspecificatie maakt onderscheid tussen een overeenkomende index en een speciaal resultaat dat alleen wordt gebruikt als er geen overeenkomst is.
stabiele sortering
Een stabiele sortering behoudt de oorspronkelijke onderlinge volgorde van records waarvan de sleutels bij vergelijking gelijk zijn, ook als andere records verplaatsen.
Een stabiele sortering op score houdt alfabetisch geordende namen binnen elke scoregroep in alfabetische volgorde.

T

tegenvoorbeeld
Een tegenvoorbeeld is geldige invoer waarvan het waargenomen gedrag een beweerde algemene eigenschap schendt en die bewering daarmee weerlegt.
Eén gesorteerde lijst met twee elementen die een oneindige zoeklus veroorzaakt, weerlegt de bewering dat de zoekmethode altijd termineert.
terminatie
Terminatie betekent dat de uitvoering uiteindelijk stopt; een afnemende niet-negatieve gehele maat kan dit voor een lus aantonen.
Door de gecontroleerde middenpositie uit te sluiten, neemt het aantal kandidaten bij binair zoeken na elke controle zonder overeenkomst strikt af.
testen met willekeurige invoer
Bij testen met willekeurige invoer genereer je gevarieerde invoer en controleer je resultaten tegen een specificatie of betrouwbare methode om onverwachte fouten te vinden.
Door veel willekeurig gegenereerde sorteerresultaten met sorted te vergelijken, kun je een geval vinden dat zorgvuldig gekozen tests hebben gemist.
tijdsbudget
Een tijdsbudget is de toegestane uitvoeringstijd, die je met een kostenmodel gebruikt om te schatten hoe groot de te verwerken invoer kan zijn.
Een kwadratisch proces kan drie keer zoveel invoer verwerken als de beschikbare tijd met een factor negen toeneemt.

V

verdeel en heers
Verdeel en heers lost een probleem op door het in kleinere problemen te splitsen, die op te lossen en hun resultaten te combineren.
Mergesort combineert gesorteerde resultaten van helften; alleen splitsen laat de oorspronkelijke volgorde van waarden onveranderd.
verdubbelingstest
Een verdubbelingstest vergelijkt kosten bij opeenvolgende verdubbelde invoergroottes om te zoeken naar de verhoudingen die bij groeifamilies horen.
Herhaalde verhoudingen tussen gemeten tijden rond vier wijzen op kwadratische groei over het gemeten bereik van groottes.
verwisseling
Een verwisseling wisselt twee opgeslagen elementen om en behoudt beide oorspronkelijke waarden, met een bewaarde waarde of een gelijktijdige toekenning.
Zonder een waarde te bewaren kunnen twee opeenvolgende toekenningen [2, 9] veranderen in [9, 9] in plaats van de waarden om te wisselen.
volledige doorloop
Een volledige doorloop bekijkt elk element in een verzameling, zoals nodig is om alle overeenkomsten te tellen of de grootste waarde te vinden.
Bij het tellen van elk voorkomen van 7 ga je na de eerste overeenkomst door, omdat latere elementen ook 7 kunnen bevatten.
voorbereidingskosten
Voorbereidingskosten zijn het werk dat vóór de hoofdbewerking nodig is, zoals een lijst sorteren zodat latere binaire zoekopdrachten geldig zijn.
Bij één zoekopdracht kan een lineaire doorloop de voorkeur hebben omdat dat plan hoge eenmalige sorteerkosten vermijdt.
voorspelling van de uitvoeringstijd
Een voorspelling van de uitvoeringstijd schat de uitvoeringstijd bij een andere grootte door een groeimodel toe te passen op een gemeten of gegeven referentietijd.
In een kwadratisch model vermenigvuldigt verdrievoudiging van de invoer de referentietijd met ongeveer negen, niet met drie.
vroegtijdige beëindiging
Vroegtijdige beëindiging stopt een algoritme vóór het maximaal geplande werk zodra een gecontroleerde voorwaarde het vereiste resultaat vaststelt.
Een doorgang zonder verwisselingen bevestigt bij bubblesort de volgorde; de vergelijkingen in die doorgang tellen nog steeds mee voor het totaal.

Z

zoeken
Bij zoeken controleer je of een zoekwaarde in een verzameling voorkomt. Je kunt daarbij de positie aangeven of een resultaat dat betekent dat de waarde ontbreekt.
Als je een lijst met record-ID’s doorzoekt, kun je de positie van het gevraagde record terugkrijgen zonder de lijst te veranderen.
zoekwaarde
Een zoekwaarde is de waarde die een zoekmethode zoekt en vergelijkt met opgeslagen elementen of met een gekozen veld van elk opgeslagen record.
Bij een zoekopdracht naar ID 630 is de zoekwaarde 630; de teruggegeven positie is een ander getal.

Over deze les

Door StudeliFout melden

Maak je eigen Studiemateriaal

Maak van je aantekeningen, slides en documenten Leermodules, Oefenvragen, Flashcards en 6 andere tools.

Aan de slag