Tilbake
2.3

2.3 Utvelgelse — Partition, Randomized-Select og Select

Finne det `i`-te minste elementet uten å sortere: `Partition`, `Randomized-Select` og `Select` (median av medianer) med kjøretider.

45 min
5 oppgaver
UtvelgelsePartitionRandomized-SelectSelect
Din fremgang i kapitlet
0 / 5 oppgaver

Forkunnskaper

- kap. 2.1 — sammenligningsbaserte sorteringer. Fra det
kapitlet: Quicksort partisjonerer om et pivot og sorterer de to sidene
rekursivt, og den er Θ(nlgn)\Theta(n\lg n) forventet og Θ(n2)\Theta(n^2) i verste
tilfelle. Vi bruker den samme Partition her, men denne gangen bare på
den ene siden.
- kap. 1.1 — de asymptotiske symbolene. Skillet mellom
«forventet» og «verste tilfelle» er selve poenget i dette kapitlet.
- kap. 1.5 — iterasjonsmetoden. Rekurrensen
T(n)=T(n/2)+Θ(n)T(n) = T(n/2) + \Theta(n) løses lettest ved å summere en geometrisk rekke.

Notasjons- og pseudokodeliste

Utvelgelsesproblemet (~6 min)

En kommune har 240 000 innbyggere og vil vite medianinntekten — beløpet der
halvparten tjener mindre og halvparten mer. Den enkleste framgangsmåten er å
sortere alle inntektene og lese av det midterste tallet. Det koster
Θ(nlgn)\Theta(n\lg n).

Men spørsmålet er mye smalere enn en sortering. Vi vil ha ett tall, ikke
hele rekkefølgen. Og det viser seg at det ene tallet kan hentes ut i lineær
tid.

Utvelgelsesproblemet
Utvelgelse er problemet: gitt nn tall og et ordenstall ii, finn det
ii-te minste elementet.

Med i=1i = 1 er det minimum, med i=ni = n er det maksimum, og med
i=(n+1)/2i = \lfloor (n+1)/2 \rfloor er det medianen.

Det er et lettere problem enn sortering, og det er nettopp poenget: en
Θ(nlgn)\Theta(n\lg n)-sortering løser det, men den gjør mye mer arbeid enn
nødvendig.

Ordensstatistikk og median

Den ii-te ordensstatistikken i en mengde er det ii-te minste elementet.

Medianen er den midterste ordensstatistikken. Med odde nn er den entydig;
med like nn finnes en nedre og en øvre median, og denne boka mener den nedre,
altså i=(n+1)/2i = \lfloor (n+1)/2 \rfloor.

Minimum og maksimum finnes trivielt i Θ(n)\Theta(n) ved ett gjennomløp — det
er de generelle ordenstallene som er interessante.

📝Oppgave 1

(Innstegsoppgave, sjanger D — definisjon med egne ord, altså én presis setning
med hovedpoenget først.)

Forklar hva utvelgelsesproblemet er, og hvorfor en sortering er en
«overdimensjonert» løsning på det.

Partition, linje for linje (~14 min)

Både utvelgelsesalgoritmene og Quicksort hviler på den samme rutinen. Den er
verdt å kunne utenat, fordi den er en fast håndkjøringsoppgave og fordi den
dukker opp i «hvilken linje mangler?»-varianten.

Partition velger siste element i delen som pivot og går gjennom delen én gang
fra venstre. Underveis holder den et skille: alt til venstre for skillet er
mindre enn eller lik pivoten. Til slutt settes pivoten inn på skilleplassen.

📜Pseudokode-kontrakt: `Partition`
Antagelser om representasjon. Arrayet er A[1..n], indeks fra 1. Kallet
arbeider på delen A[p..r] med p <= r. Pivoten er siste element, A[r].
Rutinen går på stedet.

Prebetingelse: p <= r.
Postbetingelse: rutinen returnerer en indeks q med p <= q <= r, slik at
A[p..q-1] alle er \le A[q] og A[q+1..r] alle er >> A[q]. Elementet
på plass q er pivoten, og det står på den plassen det ville hatt i et ferdig
sortert array.

Partition(A, p, r)
  Input:  array A[p..r]
  Output: pivotens sluttindeks q
  x = A[r]
  i = p - 1
  for j = p to r - 1
      if A[j] <= x
          i = i + 1
          bytt A[i] og A[j]
  bytt A[i+1] og A[r]
  return i + 1
  Kjoeretid: Theta(r - p + 1)

