5.5 DRILL — Grafalgoritmer i pseudokode: mønstergjenkjenning
Den store Del 2-drillen (sjanger H): gjenkjenn det klassiske problemet i en innpakning, kall pensumalgoritmen, og velg lavest kjøretid.
Dette er kapitlet der karakteren avgjøres.
Eksamen i IN2010 er delt i tre: en oppvarming på to gratispoeng, en Del 1 med
mange små automatisk rettede oppgaver, og en Del 2 med noen få store oppgaver
der du skal skrive algoritmen selv. Sjanger H — grafalgoritmer i pseudokode,
altså at du får et problem i en innpakning og skal skrive ned algoritmen som
løser det og oppgi kjøretiden — er den tyngste Del 2-sjangeren, og den er i
hvert eneste sett med 2–3 oppgaver. De siste av dem er de vanskeligste i
hele settet.
Grunnstoffet er høyfrekvent hele veien: grafrepresentasjon og traversering er i
100 % (7 av 7 sett), tofarging og komponentresonnement i 57 % (4 av 7), og
sterkt sammenhengende komponenter i 43 % (3 av 7) — den siste er en
UiO-signatur, brukt nettopp der toppkarakteren skilles.
Men merk hva denne drillen faktisk trener. Den lærer deg ikke
bredde-først-søk på nytt; det gjorde kap. 5.2. Den trener
mønstergjenkjenning: å se det klassiske problemet gjennom innpakningen, kalle
den pensumalgoritmen som løser det, og velge den løsningen som har lavest
kjøretid. Sensorveiledningene sier det rett ut — «lavere kjøretid er mer
poenggivende» på samme oppgave. Det kalles poengtrappen, og den betyr at
algoritmevalget ditt er et poengvalg.
Prioritet: høyeste. Dette er stoffet som skiller en C fra en A, og C er en god
og vanlig karakter.
Slik er kapitlet lagt opp (90 min): løsningsoppskriften og et
sensor-kommentert eksamenscase først (ca. 20 min), deretter tolv oppgaver i tre
bolker. Bolkene er uavhengige — ta gjerne én per kveld. Etter hver bolk står en
boks som sier hva oppgavene testet.
Forkunnskaper — sist du var her
Drillen bygger på hele Del 5: kap. 5.1 om grafer og
representasjon, kap. 5.2 om bredde-først- og
dybde-først-søk, kap. 5.3 om komponenter, tofarging og
sykeldeteksjon, og kap. 5.4 om sterkt sammenhengende
komponenter og komponentgrafen.
Du trenger ikke å slå opp. Her er de tre resultatene drillen hviler på, ferdig
oppfrisket:
1. De to dybde-først-kjøretidene er ikke den samme. Et dybde-først-søk som
starter i én node koster , der er antall kanter. Et fullt
dybde-først-søk, altså løkka som starter et nytt søk i hver node som ikke er
besøkt og dermed dekker alle komponenter, koster — leddet
kommer fra at hver node må initialiseres og sjekkes i den ytre løkka. Å bytte om
disse to er et eksplisitt trekkpunkt.
2. Bredde-først-søk gir korteste vei målt i antall kanter. I en uvektet graf
er noden du tar ut av køen i runde nøyaktig kanter fra startnoden. Dybde-
først-søk gir ingen slik garanti. Alt som handler om «lagvis», «innen ledd»
eller «færrest mulig mellomledd», er derfor et bredde-først-problem.
3. Komponentgrafen er en DAG, og inngrad 0 er startmengden. Kontraherer du
hver sterkt sammenhengende komponent til én node, får du en rettet graf uten
sykler — en DAG. Komponentene med inngrad 0 er de ingen kan nå utenfra, så de
må være med i enhver startmengde; velger du én node fra hver av dem, når du alt
som i det hele tatt kan nås. Antall slike komponenter ER svaret på «hvor få kan
jeg starte fra?».
Fra Del 1 trenger du løkketellingen fra kap. 1.2, som er
det du bruker når du skal begrunne en kjøretid.
Fem steg. De er ikke et forslag: de er nøyaktig de fire leddene sensor krever i
et Del 2-svar, pluss oppslaget i mønsterkatalogen som gjør at du finner riktig
algoritme på et halvt minutt.
1. Navngi problemet eksplisitt. Skriv setningen «dette er hvem som kan nås
fra hvem i en rettet graf» eller «dette er korteste vei målt i antall kanter, i
en uvektet graf». Denne ene setningen er ofte verdt et poeng i seg selv, og den
låser resten av besvarelsen. Hopper du over den, begynner du å improvisere.
2. Slå opp i mønsterkatalogen. Innpakningene varierer fra sett til sett;
mønstrene gjør det ikke.
| Signalordene i oppgaven | Mønsteret |
|---|---|
| rundturer, hvem når hverandre begge veier, minste startmengde, «hvor få sider må jeg starte fra» | sterkt sammenhengende komponenter, deretter komponentgrafen |
| lagvis, bølge for bølge, innen ledd, færrest mulig mellomledd | bredde-først-søk — avgrenset så det stopper ved dybde når det er en grense |
| to motparter som ikke må møtes, fordel på to lag, tofarging | tofarging |
| komponentvis behandling, hva henger sammen med hva, hva kan frigjøres | dybde-først-søk fra en rot-mengde, eller et fullt dybde-først-søk som teller komponenter |
| avhengigheter i en rekkefølge, oppdag sykel i en rettet graf | topologisk sortering — den kommer i kap. 6.1, og skal ikke brukes her |
Den siste linjen er en framoverpeker og ingenting mer: ingen oppgave i dette
kapitlet skal løses med topologisk sortering. Alt her løses med
bredde-først-søk, dybde-først-søk, komponenter, tofarging og sterkt
sammenhengende komponenter.
3. Oppgi antagelser om representasjon. Sensor binder seg ikke til
nabolister, nabomatrise eller objektstil, men du må si hva du antar. Standardformen
er: «grafen er gitt som nabolister; hver node v har et felt v.besokt som
starter usant». Ett kort avsnitt, og du har sikret et delmoment.
4. Skriv presis pseudokode i et gjerde. Procedure-hode, Input- og
Output-linje, kort kropp. Presis slår lang: en setning man ikke forstår etter to
gjennomlesninger, blir ignorert av sensor.
5. Oppgi kjøretiden — og la den matche koden du faktisk skrev. Definér
størrelsene: « er antall noder og er antall kanter». Legg til én
setning om hvorfor dette er lavest mulig: «du må lese hver kant minst én gang,
for en kant du aldri så, kunne ha endret svaret».
De to kontrollene som tar ti sekunder til sammen:
- Kjørte du en traversering inne i en løkke over alle noder? Da har du
sannsynligvis der én kjøring holdt.
- Står tallet , eller grensen oppgaven ga, faktisk i koden din? En
avgrenset søkeoppgave besvart med en full traversering svarer på et annet
spørsmål enn det som ble stilt.
(Eksamensnivå, sjanger H — grafalgoritme i pseudokode.) En oppgave verdt 10
poeng, i to deler.
a) (6 p) En kommune har sju saksdokumenter, A til G. Hvert dokument viser
videre til andre dokumenter, og henvisningene går bare én vei. Henvisningene er:
A -> B
B -> C
C -> D
D -> A, E
E -> F
F -> E
G -> A, FDu skal lage en oversiktsside som lenker til noen få dokumenter, slik at en leser
kan følge henvisninger derfra og komme fram til alle sju. Skriv en algoritme som
finner det minste antallet dokumenter oversiktssiden må lenke til. Oppgi
kjøretid.
b) (4 p) Tolv frivillige i en beredskapsgruppe har avtalt hvem de ringer
videre til. Avtalene går begge veier:
A: B, C, D
B: A, E, F
C: A, G
D: A, H
E: B, I
F: B, J
G: C, K
H: D, L
I: E
J: F
K: G
L: HSkriv en algoritme som teller hvor mange utenom A som blir varslet når A
starter en varsling som bare får gå to ledd videre. Kjør den på tallene over.
Oppgi kjøretid.
Problemet navngitt: dette er hvem som kan nås fra hvem i en rettet graf, og
spørsmålet «hvor få må jeg starte fra» er nøyaktig spørsmålet om hvor mange
komponenter i komponentgrafen som ingen kan nå utenfra.
Antagelser om representasjon: grafen er gitt som nabolister over de sju
dokumentene, med én rettet kant per henvisning. Vi antar at algoritmenStronglyConnectedComponents fra kap. 5.4 er tilgjengelig,
og at vi kan bygge komponentgrafen ved å gå gjennom kantene én gang.
Procedure MinsteStartmengde(G)
Input: rettet graf G gitt som nabolister
Output: minste antall startnoder som til sammen naar alle noder
K = StronglyConnectedComponents(G)
H = Komponentgraf(G, K)
antall = 0
for hver komponent k i H:
if inngrad(k) er 0:
antall = antall + 1
return antallKjøringen på dokumentene. Komponentalgoritmen gjør to dybde-først-gjennomløp.
Første gjennomløp noterer i hvilken rekkefølge kallene blir ferdige:
| Hendelse | Node | Ferdignummer |
|---|---|---|
| ny DFS-start (ytre løkke) | A | - |
| besøker | A | - |
| besøker | B | - |
| besøker | C | - |
| besøker | D | - |
| besøker | E | - |
| besøker | F | - |
| ferdig (kallet returnerer) | F | 1 |
| ferdig (kallet returnerer) | E | 2 |
| ferdig (kallet returnerer) | D | 3 |
| ferdig (kallet returnerer) | C | 4 |
| ferdig (kallet returnerer) | B | 5 |
| ferdig (kallet returnerer) | A | 6 |
| ny DFS-start (ytre løkke) | G | - |
| besøker | G | - |
| ferdig (kallet returnerer) | G | 7 |
Ferdigrekkefølgen blir
F, E, D, C, B, A, G, og den synkende rekkefølgen somandre gjennomløp bruker, er
G, A, B, C, D, E, F.Den snudde grafen, der hver kant er vendt:
A: D, G
B: A
C: B
D: C
E: D, F
F: E, G
G: (ingen)Andre gjennomløp, dybde-først i den snudde grafen, i synkende ferdigrekkefølge:| Nr. | Startnode | Noder samlet inn | = komponent |
|---|---|---|---|
| 1 | G | G | K1 |
| 2 | A | A, B, C, D | K2 |
| 3 | E | E, F | K3 |
Tre komponenter:
K1 = {G}, K2 = {A, B, C, D}, K3 = {E, F}. Komponentgrafen:K1 = {G}: K2 -> K3
K2 = {A, B, C, D}: K3
K3 = {E, F}: (ingen utkanter)| Komponent | Noder | Inngrad | Utgrad |
|---|---|---|---|
| K1 | G | 0 | 2 |
| K2 | A, B, C, D | 1 | 1 |
| K3 | E, F | 2 | 0 |
Svar: én komponent har inngrad 0, nemlig
K1 = {G}. Oversiktssiden trenger ålenke til ett dokument,
G. Kontrollen bekrefter det: fra G nås alle sjudokumentene, mens fra
A nås seks og fra E bare to.Kjøretid: , der er antall dokumenter og er antall
henvisninger. Komponentalgoritmen gjør to dybde-først-gjennomløp, hvert
; å bygge komponentgrafen og telle inngrader er ett gjennomløp til
over de samme kantene. Lavere er ikke mulig: du må se hver henvisning minst én
gang, siden en henvisning du aldri leste, kunne ha gjort et dokument nåbart.
Sensornotat, a) — 6 poeng. Typisk fordeling: 2 p for å navngi problemet og
velge komponentalgoritmen, 2 p for å oversette «minste startmengde» til«komponenter med inngrad 0», 1 p for antagelser og pseudokode, 1 p for kjøretid
med størrelsene definert.
Her skilles toppsjiktet fra midtsjiktet. En midtsjiktsbesvarelse svarer
ofte: «kjør et dybde-først-søk fra hvert dokument, og se hvilket som når flest».
Den er riktig — den finnerG— og den får uttelling for idé og for
gjennomføring. Men den koster , altså én traversering
per node, og den svarer i tillegg på feil spørsmål når to dokumenter må til:
«det som når flest» er ikke det samme som «den minste mengden som til sammennår alle». Poengtrappen slår inn her, og den koster typisk halve oppgaven.
Fellen har et nummer i bokas feilregister: #1 — full eller naiv traversering
der én kjøring av komponentalgoritmen holdt.
En annen midtsjiktsvariant svarer «K3 = {E, F}har utgrad 0, så start der».
Det er å forveksle enden med begynnelsen: en komponent med utgrad 0 når ingen
andre komponenter, såEnår bareEogF.
b) Varslingen som stopper etter to ledd.
Problemet navngitt: dette er lagvis spredning med en dybdegrense, altså et
bredde-først-søk som ikke får legge noder dypere enn k i køen.
Antagelser om representasjon: grafen er gitt som nabolister over de tolv
frivillige, urettet, med k = 2. Hver node v har et felt v.avstand som starter
som uendelig.
Procedure AntallInnenK(G, s, k)
Input: graf G gitt som nabolister, startnode s, heltall k som er minst 0
Output: antall noder ulik s med avstand hoeyst k kanter fra s
for hver node v i G:
v.avstand = uendelig
s.avstand = 0
Q = tom koe
Enqueue(Q, s)
antall = 0
while Q er ikke tom:
v = Dequeue(Q)
if v.avstand < k:
for hver nabo w av v:
if w.avstand er uendelig:
w.avstand = v.avstand + 1
antall = antall + 1
Enqueue(Q, w)
return antallKjøringen med s = A og k = 2:| Steg | Tas ut av køen | Dybde | Legges i køen | Køen etter steget | Besøkt |
|---|---|---|---|---|---|
| start | - | - | - | A | A |
| 1 | A | 0 | B, C, D | B, C, D | A, B, C, D |
| 2 | B | 1 | E, F | C, D, E, F | A, B, C, D, E, F |
| 3 | C | 1 | G | D, E, F, G | A, B, C, D, E, F, G |
| 4 | D | 1 | H | E, F, G, H | A, B, C, D, E, F, G, H |
| 5 | E | 2 | ingen | F, G, H | A, B, C, D, E, F, G, H |
| 6 | F | 2 | ingen | G, H | A, B, C, D, E, F, G, H |
| 7 | G | 2 | ingen | H | A, B, C, D, E, F, G, H |
| 8 | H | 2 | ingen | (tom) | A, B, C, D, E, F, G, H |
- lag 0 (avstand 0 kanter fra A): A
- lag 1 (avstand 1 kanter fra A): B, C, D
- lag 2 (avstand 2 kanter fra A): E, F, G, H
- ikke nådd: I, J, K, L
Svar: 7 blir varslet utenom A, nemlig B, C, D, E, F, G, H. Fire frivillige
— I, J, K, L — ligger tre ledd unna og blir ikke varslet.
Kjøretid: i verste fall, der er antall frivillige og
er antall avtaler. Poenget er ikke det asymptotiske taket, men at søket
bare rører nodene innen k ledd: her stoppes køen etter åtte uttak i stedet
for tolv, og de fire ytterste blir aldri sett på.
Sensornotat, b) — 4 poeng. Typisk fordeling: 1 p for å se at dette erbredde-først og ikke dybde-først, 2 p for koden med grensen faktisk til stede,
1 p for riktig svar og kjøretid.Her er skillet mellom sjiktene mindre synlig, men like dyrt. En besvarelse
som kjører et fullt bredde-først-søk og deretter teller nodene med avstand
høyst 2, gir riktig tall og får det meste av uttellingen. Men den har lest helegrafen for å svare på et spørsmål om en liten del av den, og på en oppgave der
teksten sier «bare to ledd», er avgrensningen selve poenget.
Den dyre varianten er å glemme grensen helt og svare 11. Da erif v.avstand < kborte, og algoritmen svarer på et annet spørsmål enn det som ble stilt.
Margnotat om delvis uttelling. Oppgaven er verdt 10 poeng, og de fire leddene
i svarformen — problemet navngitt, antagelser, algoritme, kjøretid — er
uavhengige delmomenter. Du kan bomme på selve kjøringen i a) og likevel ha
fire av seks poeng, hvis du navnga problemet, valgte komponentalgoritmen, skrev
koden og oppga kjøretiden. La aldri en deloppgave stå tom.
Bolk 1 — lagvis spredning og avgrensede søk (ca. 20 min)
Fire oppgaver på det mønsteret som dukker opp oftest, fordi det har flest
innpakninger: noe som sprer seg utover fra et startpunkt. Bruk oppskriften på
alle fire, også når du ser svaret med det samme — det er formen som gir poengene.
Åtte naboer i et gjenbruksnettverk låner
verktøy av hverandre. Låneforholdene går begge veier:
A: B, C
B: A, D
C: A, D, E
D: B, C, F
E: C, F
F: D, E, G
G: F, H
H: GA vil vite hvor mange hun kan nå med en beskjed som får gå høyst to ledd.
a) Navngi problemet, og si hvilket mønster det hører til.
b) Skriv algoritmen. Oppgi antagelser om representasjon.
c) Kjør den for hånd og oppgi svaret.
d) Oppgi kjøretiden, med størrelsene definert.
Ni sykkelstativ i en bydel er koblet med
sykkelveier. Koblingene går begge veier:
A: B, D
B: A, C, E
C: B, F
D: A, E, G
E: B, D, F, H
F: C, E, I
G: D, H
H: E, G, I
I: F, HEn reparatør står ved A og rekker bare å kjøre to strekninger.
a) Hvor mange stativ utenom A rekker hun?
b) Vis kjøringen som en tabell, med køen for hvert steg.
c) En medstudent svarer: «kjør et fullt bredde-først-søk og tell etterpå
hvilke som har avstand høyst 2». Gir det riktig svar, og hva koster det?
d) Oppgi kjøretiden for din egen løsning.
Fem vaktrom i et sykehus ligger i en rundgang, slik
at hvert rom har nøyaktig to naborom:
A: B, E
B: A, C
C: B, D
D: C, E
E: A, DEn beskjed skal sendes ut lagvis fra A: først til alle som er ett rom unna,
så til alle som er to rom unna, og så videre.
a) Skriv algoritmen som deler rommene inn i lag etter avstand fra A.
b) Kjør den og oppgi lagene.
c) Hvorfor kan ikke dybde-først-søk brukes til dette?
d) Oppgi kjøretid.
Samme sykkelnett som i oppgave 2:
A: B, D
B: A, C, E
C: B, F
D: A, E, G
E: B, D, F, H
F: C, E, I
G: D, H
H: E, G, I
I: F, HDenne gangen skal du ikke bruke rekursjon.
a) Skriv dybde-først-søk fra én node med en eksplisitt stakk.
b) Kjør den fra A, og vis stakken etter hvert steg.
c) Oppgi besøksrekkefølgen.
d) Oppgi kjøretiden — og forklar hvorfor den er en annen enn for et fullt
dybde-først-søk over hele grafen.
De fire oppgavene rullerte spredningsmønsteret:
- Oppgave 1 testet at du setter grensen k inn i selve søket i stedet for å
traversere hele grafen og telle etterpå.
- Oppgave 2 testet det samme i en tettere graf, der en node på siste lag har
ubesøkte naboer som ikke skal i køen — og den lot deg vurdere en løsning som
er riktig, men leser mer enn den trenger.
- Oppgave 3 testet at «lagvis» er bredde-først og ikke dybde-først, og at du
kan si hvorfor.
- Oppgave 4 testet den iterative dybde-først-varianten, og skillet mellom
fra én node og for et fullt søk.
Er du i tvil om hvilket søk en oppgave ber om, still ett spørsmål: spør
oppgaven om avstand? Handler den om lag, ledd, mellomledd eller «færrest mulig
hopp», er svaret bredde-først. Handler den om hva som henger sammen med hva, er
dybde-først like bra og ofte kortere å skrive.
Bolk 2 — komponenter, dybde-først og tofarging (ca. 23 min)
— naturlig pausepunkt —
Fire oppgaver på de to mønstrene som handler om struktur i stedet for
avstand: hva henger sammen med hva, og kan nodene deles i to grupper uten at to
naboer havner sammen.
Elleve maskiner i et verksted er koblet sammen med
datakabler. Kablene går begge veier:
A: B
B: A
C: D
D: C, E
E: D
F: G, H
G: F, H
H: F, G
I: J
J: I
K: (ingen)Driftsavdelingen vil vite hvor mange atskilte nettverk maskinene utgjør, slik
at de kan sette opp én ruter per nettverk.
a) Navngi problemet og skriv algoritmen.
b) Kjør den for hånd, og vis den ytre løkka node for node.
c) Oppgi svaret og kjøretiden.
d) Hva ville svaret vært hvis du glemte den ytre løkka og bare kjørte ett
dybde-først-søk fra A?
Et delt dokumentarkiv har seks mapper. En kobling
betyr at den ene mappen har en snarvei til den andre, og snarveiene virker begge
veier:
A: B, C
B: A, D, E
C: A, F
D: B
E: B
F: CRydderutinen skal beholde alt som kan nås fra A og slette resten.
a) Skriv det rekursive dybde-først-søket rydderutinen bruker.
b) Kjør det fra A. Vis kallstakken etter hvert steg.
c) Hvilke mapper slettes, og hva er den største dybden kallstakken når?
d) Hva ville svaret blitt hvis arkivet i stedet hadde sett ut som verkstedet i
oppgave 5, og rot-mengden fortsatt var {A}?
Ni deltakere på et kurs skal deles i to grupper.
Noen par har jobbet sammen før og skal derfor havne i hver sin gruppe. Parene:
A: B, D
B: A, C, E
C: B, F
D: A, E, G
E: B, D, F, H
F: C, E, I
G: D, H
H: E, G, I
I: F, Ha) Navngi problemet og skriv algoritmen.
b) Kjør den fra A, og vis køen for hvert steg.
c) Oppgi de to gruppene, eller si at det er umulig.
d) Oppgi kjøretid, og forklar i én setning hvorfor algoritmen aldri trenger å
prøve seg fram.
Sju vakter skal settes opp i to skift. Sju
par tåler ikke å gå på samme skift:
A: B, C
B: A, D
C: A, D
D: B, C, E
E: D, F, G
F: E, G
G: E, Fa) Er det mulig? Kjør algoritmen og vis hvert steg til svaret er avgjort.
b) Hvis det er umulig: hvilken kant avslører det, og hvorfor er den
uunngåelig?
c) Hva er den minste endringen i konfliktlista som ville gjort oppsettet
mulig?
d) Hva er kjøretiden, og hvor mye av grafen rakk algoritmen å se på før den
svarte?
- Oppgave 5 testet komponenttelling, og at den ytre løkka er det som gjør
algoritmen riktig — og som gjør kjøretiden og ikke .
- Oppgave 6 testet mønsteret «hva kan nås fra en rot-mengde, og hva kan
frigjøres», samt at rekursjonsdybden er en reell kostnad.
- Oppgave 7 testet tofarging på en graf der det går opp, og innsikten om at
fargene er tvunget så snart startnoden er valgt.
- Oppgave 8 testet tofarging på en graf der det ikke går, og at du kan peke på
kanten og forklare hvorfor den er uunngåelig.
Fellesnevneren i alle fire: ingen av dem trenger noe mer avansert enn et
dybde- eller bredde-først-søk med et ekstra felt per node. Når du kjenner igjen
mønsteret, er kodingen fem minutter. Det er gjenkjenningen som er ferdigheten.
Bolk 3 — rundturer, startmengder og valget som avgjør poengsummen (ca. 27 min)
— naturlig pausepunkt —
De fire tyngste. Alle handler om rettede grafer og om spørsmålet «hvem kan nå
hvem», og alle har en naiv løsning som er riktig, men som koster halve
poengsummen. Det er her toppkarakteren skilles.
Sju sportsklubber har avtaler om utlån av
spillere. En avtale X -> Y betyr at X kan låne ut til Y, men ikke omvendt:
A: B
B: C
C: D, F
D: E
E: A
F: G
G: Fa) Hvilke grupper av klubber kan låne spillere til hverandre begge veier,
eventuelt via mellomledd?
b) Hvor få klubber må en spiller starte i for å kunne nå alle sju gjennom en
kjede av utlån?
c) Oppgi kjøretid, og forklar hvorfor den er lavest mulig.
Merk: i denne oppgaven skal du svare uten pseudokode, i klar prosa. Det er
en tillatt svarform, og fasiten viser hvordan den ser ut når den gir full
uttelling.
Ni artikler på et nettsted lenker til
hverandre. Lenkene går én vei:
A: B
B: C
C: B, D
D: A, E
E: F
F: G
G: E
H: D, I
I: Ha) Finn gruppene der alle artiklene når hverandre begge veier.
b) Hvor få artikler må forsiden lenke til for at en leser skal kunne nå alle
ni?
c) Fra hvilke artikler når man alle de andre, og fra hvilke når man færrest?
d) Skriv algoritmen som besvarer b), og oppgi kjøretid.
Ni tjenester i et driftssystem varsler
hverandre. En kant X -> Y betyr at X sender varsel videre til Y:
A: B
B: C
C: A, G
D: A, E
E: F
F: D, I
G: H
H: C
I: (ingen)a) Finn gruppene der varsler kan gå rundt og tilbake.
b) Hvor få tjenester må et varsel settes i gang fra for å nå alle ni?
c) Hvilke grupper sender ikke varsel videre til noen andre gruppe?
d) En besvarelse svarer på b) med «to — det er to grupper som ikke sender
videre». Hva er galt, og hva koster feilen?
En oppgave lyder: «Gitt en rettet graf av
nettsider med lenker. Skriv en algoritme som avgjør om det finnes en side som kan
nå alle de andre. Oppgi kjøretid.» En besvarelse har levert dette:
Procedure Finn(G)
for hver node v i G:
lag et nytt sett S
gjoer et soek fra v og legg alt du finner i S
hvis alt som ikke er v ligger i S og ogsaa v selv ligger der
eller v ikke ligger der men alle andre gjoer det
saa er v et svar og vi kan returnere v med en gang
ellers gaar vi videre til neste v
det finnes ikke noe svar
Kjoeretid: O(|V| + |E|)a) Er algoritmen riktig?
b) Er kjøretiden riktig oppgitt? Regn den ut.
c) Hvor står besvarelsen i poengtrappen, og hvorfor?
d) Skriv en løsning som står øverst i trappen. Navngi problemet, oppgi
antagelser og kjøretid.
- Oppgave 9 testet den rene reachability-oppgaven — «hvem når hvem» — og at en
fasit uten pseudokode kan gi full uttelling så lenge den er entydig.
- Oppgave 10 testet at én kjøring av komponentalgoritmen svarer på fire
spørsmål samtidig: grupper, startmengde, hvem som når alt, og hvem som når
minst.
- Oppgave 11 testet skillet mellom inngrad 0 og utgrad 0 på en graf der
forvekslingen ikke kan skjules — to komponenter uten utgående kanter, men bare
én uten innkommende.
- Oppgave 12 testet poengtrappen direkte, og at en kjøretid som ikke matcher
koden, er et selvstendig tap.
Det ene grepet som er verdt mest i hele Del 2: når du ser ordene rundtur,
hverandre, begge veier, minste startmengde eller nå alle i en rettet graf
— ikke begynn å skrive et søk. Skriv «dette løses med sterkt sammenhengende
komponenter og komponentgrafen», og bygg besvarelsen derfra.
Samlet, i den rekkefølgen de koster mest poeng.
- Full eller naiv traversering der én kjøring holdt (felle #1). Et søk fra
hver node koster der komponentalgoritmen gjør jobben i
. Kontrollen: står det en traversering inne i en løkke over
alle noder? Da har du sannsynligvis tatt det dyre trinnet i poengtrappen.
- Uklar eller unødig lang pseudokode (felle #12). En setning man ikke
forstår etter to gjennomlesninger, ignoreres. Kort, presis kode slår lang og
omtrentlig — og en klar forklaring i vanlig prosa slår begge, hvis den er
entydig.
- Grensen glemt i et avgrenset søk. Spør oppgaven om «innen ledd», må
tallet stå i koden. En full traversering svarer på et annet spørsmål og gir et
annet tall.
- De to dybde-først-kjøretidene forvekslet (felle #6). Fra én node er det
; med den ytre løkka over alle noder er det . Har koden
din en løkke over alle noder — for å initialisere, for å dekke komponenter eller
for å telle — er kjøretiden .
- Inngrad 0 forvekslet med utgrad 0. Inngrad 0 er der du må starte; utgrad
0 er der du ender. Kontrollen: test forslaget ditt — hvor mange noder når
du faktisk hvis du starter der?
- Dybde-først brukt til et avstandsspørsmål. Bare bredde-først gir korteste vei
i antall kanter. Handler oppgaven om lag, ledd eller mellomledd, er dybde-først
feil verktøy uansett hvor pent det er skrevet.
- Antagelser om representasjon ikke oppgitt. Sensor binder seg ikke til
nabolister eller nabomatrise, men du må si hva du antar. Det er et eget
delmoment, og det tar to linjer.
- og brukt uten å defineres (felle #10). Skriv « er
antall noder, er antall kanter» i klartekst — det gir eksplisitt trekk å la
det være.
- Kjøretid som ikke matcher koden. Å oppgi kjøretiden for én indre operasjon
som om det var kjøretiden for hele algoritmen, koster et selvstendig delmoment.
Tell løkkene før du skriver tallet.
Og den viktigste vanen: skriv de fire leddene i rekkefølge hver gang —
problemet navngitt, antagelser, algoritme, kjøretid. De er uavhengige delmomenter,
så selv en besvarelse med en feil i selve algoritmen henter inn flertallet av
poengene hvis de tre andre står der.
Begrepsbank
Begrepsbanken er flashcard-/repetisjonsstoff — den gjentar det du nettopp har
lest. Hopp trygt over ved førstegangslesing; tidsanslaget for kapitlet gjelder
kjernestoffet.
«Rundturer / hvem når hverandre / minste startmengde» gir sterkt sammenhengende
komponenter og komponentgrafen. «Lagvis / innen ledd» gir bredde-først-søk,
avgrenset ved dybde . «To motparter som ikke må møtes» gir tofarging.
«Komponentvis behandling / hva kan frigjøres» gir dybde-først-søk fra en
rot-mengde.
Innpakningene varierer fra sett til sett; mønstrene gjør det ikke. Å finne
mønsteret er halve oppgaven.
algoritmen — pseudokode eller klar prosa. 4) Oppgi kjøretiden som matcher
koden, med og definert, og én setning om hvorfor lavere ikke er mulig.
Alle fire er uavhengige delmomenter. En besvarelse med feil i selve
algoritmen, men med de tre andre leddene på plass, henter inn flertallet av
poengene.
Én kjøring, , svarer på hele familien av «hvem når hvem»-spørsmål i
en rettet graf: hvilke noder ligger på en rundtur sammen, hvor få må jeg starte
fra, og hvem når alt.
Alternativet — et søk fra hver node — er også riktig, men koster
og er felle #1 i bokas feilregister.
Antall komponenter med inngrad 0 i komponentgrafen er nøyaktig den minste
mengden noder du må starte fra for å nå alt som kan nås. Ingen kant peker inn i en
slik komponent, så den må ha sin egen startnode.
Og det er nok: komponentgrafen er en DAG, så enhver annen komponent kan følges
bakover til en komponent med inngrad 0.
En komponent med utgrad 0 har ingen kant ut til noen annen komponent. Starter
du der, når du bare komponenten selv.
Utgrad 0 er altså ikke et startpunkt. Å bytte om de to er en av de vanligste
feilene i denne sjangeren, og den skjules når grafen tilfeldigvis har én av hver.
Spør en oppgave om det finnes en rundtur, eller om noen kan komme tilbake dit de
startet, ser du på komponentene: en sterkt sammenhengende komponent med mer enn
én node betyr at det finnes en rundtur gjennom dem.
En komponent med bare én node har ingen rundtur, med mindre noden peker på seg
selv.
Skal du telle hvor mange som nås innen ledd, legger du aldri en node dypere
enn i køen: if v.avstand < k før du ser på naboene.
Kjøretiden er i verste fall, men poenget er at søket bare rører
nodene innen ledd. Glemmer du grensen, svarer algoritmen på et annet
spørsmål enn det som ble stilt.
I en uvektet graf er noden som tas ut av køen i runde , nøyaktig kanter fra
startnoden. Dybde-først gir ingen slik garanti — den kan gi en nabo avstanden 4.
Alt som handler om lag, ledd, mellomledd eller «færrest mulig hopp», er derfor
bredde-først.
Et dybde-først-søk fra én node er . Et fullt dybde-først-søk, med
den ytre løkka som starter et nytt søk i hver ubesøkt node, er —
leddet kommer fra initialiseringen og fra selve løkka.
Å bytte om dem er felle #6 og et eksplisitt trekkpunkt. Har koden din en løkke
over alle noder, er kjøretiden .
Gi startnoden en farge; da er alle de andre fargene bestemt, for hver nabo må
ha den motsatte. Det finnes ingen valg å angre på, og derfor holder én traversering
i .
Går det ikke opp, skyldes det en sykel med et odde antall kanter: fargene
veksler langs sykelen og stemmer ikke når du kommer tilbake til start.
Tell hvor mange ganger den ytre løkka må starte et nytt søk: hver gang den møter
en ubesøkt node, står den i en komponent den aldri har vært i. Kjøretid
.
Den ytre løkka er ikke pynt. Uten den finner du bare komponenten startnoden ligger
i, og isolerte noder uten kanter blir aldri oppdaget.
På samme oppgave gir full pott og minst.
Sensorveiledningene sier det eksplisitt: lavere kjøretid er mer poenggivende.
Begge løsningene er korrekte — forskjellen er ikke riktig mot galt, men mange
poeng mot få. Å velge algoritme er å velge poeng.
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 Universitetet i Oslo. Dette er ikke offisielt studiemateriell. Les mer.