Begreper & referanser
Alle nøkkelbegrepene, formlene og referansene fra CSP II: backtracking og lokalt søk, samlet på én side. Bruk denne som oppslag når du leser, øver flashcards eller tar quiz.
Begreper
Sentrale begreper fra kapittelet med korte definisjoner.
Dybde-først-søk som gir én variabel en verdi om gangen, forkaster verdier som bryter en begrensning mot de tilordnede variablene, og trekker tilbake forrige tilordning når en variabel går tom for verdier (backtracking search).
Egenskapen at rekkefølgen tilordningene gjøres i, ikke endrer resultatet. Derfor holder det å velge én variabel per nivå, og søketreet får blader i stedet for (commutativity).
Velg som neste variabel den som har færrest lovlige verdier igjen. Har en variabel null lovlige verdier, oppdages blindveien straks. På norsk: minste gjenværende verdier (minimum remaining values, MRV).
Velg variabelen med flest begrensninger mot variabler som ikke er tilordnet ennå. Brukes særlig som tie-break når MRV gir likhet (degree heuristic).
Prøv først verdien som fjerner færrest lovlige verdier hos de ikke-tilordnede naboene, med laveste verdi ved likhet (least constraining value, LCV).
Prinsippet bak MRV og LCV: velg variabelen som mest sannsynlig feiler, fordi alle må tilordnes uansett, men verdien som minst sannsynlig feiler, fordi én løsning er nok (fail-first, fail-last).
Etter hver tilordning fjernes verdiene hos de ikke-tilordnede naboene til som er inkonsistente med . Blir et nabodomene tomt, forkastes straks (forward checking, FC).
Å opprettholde buekonsistens under søket: etter tilordningen kjøres AC-3 med buene for de ikke-tilordnede naboene , og propageringen fortsetter så langt den rekker (maintaining arc consistency, MAC).
Lokalt søk for CSP: start med en komplett tilordning, velg en tilfeldig variabel i konflikt, og gi den verdien med færrest konflikter. Svært raskt når løsningene ligger tett, men ikke fullstendig (min-conflicts).
Et CSP der begrensningsgrafen er et tre. Det kan løses i uten tilbakesteg ved å gjøre buene konsistente fra bladene og tilordne fra roten. Utenfor pensum 2026 (tree-structured CSP).
En rekkefølge av nodene i et tre med valgt rot der hver node kommer etter forelderen sin. Brukes i algoritmen for trestrukturerte CSP (topological sort).
Formler
Hver formel: hva den heter, hvordan den ser ut, og hva symbolene betyr.
Antall blader i søketreet
Uten kommutativitet kan hver ledig variabel få hver verdi på hvert nivå, så forgreningen er på nivå . Med én variabel per nivå er forgreningen . Vanlig feil er å svare .
MRV-valget
Lovlige verdier er verdiene som ikke bryter en begrensning mot de tilordnede variablene, eller domenet etter FC. Ved likhet brukes gradheuristikken og så laveste indeks, med mindre oppgaven sier noe annet.
Min-conflicts-trekket
Variabelen er valgt tilfeldig blant dem som er i konflikt, og får verdien som bryter færrest begrensninger med de andres nåværende verdier. Ved likhet trekkes en av de beste, og den nåværende verdien kan være med.
Kjøretiden for trestrukturerte CSP
Gjelder bare når begrensningsgrafen er et tre: buer gjøres konsistente, hver for , og tilordningen etterpå går uten tilbakesteg. Utenfor pensum 2026.
Kodesnutter
Kodesnutter fra kapittelet, vist literal.
Backtracking på 14 dronninger med MRV og FC
bruk_mrv = 0 # @param 0..1bruk_fc = 0 # @param 0..1 n = 14 # variabel = kolonne, verdi = radforsok = tilbake = 0 def kolliderer(k1, r1, k2, r2): return r1 == r2 or abs(r1 - r2) == abs(k1 - k2) def los(tilordning, domener): global forsok, tilbake if len(tilordning) == n: return tilordning ledige = [k for k in range(n) if k not in tilordning] lovlige = {k: [r for r in domener[k] if not any(kolliderer(k, r, j, s) for j, s in tilordning.items())] for k in ledige} k = min(ledige, key=lambda j: len(lovlige[j])) if bruk_mrv else ledige[0] for r in lovlige[k]: forsok += 1 nye = domener if bruk_fc: # fjern verdier hos de ledige som kolliderer med k = r nye = {j: [s for s in domener[j] if not kolliderer(k, r, j, s)] for j in ledige if j != k} if any(not d for d in nye.values()): continue # et domene ble tomt: forkast verdien svar = los({**tilordning, k: r}, nye) if svar: return svar tilbake += 1 # k gikk tom: tilbake til forrige variabel return None svar = los({}, {k: list(range(1, n + 1)) for k in range(n)})print(f"Løsning: {', '.join(str(svar[k]) for k in range(n))}" if svar else "Løsning: ingen")print(f"Tilordninger forsøkt: {forsok}")print(f"Tilbakesteg: {tilbake}")Prøv: sett bruk_mrv til 1 og se at linja Tilordninger forsøkt: faller fra 1899 til 124, og sett så bare bruk_fc til 1 og se at Tilbakesteg: faller til under halvparten.
Min-conflicts på n dronninger
import random random.seed(4)n = 8 # @param 4..40maks_steg = 100 # @param 10..1000 rad = [random.randrange(n) for _ in range(n)] # tilfeldig start: rad[k] i kolonne k def konflikter(k, r): return sum(1 for j in range(n) if j != k and (rad[j] == r or abs(rad[j] - r) == abs(j - k))) lost_etter = Nonefor steg in range(maks_steg + 1): i_konflikt = [k for k in range(n) if konflikter(k, rad[k]) > 0] if not i_konflikt: lost_etter = steg break if steg == maks_steg: break k = random.choice(i_konflikt) # tilfeldig variabel i konflikt tall = [konflikter(k, r) for r in range(n)] rad[k] = random.choice([r for r in range(n) if tall[r] == min(tall)]) # likhet: tilfeldig par = sum(1 for a in range(n) for b in range(a + 1, n) if rad[a] == rad[b] or abs(rad[a] - rad[b]) == b - a)print(f"Løst etter steg: {lost_etter}" if lost_etter is not None else f"Løst etter steg: ikke løst innen {maks_steg} steg")print(f"Konflikter igjen: {par}")Prøv: øk n til 40 og se at linja Løst etter steg: fortsatt havner under 100, og senk maks_steg til 10 og se at Konflikter igjen: blir større enn 0.
Pseudokode
Pseudokode som forklarer fremgangsmåten steg for steg.
Backtracking-søk i pseudokode
tilbakespor(tilordning, domener): hvis alle variabler har en verdi: returner tilordning x = velg_variabel(tilordning, domener) # fast, MRV, MRV + grad for v i ordne_verdier(x, tilordning, domener): # fast eller LCV hvis v passer med alle tilordnede naboer: legg til x = v nye = utled(x, v, domener) # ingenting, FC eller MAC hvis ingen domener i nye er tomme: svar = tilbakespor(tilordning, nye) hvis svar ikke er feil: returner svar fjern x = v # prøv neste verdi returner feil # tilbake til forrige valgTre valg styrer søket: hvilken variabel som tas, i hvilken rekkefølge verdiene prøves, og hvor mye som utledes etter hver tilordning. Strukturen er etter Russell & Norvig, slik den ligger i aimacode/aima-pseudocode (CC BY 4.0), oversatt og med egne navn.
Læringsmål
Hva du skal kunne etter å ha lest kapittelet.
- 01Forklar hvordan backtracking-søk bygger delvise tilordninger, og hvorfor kommutativitet gir blader i stedet for
- 02Spor backtracking med MRV, gradheuristikken og laveste indeks, og oppgi tilordningsrekkefølgen og antall tilbakesteg
- 03Velg verdi med LCV ved å telle hvor mange verdier hver kandidat fjerner hos de ikke-tilordnede naboene
- 04Skriv alle domener etter hver tilordning med fremoversjekking, og marker forkastede verdier og tilbakesteg
- 05Sammenlign fremoversjekking og MAC, og forklar hva MAC oppdager som FC ikke ser
- 06Beskriv min-conflicts, og vurder når lokalt søk slår systematisk søk