Invarianten i én setning: rett før hver runde med indeks j er alt i
A[p..i] mindre enn eller lik pivoten, alt i A[i+1..j-1] er større enn
pivoten, og A[r] er pivoten selv.

Legg merke til at i starter på p - 1. Den venstre regionen er tom til
å begynne med, og i peker alltid på det siste elementet i den. Er alle
elementene større enn pivoten, blir i liggende på p - 1, og pivoten havner
på plass p.

Kjøretid: for-løkka gjør r - p runder med konstant arbeid, pluss ett
bytte til slutt — altså Θ(rp+1)\Theta(r-p+1), lineært i lengden på delen.

✏️Eksempel 1: `Partition` med siste element som pivot

Sju måleverdier ligger slik: A = 53, 18, 67, 24, 71, 39, 45 (indeks fra 1).

Kjør Partition(A, 1, 7). Oppgi arrayet etter partisjoneringen og pivotens
sluttindeks q.

Pivoten er A[7] = 45, og i starter på 0.

jTesti etterpåArray etter runden
1A[1]=53 >> 4553, 18, 67, 24, 71, 39, 45
2A[2]=18 \le 45118, 53, 67, 24, 71, 39, 45
3A[3]=67 >> 4518, 53, 67, 24, 71, 39, 45
4A[4]=24 \le 45218, 24, 67, 53, 71, 39, 45
5A[5]=71 >> 4518, 24, 67, 53, 71, 39, 45
6A[6]=39 \le 45318, 24, 39, 53, 71, 67, 45

Etter løkka står i på 3. Det siste steget bytter A[i+1] og A[r], altså
A[4] og A[7].
Sluttilstanden — det du ville levert på eksamen:
Array: 18, 24, 39, 45, 71, 67, 53, og q = 4.
Kontrollen er enkel: alt til venstre for plass 4 er \le
45, og alt til høyre er >> 45. Legg merke
til at høyresiden ikke er sortert — Partition lover ingenting om

rekkefølgen innenfor hver side.
Oppgaven ba om arrayet og q. Ba den bare om q, er det ene tallet hele

svaret.

📝Oppgave 2
Eksamensnivå, sjanger C

Kjør Partition(A, 1, 6)A = 12, 45, 7, 33, 21, 19 med siste element som
pivot.

a) Oppgi arrayet etter partisjoneringen.
b) Oppgi pivotens sluttindeks q.

📝Oppgave 3
Eksamensnivå, sjanger F

En kandidat skriver: «Etter Partition er venstresiden sortert stigende og
høyresiden sortert stigende.»

Stemmer det? Svar ja eller nei, og begrunn.

Randomized-Select (~15 min)

Nå kommer ideen som gjør utvelgelse lineær. Quicksort partisjonerer og går
rekursivt inn i begge sidene. Men leter du etter det ii-te minste
elementet, vet du etter partisjoneringen nøyaktig hvilken side svaret ligger
på — og den andre siden kan kastes.

Etter Partition(A, p, r) som returnerer q, står pivoten som nummer
k = q - p + 1 innenfor delen. Er i = k, er pivoten svaret. Er i < k,
ligger svaret til venstre. Er i > k, ligger det til høyre, og du leter der
etter det (i - k)-te minste — fordi k elementer nå er strøket bort under
det du leter etter.

📜Pseudokode-kontrakt: `Randomized-Select`
Antagelser om representasjon. Arrayet er A[1..n], indeks fra 1. Kallet
utenfra er Randomized-Select(A, 1, A.length, i) med
1 <= i <= A.length. Elementene antas forskjellige.
Randomized-Partition bytter et tilfeldig element i delen til plass r og
kaller Partition.

Prebetingelse: 1 <= i <= r - p + 1.
Postbetingelse: rutinen returnerer det ii-te minste elementet i A[p..r].
Svaret er alltid riktig; det er bare kjøretiden som avhenger av de tilfeldige
trekkene.

Randomized-Select(A, p, r, i)
  Input:  array A[p..r] og et ordenstall i
  Output: det i-te minste elementet i A[p..r]
  if p == r
      return A[p]
  q = Randomized-Partition(A, p, r)
  k = q - p + 1
  if i == k
      return A[q]
  else if i < k
      return Randomized-Select(A, p, q-1, i)
  else
      return Randomized-Select(A, q+1, r, i-k)
  Kjoeretid: Theta(n) forventet, Theta(n^2) verste

Grunnideen i én setning: partisjoneringen forteller hvilken side svaret
ligger på, og den andre siden trenger aldri å ses på igjen.

Intuisjon for hvorfor det blir lineært. Antar vi at hvert kall halverer
delen, blir arbeidet n+n/2+n/4+n/8+n + n/2 + n/4 + n/8 + \dots, og den summen er mindre
enn 2n2n uansett hvor mange ledd du tar med. Rekurrensen
T(n)=T(n/2)+Θ(n)T(n) = T(n/2) + \Theta(n) gir altså Θ(n)\Theta(n) — i motsetning til
Quicksorts T(n)=2T(n/2)+Θ(n)=Θ(nlgn)T(n) = 2T(n/2) + \Theta(n) = \Theta(n\lg n), der begge sidene
må behandles. Det er den ene forkastede halvdelen som er hele gevinsten.

Kjøretid: Θ(n)\Theta(n) forventet. Verste tilfelle er Θ(n2)\Theta(n^2): gir
hvert pivotvalg en deling som skiller av bare ett element, blir rekurrensen
T(n)=T(n1)+Θ(n)T(n) = T(n-1) + \Theta(n).

✏️Eksempel 2: `Randomized-Select` finner det fjerde minste

Ni sensorutslag ligger slik: A = 38, 15, 72, 9, 54, 27, 63, 41, 20
(indeks fra 1). Vi vil ha det fjerde minste, altså i=4i = 4.

Her er kjøringen med disse tilfeldige pivotvalgene: først element nummer 5,
deretter nummer 2 i den gjenværende delen, deretter nummer 3, deretter nummer
4. (På eksamen er pivotene oppgitt eller valgt av deg; her viser vi ett
konkret løp.)

Følg hvilken del som står igjen etter hvert kall.

KallDelPivotqkHva som skjer
1A[1..9]5477i = 4 << k = 7: fortsett i A[1..6]
2A[1..6]1522i = 4 >> k = 2: fortsett i A[3..6] med i = 2
3A[3..6]3853i = 2 << k = 3: fortsett i A[3..4]
4A[3..4]2742i == k = 2, svaret er A[4] = 27

Sluttilstanden — svaret: 27
Kontroll: sortert er arrayet 9, 15, 20, 27, 38, 41, 54, 63, 72, og det
fjerde minste er 27 — men vi kom fram til det uten å
sortere.
Legg merke til hvordan i endrer seg. I det andre kallet gikk vi til

høyre side og satte i = 4 - 2 = 2: to elementer (pivoten og det ene til

venstre for den) ligger nå garantert under det vi leter etter, og er strøket
fra regnskapet. Å glemme dette fratrekket er den vanligste feilen i
håndkjøring av utvelgelse.

Tell arbeidet. Delene som ble partisjonert hadde lengde 9, 6, 4 og 2 —
til sammen 21 elementgjennomganger, ikke 9lg9299 \lg 9 \approx 29. Forskjellen
vokser fort med nn.

📝Oppgave 4
Eksamensnivå, sjanger E
a) Hva er forventet kjøretid for Randomized-Select?
b) Hva er verste-tilfelle-kjøretiden, og hvordan oppstår den?
c) Hvorfor er Quicksort Θ(nlgn)\Theta(n\lg n) forventet når
Randomized-Select er Θ(n)\Theta(n) forventet, når begge bruker samme
partisjonering?
📝Oppgave 5
Eksamensnivå, sjanger C
A = 30, 12, 47, 8, 25 (indeks fra 1), og du kjører Randomized-Select for
i=2i = 2. Det tilfeldige valget faller hver gang på siste element i den
gjeldende delen, altså oppfører algoritmen seg som Partition uten
omstokking.

Oppgi hvilke deler som partisjoneres, og hva svaret blir.

Select og den garanterte lineære tiden (~10 min)

Randomized-Select er rask nesten alltid, men gir ingen garanti. Det finnes en
variant som gjør det: Select, ofte kalt median av medianer. Den er
Θ(n)\Theta(n) i verste tilfelle.

Dette er et tema du skal kjenne til: du skal kunne si hva den garanterer og
hvordan pivoten velges, ikke utlede analysen.

`Select` (median av medianer)

finner det ii-te minste elementet i garantert Θ(n)\Theta(n) tid ved å velge
pivoten omhyggelig i stedet for tilfeldig.

Pivotvalget: del elementene i grupper på fem, finn medianen i hver gruppe, og
finn så rekursivt medianen av disse medianene. Den brukes som pivot.

Garantien kommer av at pivoten aldri er for skjev. Minst omtrent tre
tideler av elementene ligger på hver side av den, så hvert rekursivt kall
arbeider på høyst omtrent syv tideler av delen — og n+0,7n+0,49n+n + 0{,}7n + 0{,}49n + \dots er en konvergent geometrisk rekke, altså Θ(n)\Theta(n).

I praksis brukes likevel Randomized-Select: konstantene i Select er
store. Garantien er det teoretiske poenget.

Kjøretidene samlet

Dette er kapitlets puggeflate. Eksamen er hjelpemiddelfri, så tabellen må ligge
i hodet — og kolonnen «forventet mot verste» er der poengene faller.

OperasjonForventetVersteKrav / egenskap
PartitionΘ(n)\Theta(n)Θ(n)\Theta(n)ett gjennomløp, på stedet; pivoten er siste element
Randomized-PartitionΘ(n)\Theta(n)Θ(n)\Theta(n)ett tilfeldig bytte før partisjoneringen
Randomized-SelectΘ(n)\Theta(n)Θ(n2)\Theta(n^2)kaster den ene siden; ingen garanti
Select (median av medianer)Θ(n)\Theta(n)Θ(n)\Theta(n)garantert; grupper på fem; store konstanter
Minimum eller maksimumΘ(n)\Theta(n)Θ(n)\Theta(n)ett gjennomløp med én sammenligning per element
Median via sorteringΘ(nlgn)\Theta(n\lg n)Θ(nlgn)\Theta(n\lg n)korrekt, men gjør mer arbeid enn nødvendig
QuicksortΘ(nlgn)\Theta(n\lg n)Θ(n2)\Theta(n^2)går rekursivt inn i begge sidene

Én presisering som er verdt å ta med seg. Sortering kan ikke gjøres i
lineær tid med sammenligninger, men utvelgelse kan. Det er ingen motsetning:
utvelgelse er et mindre problem, og Ω(nlgn)\Omega(n\lg n)-grensen gjelder bare
sortering.

Begrepsbank

Begrepsbanken er flashcard- og repetisjonsstoff — den gjentar det du nettopp
har lest. Hopp trygt over ved førstegangslesing; tidsanslaget for kapitlet
gjelder kjernestoffet.

`Partition`

stokker A[p..r] om pivoten A[r] slik at alt \le pivoten havner til
venstre for den og alt >> havner til høyre, og returnerer pivotens
sluttindeks q.

Kjøretid Θ(rp+1)\Theta(r-p+1) — ett gjennomløp, på stedet.

Sidene blir ikke sortert. Det eneste som er ferdig etterpå, er pivotens
plassering.

Invarianten i `Partition`

rett før hver runde med indeks j er alt i A[p..i] \le pivoten, alt i
A[i+1..j-1] er >> pivoten, og A[r] er pivoten.

Indeksen i peker på det siste elementet i den venstre regionen, og starter
derfor på p - 1.

Invarianten er kontrollen din under håndkjøring: står det et for stort
element til venstre for i, har du gjort en feil.

`Randomized-Partition`

bytter et tilfeldig valgt element i A[p..r] til plass r og kaller
Partition.

Kjøretid Θ(rp+1)\Theta(r-p+1), som Partition.

Hensikten er ikke hastighet, men uavhengighet av inputen. Ingen bestemt
input kan lenger framtvinge en skjev deling.

`Randomized-Select`

finner det ii-te minste elementet ved å partisjonere og fortsette bare i
den siden svaret ligger i.

Kjøretid Θ(n)\Theta(n) forventet, Θ(n2)\Theta(n^2) verste.

Forventet er ikke garantert. Ordet må stå i svaret; utelates det, har du
lovet noe algoritmen ikke holder.

`Select` (median av medianer)

utvelgelse med garantert Θ(n)\Theta(n) i verste tilfelle, oppnådd ved å velge
pivoten som medianen av gruppemedianer i grupper på fem.

Kjøretid Θ(n)\Theta(n) verste.

Skilles fra Randomized-Select på nettopp garantien. I praksis er
konstantene så store at den tilfeldige varianten vinner.

Ordenstallet `k` i en deling

pivotens plassering innenfor delen, regnet som k = q - p + 1.

Er i == k, er pivoten svaret. Er i < k, ligger svaret til venstre. Er
i > k, ligger det til høyre, og du leter videre etter det (i - k)-te
minste.

Fratrekket i - k er den vanligste håndkjøringsfeilen i denne sjangeren.

Utvelgelse mot sortering

utvelgelse spør om ett element; sortering fastsetter rekkefølgen mellom
alle.

Utvelgelse kan gjøres i Θ(n)\Theta(n); sortering med sammenligninger kan ikke
komme under Ω(nlgn)\Omega(n\lg n).

Ingen motsetning: grensen gjelder det større problemet.

Ordensstatistikk

den ii-te ordensstatistikken er det ii-te minste elementet i mengden.

Minimum er den første, maksimum den nn-te, medianen den midterste.

Begrepet er språket oppgavetekstene bruker når de ber om «det ii-te
minste».

Median

den midterste ordensstatistikken, altså elementet der like mange ligger under
som over.

Med like nn finnes en nedre og en øvre median; denne boka mener den nedre,
i=(n+1)/2i = \lfloor (n+1)/2 \rfloor.

Kan finnes i Θ(n)\Theta(n) — det er hovedresultatet i dette kapitlet.

Grupper på fem i `Select`

elementene deles i n/5\lceil n/5 \rceil grupper à fem, hver gruppe får sin
median, og medianen av disse medianene brukes som pivot.

Tallet fem er valgt fordi det er det minste som gjør at den rekursive
regningen går opp.

Effekten er at pivoten aldri blir for skjev, og det er nettopp det
garantien hviler på.

Forventet mot garantert kjøretid
forventet er et gjennomsnitt over algoritmens egne tilfeldige valg;
garantert gjelder for enhver kjøring.

Randomized-Select er Θ(n)\Theta(n) forventet; Select er Θ(n)\Theta(n)
garantert.

Les alltid om oppgaven spør etter det ene eller det andre. Svaret er
forskjellig.

Rekurrensen T(n)=T(n/2)+Θ(n)T(n) = T(n/2) + \Theta(n)

utvelgelsesrekurrensen ved jevn deling: ett rekursivt kall på halve delen,
pluss lineært arbeid.

Løsningen er Θ(n)\Theta(n), fordi n+n/2+n/4+<2nn + n/2 + n/4 + \dots < 2n.

Kontrasten er T(n)=2T(n/2)+Θ(n)=Θ(nlgn)T(n) = 2T(n/2) + \Theta(n) = \Theta(n\lg n), altså
Quicksort, der begge sidene må behandles.

Sjanger C — håndkjøring

oppgavetypen der du utfører en navngitt algoritme steg for steg og oppgir
sluttilstanden.

For Partition er svarformen arrayet etterpå og pivotens sluttindeks q
begge deler når begge er spurt om.

Ingen ekstra uttelling for å forklare algoritmen.

Sjanger E — kjøretidskunnskap

oppgavetypen der du oppgir kjøretiden til en navngitt operasjon.

Svarformen er ett uttrykk i det strammeste som er riktig, med ordet
«forventet» der det hører hjemme.

Her bor felle #9: Randomized-Select sitt verste tilfelle er
Θ(n2)\Theta(n^2), ikke Θ(n)\Theta(n).

Repetisjonsoppgaver

Dette kapitlet er skrevet av Anthropics toppmodeller (Claude Opus og Claude Fable) og er foreløpig ikke manuelt gjennomgått — kvalitetskontrollen gjøres av uavhengige KI-agenter, og innmeldte feil rettes fortløpende. Funnet en feil? Meld fra, så retter vi den. Les mer om hvordan innholdet lages.

Skolesaga er en uavhengig læringsressurs og er ikke tilknyttet eller godkjent av Norges teknisk-naturvitenskapelige universitet. Dette er ikke offisielt studiemateriell. Les mer.