Přeskočit na obsah
coden

Prohledávání stavového prostoru: BFS, DFS, UCS a A* s animacemi

Základy umělé inteligence názorně: co je stavový prostor, jak fungují BFS, DFS, UCS, hladové hledání a A*, co je heuristika a jak s tím souvisí strojové učení.

14 minut Matěj Žalmánek 2026-10-15

Série Základy AI Díl 21 z 83
  1. Mapa umělé inteligence: co je co a jak to do sebe zapadá
  2. Stručná historie umělé inteligence: od Turinga po jazykové modely
  3. Data, model, trénink, inference: základní pojmy na jednom příkladu
  4. Jak číst matematický zápis: symboly, indexy, sumy a kvantifikátory
  5. Množiny a relace: stavebnice, ze které se skládají definice v AI
  6. Funkce a zobrazení: proč je každý model v AI funkce
  7. Logika a důkazy: pravidla, podle kterých se v AI uvažuje
  8. Grafy a stromy: struktura, na které stojí hledání i neuronové sítě
  9. Lineární algebra pro strojové učení: vektory, matice a proč AI hlavně násobí
  10. Derivace, gradient a řetízkové pravidlo: jak model zjistí, kterým směrem se učit
  11. Gradientní sestup: jak se model učí
  12. Co je pravděpodobnost: od hodu kostkou ke Kolmogorovovým axiomům
  13. Podmíněná pravděpodobnost a Bayesova věta: jak změnit odhad, když se dozvíte něco nového
  14. Náhodná veličina a distribuční funkce: jedna funkce, která o rozdělení řekne všechno
  15. Funkce hustoty a pravděpodobnostní funkce: proč má spojitá veličina v bodě pravděpodobnost nula
  16. Střední hodnota, rozptyl a kovariance: kde rozdělení leží, jak je široké a co s čím souvisí
  17. Přehled pravděpodobnostních rozdělení: Bernoulli, binomické, Poisson, rovnoměrné, normální a exponenciální
  18. Zákon velkých čísel a centrální limitní věta: proč se průměry chovají tak předvídatelně
  19. Statistika a odhady parametrů: maximální věrohodnost, MAP a intervaly spolehlivosti
  20. Teorie informace: entropie, křížová entropie a KL divergence — odkud se berou ztrátové funkce
  21. Prohledávání stavového prostoru: BFS, DFS, UCS a A* s animacemi
  22. Hry a protivník: minimax, alfa-beta ořezávání a Monte Carlo tree search
  23. Splňování omezujících podmínek: backtracking, propagace omezení a AC-3 na sudoku a mapě krajů
  24. Logika a znalostní systémy: dopředné a zpětné řetězení, rezoluce a expertní systémy
  25. Plánování: STRIPS, svět kostek, heuristiky z relaxace a plánování v AI agentech
  26. Lokální hledání a simulované žíhání: obchodní cestující přes krajská města
  27. Genetické algoritmy: populace, křížení, mutace a výběr na batohu na výlet
  28. Evoluční strategie a genetické programování: od pravidla jedné pětiny po Keplerův zákon
  29. Optimalizace mravenčí kolonií: jak feromonová stopa najde krátkou cestu
  30. Optimalizace hejnem částic: setrvačnost, vlastní zkušenost a zkušenost hejna
  31. Neuroevoluce: evoluce vah i tvaru neuronových sítí a algoritmus NEAT
  32. Fuzzy množiny a funkce příslušnosti: jak zapsat „trochu teplo“
  33. Fuzzy logika a pravděpodobnost: proč „trochu teplo“ neznamená „pravděpodobně teplo“
  34. Fuzzy logika: t-normy, implikace a pravidla KDYŽ–PAK
  35. Fuzzy řízení: Mamdani a Sugeno od ventilátoru po kyvadlo na vozíku
  36. Fuzzy shlukování: algoritmus fuzzy c-means a body mezi shluky
  37. Neuro-fuzzy systémy (ANFIS): fuzzy pravidla, která se učí z dat
  38. Markovovy řetězce: přechodová matice, stacionární rozdělení a PageRank
  39. Skryté Markovovy modely: Viterbiho algoritmus a nepoctivé kasino
  40. Bayesovské sítě: proč je tráva mokrá a co z toho plyne pro postřikovač
  41. Kalmanův filtr: jak z nepřesných měření odhadnout, kde auto je a jak rychle jede
  42. Druhy strojového učení: s učitelem, bez učitele, s částečným učitelem a další
  43. Přeučení a regularizace: proč model, který zná data nazpaměť, selže
  44. Vyhodnocení modelu: křížová validace, F1 a křivka ROC
  45. Shlukování a snížení dimenze: k-means, PCA a t-SNE
  46. Klasifikace a regrese: dvě úlohy učení s učitelem
  47. Lineární regrese: nejmenší čtverce geometricky i maticově
  48. Polynomiální a nelineární regrese: jak přímku ohnout
  49. Logistická regrese: sigmoida, šance a křížová entropie
  50. Vícetřídní klasifikace a softmax: od dvou tříd k mnoha
  51. Naivní Bayesův klasifikátor: spamový filtr, který funguje lépe, než by měl
  52. Ztrátové funkce: co přesně model minimalizuje
  53. Rozhodovací stromy a k nejbližších sousedů: modely bez vzorců
  54. Ansámbly: náhodný les a gradient boosting
  55. SVM a jádrový trik: nejširší hranice a jak ji ohnout
  56. Signál a vzorkování: proč platí Nyquistův teorém
  57. Fourierova transformace: rozklad signálu na frekvence
  58. Filtry a konvoluce: vyhlazení, hrany a odstranění šumu
  59. Zvuk a řeč: spektrogram, melová škála a MFCC
  60. Obraz jako signál: pixely, barvy, konvoluce a hrany
  61. Perceptron a umělý neuron: nejjednodušší neuronová síť a co neumí
  62. Neuronová síť od nuly: zpětné šíření chyby krok za krokem
  63. Aktivace, inicializace a normalizace: drobnosti, na kterých hluboká síť stojí
  64. Konvoluční sítě: jak síť poznává obrázky
  65. Rekurentní sítě: paměť pro posloupnosti a mizející gradient
  66. LSTM a GRU: brány, které rozhodují, co si síť zapamatuje
  67. Autoenkodéry: síť, která se učí data stlačit a zase rozbalit
  68. Embeddingy a word2vec: jak se slovo stane vektorem
  69. Mechanismus pozornosti: jak se model naučí, kam se dívat
  70. Transformery a velké jazykové modely: architektura krok za krokem
  71. Tokenizace: jak se text rozseká na kousky pro model
  72. Předtrénování a škálování: proč větší model a víc dat funguje
  73. Doladění a RLHF: jak se z doplňovače textu stane asistent
  74. Prompty, kontext a RAG: jak modelu dát správné informace
  75. Variační autoenkodéry (VAE): nové obrázky ze spojitého latentního prostoru
  76. Generativní adversariální sítě (GAN): padělatel proti detektivovi
  77. Difúzní modely: jak se z čistého šumu stane obrázek
  78. Markovovy rozhodovací procesy: stavy, akce, odměny a Bellmanovy rovnice
  79. Q-learning a DQN: učení hodnoty akcí od tabulky po neuronovou síť
  80. Policy gradient a PPO: učení přímo strategie
  81. Jak funguje AI agent: jazykový model ve smyčce s nástroji
  82. Programování s AI agenty: zadání, testy a kontrola
  83. Vyhodnocení a bezpečnost AI systémů: jak měřit a jak hlídat

Celá osnova série 83 z 83 témat hotovo

Co si odnesete
  • Jak formálně zapsat úlohu jako hledání cesty v grafu stavů.
  • Čím se liší BFS, DFS, UCS, hladové hledání a A* — a proč je to v jádru jediný řádek kódu.
  • Co je heuristika, kdy je přípustná a proč s ní A* najde nejlevnější cestu s nejmenší prací.

Co se hodí znát předem: Mapa umělé inteligence: co je co a jak to do sebe zapadá, Jak číst matematický zápis: symboly, indexy, sumy a kvantifikátory

Když se dnes řekne umělá inteligence, většina lidí si vybaví neuronové sítě a velké jazykové modely. Jenže dlouho předtím, než se stroje začaly učit z dat, uměly hledat. Navigace v mapě, tah v šachu, plán robota ve skladu — to všechno je hledání cesty v prostoru možností. Prohledávání stavového prostoru je první kapitola každé učebnice umělé inteligence a myšlenky z něj potkáte i ve strojovém učení: heuristiky, optimalizaci, hledání nejlepšího řešení.

Každý pojem tu najdete dvakrát: nejdřív přesnou matematickou definici, hned pod ní totéž lidsky. Všechny animace jsou skutečný běh algoritmu — nic z toho není kreslené ručně.

1. Co je stavový prostor

Než začneme cokoliv hledat, musíme úlohu popsat tak, aby jí rozuměl počítač. K tomu slouží úloha prohledávání:

Definice — úloha prohledávání

Úloha prohledávání je šestice

P = (S, s0, A, T, G, c),

která splňuje:

  1. S je neprázdná množina; její prvky nazýváme stavy;
  2. s0 ∈ S; tento stav nazýváme počáteční stav;
  3. A přiřazuje každému stavu s ∈ S konečnou množinu A(s) akcí použitelných ve stavu s; označme 𝒟 = {(s, a) : s ∈ S, a ∈ A(s)} množinu všech dvojic „stav, v něm použitelná akce“;
  4. T : 𝒟 → S je přechodová funkce; pro (s, a) ∈ 𝒟 je stav s′ = T(s, a) následník stavu s (po akci a);
  5. G ⊆ S je množina cílových stavů;
  6. c : 𝒟 → ℝ+ = (0, ∞) je cena akce: c(s, a) > 0 je cena použití akce a ve stavu s.

Úloha tedy přesně říká, odkud začínáme, jaké tahy máme v každém stavu k dispozici, kam který tah vede, kolik stojí a kdy jsme v cíli.

Laicky řečeno

Stav je „snímek světa“ v jednom okamžiku — kde stojí panáček v bludišti, jak leží kostičky hlavolamu. Akce je tah, který můžete udělat. Přechodová funkce říká, kam vás ten tah dostane. Cíl je to, kam se chcete dostat, a cena říká, kolik vás který tah stojí — čas, palivo, kroky.

Definice — cesta, řešení, optimum

Nechť P = (S, s0, A, T, G, c) je úloha prohledávání a k ∈ ℕ0. Cesta délky k z počátečního stavu je posloupnost

π = (s0, a1, s1, a2, …, ak, sk)

taková, že pro každé i = 1, …, k platí ai ∈ A(si−1) a si = T(si−1, ai). (Pro k = 0 je cesta jen (s0).)

Cena cesty π je součet cen jejích akcí:

g(π) = ∑i=1k c(si−1, ai).

Cesta π je řešení, právě když sk ∈ G. Označme Π množinu všech řešení. Je-li Π ≠ ∅, je optimální cena

C* = min {g(π) : π ∈ Π}

(pokud minimum existuje; v úlohách s konečně mnoha stavy, jako je naše bludiště, existuje vždy) a řešení π je optimální, právě když g(π) = C*. Optimální řešení je tedy řešení, které není dražší než žádné jiné řešení.

Laicky řečeno

Cesta je seznam tahů, které jdou po sobě. Řešení je cesta, která dojde do cíle. Optimální řešení je ta nejlevnější ze všech — a právě tu většinou chceme najít.

Stavy spojené akcemi tvoří orientovaný graf: stavy jsou vrcholy, akce hrany s vahou c. Hledat řešení tedy znamená najít v grafu cestu z s0 do některého vrcholu z G. Klasický příklad je hlavolam 8: kostičky 1–8 v rámečku 3 × 3 a jedno prázdné místo. Stav je rozložení kostiček, akce posun prázdného místa a cíl seřazené kostičky:

12345678 nahoru 13425678 vpravo 12345678 dolů 12347568 vlevo 12345678
Jeden stav hlavolamu 8 a jeho čtyři následníci

Vypadá to nevinně, ale hlavolam 8 má 181 440 dosažitelných stavů a jeho větší bratr hlavolam 15 přes deset bilionů. Graf si proto nikdy nepostavíme celý dopředu. Algoritmus ho objevuje postupně — z přechodové funkce si vždy spočítá jen následníky stavu, u kterého právě stojí.

Příklad — odkud se bere 181 440

Osm kostiček a prázdné místo se dá do devíti políček rozmístit 9! = 9 · 8 · 7 · 6 · 5 · 4 · 3 · 2 · 1 = 362 880 způsoby. Tahem se ale dostanete jen do poloviny z nich: zapíšete-li kostičky po řádcích (bez prázdného místa), každý tah zachová, zda je v tom pořadí sudý, nebo lichý počet dvojic „větší před menší“. Posun vodorovně pořadí nezmění vůbec, posun svisle přeskočí kostičkou dvě jiné a počet takových dvojic změní o 0 nebo o ±2. Do stavu s opačnou paritou se proto dostat nedá. Že je dosažitelná právě polovina stavů (u hlavolamu 15 s trochu složitějším invariantem, do kterého vstupuje i řádek prázdného místa), dokázali W. W. Johnson a W. E. Story v roce 1879.

Hlavolam 8: 9!/2 = 362 880/2 = 181 440 stavů.
Hlavolam 15: 16!/2 = 20 922 789 888 000/2 = 10 461 394 944 000 ≈ 1,05 · 1013 stavů — přes deset bilionů.

2. Zadání: cesta bludištěm

Všechny algoritmy si ukážeme na stejné úloze. Máme bludiště 9 × 14 políček a chceme se dostat ze startu do cíle co nejlevněji. Nejdřív, co které políčko znamená:

  • Volné políčko — vstup na něj stojí 1
  • Bahno — projít se dá, ale vstup stojí 5
  • Zeď — vstoupit na ni nelze
  • S Start — počáteční stav, políčko (0, 0)
  • G Cíl (goal) — políčko (8, 13)
  • Prozkoumané políčko — algoritmus ho rozbalil
  • Nalezená cesta — výsledek algoritmu
S G 0 1 2 3 4 5 6 7 8 9 10 11 12 13 0 1 2 3 4 5 6 7 8
Bludiště ze zadání — čísla u okraje jsou řádky a sloupce
Formální zadání

Políčko zapíšeme jako dvojici (r, s) — řádek r ∈ {0, …, 8}, sloupec s ∈ {0, …, 13}. Označme Z množinu zdí a B množinu políček s bahnem.

  • Stavy: S = ({0, …, 8} × {0, …, 13}) ∖ Z — všechna políčka kromě zdí.
  • Počáteční stav: s0 = (0, 0). Cíl: G = {(8, 13)}.
  • Posuny: ↑ = (−1, 0), → = (0, 1), ↓ = (1, 0), ← = (0, −1). Akce použitelné ve stavu (r, s) ∈ S jsou ty, které vedou na políčko uvnitř mřížky, které není zeď: A((r, s)) = {(Δr, Δs) ∈ {↑, →, ↓, ←} : (r + Δr, s + Δs) ∈ S}.
  • Přechod: T((r, s), (Δr, Δs)) = (r + Δr, s + Δs), např. T((r, s), →) = (r, s + 1).
  • Cena: c(x, a) = 5, pokud T(x, a) ∈ B, jinak c(x, a) = 1 — platí se za políčko, na které vstupujeme.
  • Úkol: najít řešení s nejmenší cenou C*.
Laicky řečeno

Panáček stojí vlevo nahoře a chce doprava dolů. Chodí jen nahoru, dolů, doleva a doprava, přes zeď neprojde a každý krok ho stojí 1 — kromě kroku do bahna, ten stojí 5. Hledáme cestu, která ho bude stát nejméně.

Bahno leží schválně na nejpřímější trase. Uvidíme, že ne každý algoritmus si ho všimne: některé počítají jen kroky, jiné skutečnou cenu.

3. Společná kostra všech algoritmů

Všechny algoritmy v článku mají stejnou kostru. Liší se jen v jednom rozhodnutí.

Definice — okraj, uzavřené stavy, rozbalení

Nechť P = (S, s0, A, T, G, c) je úloha prohledávání. Pro stav s ∈ S označme množinu jeho následníků

N(s) = {T(s, a) : a ∈ A(s)}.

Algoritmus prohledávání si v každém okamžiku běhu udržuje dvě množiny stavů:

  1. okraj (frontier) F ⊆ S — stavy, které algoritmus už objevil (vede do nich nalezená cesta z s0), ale ještě nerozbalil; na začátku F = {s0};
  2. uzavřené stavy E ⊆ S — stavy, které už rozbalil; na začátku E = ∅.

Rozbalit stav s ∈ F znamená provést tyto dvě změny (v tomto pořadí):

E ← E ∪ {s},   F ← (F ∖ {s}) ∪ (N(s) ∖ E).

Rozbalený stav se tedy přesune z okraje mezi uzavřené a do okraje přibudou ti jeho následníci, kteří ještě nebyli rozbaleni.

Laicky řečeno

Okraj je seznam míst, o kterých víte, že tam vede cesta, ale ještě jste se tam nepodívali. Uzavřené stavy jsou místa, kde už jste byli — díky nim nechodíte v kruhu. Rozbalit stav znamená postavit se na něj a rozhlédnout se, kam se dá jít dál.

okraj = [start]
uzavrene = {}
dokud okraj není prázdný:
    stav = vyber_z(okraj)          # ← tady se algoritmy liší
    když je stav cíl: hotovo
    přidej stav do uzavrene
    pro každého následníka stavu:
        když ještě není uzavřený: přidej ho do okraje

Celý rozdíl mezi algoritmy je v jediném řádku: který stav z okraje vybereme jako další. Fronta, zásobník, nebo prioritní fronta podle nějakého čísla. Na tom stojí všechno ostatní.

4. Prohledávání do šířky (BFS)

Definice — BFS

Vstup: úloha prohledávání P = (S, s0, A, T, G, c). Výstup: řešení (cesta z s0 do některého stavu z G), nebo zpráva „řešení neexistuje“.

Prohledávání do šířky (BFS) je společná kostra výše, ve které je okraj F fronta (FIFO, first in — first out):

  1. F ← (s0), E ← ∅.
  2. Je-li F prázdná, skonči: řešení neexistuje.
  3. Odeber z F stav s, který do ní přibyl nejdřív.
  4. Je-li s ∈ G, skonči a vrať cestu z s0 do s.
  5. Rozbal s: přidej ho do E a každého následníka s′ ∈ N(s), který není v E ani v F, zařaď na konec F. Pokračuj krokem 2.

Vlastnosti. Označme d(s) hloubku stavu s — nejmenší délku cesty z s0 do s. BFS rozbaluje stavy v neklesajícím pořadí d(s), a proto vrátí řešení s nejmenším počtem akcí. Nechť b = maxs ∈ S |A(s)| je větvicí faktor (největší počet následníků stavu) a d hloubka nejmělčího řešení. Pak BFS vygeneruje nejvýš b + b2 + … + bd = O(bd) stavů a stejně tolik jich drží v paměti.

Laicky řečeno

Kdo přišel první, jde první na řadu — jako fronta v obchodě. Nejdřív prozkoumáte všechno jeden krok od startu, pak všechno dva kroky, pak tři. Šíří se to jako vlna na vodě, a proto první cesta, která dorazí do cíle, má nejméně kroků. Háček: vlnu si musíte pamatovat celou, a ta roste strašně rychle.

A 1 B 2 C 3 D 4 E 5 F 6 G 7 H 8 I 9 J 10
Pořadí návštěv při BFS — po vrstvách A → B → C → D → E → F → G → H → I → J

V bludišti je vlna vidět dobře — rozlévá se všemi směry, dokud nenarazí na cíl:

S G
Prohledávání do šířky prozkoumáno políček: 75 kroků: 25 cena cesty: 37

Všimněte si ale, že cesta vede přímo bahnem. BFS počítá akce, ne cenu — krok do bahna je pro něj stejný jako krok po suchu. Nejkratší cesta tak vyšla na 25 kroků, ale cenu 37. BFS je optimální, jen když mají všechny akce stejnou cenu.

Příklad — cena cesty BFS a jak rychle roste vlna

Cesta BFS má 25 kroků, z toho 3 do bahna: g = 22 · 1 + 3 · 5 = 22 + 15 = 37.

Velikost vlny pro větvicí faktor b = 10: v hloubce d = 5 je to 10 + 100 + 1 000 + 10 000 + 100 000 = 111 110 stavů, v hloubce d = 10 už 11 111 111 110 ≈ 1,1 · 1010. Kdyby jeden stav zabral v paměti 1 kB, potřebovala by vlna v hloubce 10 přes 11 TB. Každé další patro to zdesetinásobí.

from collections import deque

def bfs(start, cil, nasledovnici):
    okraj = deque([start])
    rodic = {start: None}
    while okraj:
        stav = okraj.popleft()          # fronta: první dovnitř, první ven
        if stav == cil:
            return cesta(rodic, cil)
        for dalsi in nasledovnici(stav):
            if dalsi not in rodic:
                rodic[dalsi] = stav
                okraj.append(dalsi)
    return None

def cesta(rodic, stav):
    vysledek = []
    while stav is not None:
        vysledek.append(stav)
        stav = rodic[stav]
    return vysledek[::-1]

5. Prohledávání do hloubky (DFS)

Definice — DFS

Vstup: úloha prohledávání P = (S, s0, A, T, G, c). Výstup: řešení, nebo zpráva „řešení neexistuje“.

Prohledávání do hloubky (DFS) je společná kostra výše, ve které je okraj F zásobník (LIFO, last in — first out):

  1. F ← (s0), E ← ∅.
  2. Je-li F prázdný, skonči: řešení neexistuje.
  3. Odeber z F stav s, který do něj přibyl naposledy (vrchol zásobníku).
  4. Je-li s ∈ G, skonči a vrať cestu z s0 do s.
  5. Rozbal s: přidej ho do E a každého následníka s′ ∈ N(s) ∖ E polož na vrchol F. Pokračuj krokem 2.

Vlastnosti. Nechť b je větvicí faktor a m největší hloubka stavového prostoru (největší délka cesty bez opakování stavů, případně ∞). Ve stromové variantě (bez množiny E, opakování stavů se hlídá jen na aktuální větvi) drží DFS v paměti jen aktuální větev a její sourozence: O(b · m) stavů. Čas je v nejhorším případě O(bm). DFS není optimální a v nekonečném prostoru ani úplné — může sestupovat donekonečna.

Laicky řečeno

Jako když v bludišti jdete pořád za pravou rukou: vyberete si jednu chodbu a jdete po ní až na konec. Teprve ve slepé uličce se vrátíte k poslední odbočce a zkusíte jinou. Paměti to potřebuje málo — pamatujete si jen cestu, kterou zrovna jdete. Ale nejkratší cestu tím najdete jen náhodou.

A 1 B 2 D 3 H 4 I 5 E 6 C 7 F 8 G 9 J 10
Pořadí návštěv při DFS — nejdřív do hloubky A → B → D → H → I → E → C → F → G → J

Ve stromu je to vidět na pořadí: A, B, D, H, I — a teprve potom E. Proč, ukáže okraj krok po kroku. Kostra je u obou stejná, liší se jen to, z kterého konce okraje se bere: BFS bere zleva nejstarší stav (hlava fronty), DFS zleva nejnovější (vrchol zásobníku).

krokBFS rozbalífronta potomDFS rozbalízásobník potom
1AB CAB C
2BC D EBD E C
3CD E F GDH I E C
4DE F G H IHI E C
5EF G H IIE C
6FG H IEC
7GH I JCF G
8HI JFG
9IJGJ
10J—J—

Ve druhém kroku oba přidají děti uzlu B, tedy D a E. BFS je zařadí na konec fronty za C a jako další vezme C, protože čeká nejdéle. DFS je položí na vrchol zásobníku nad C a vezme D, protože přišlo naposledy — a děti H, I se pak dostanou dokonce před E i C. Fronta naroste až na pět uzlů (E F G H I), zásobník nejvýš na čtyři (H I E C): u BFS je v okraji celé patro, u DFS jen jedna větev a sourozenci jejích uzlů. Na tak malém stromu je to rozdíl jednoho uzlu, s každým patrem ale roste. V bludišti:

S G
Prohledávání do hloubky prozkoumáno políček: 32 kroků: 31 cena cesty: 59

Tady měl DFS štěstí a k cíli se dostal, aniž by prozkoumal moc políček. Cesta je ale zbytečně dlouhá — 31 kroků místo 25 a cena 59.

Definice — iterativní prohlubování (IDDFS)

Vstup: úloha prohledávání P = (S, s0, A, T, G, c). Výstup: řešení s nejmenším počtem akcí (pokud nějaké řešení existuje).

Pro ℓ ∈ ℕ0 označme DLS(ℓ) (depth-limited search) prohledávání do hloubky, které nerozbaluje stavy v hloubce ℓ a větší — cesty delší než ℓ tedy nezkoumá. Iterativní prohlubování (IDDFS):

  1. ℓ ← 0.
  2. Spusť DLS(ℓ).
  3. Našlo-li DLS(ℓ) řešení, skonči a vrať ho.
  4. ℓ ← ℓ + 1 a pokračuj krokem 2.

Podmínka zastavení: první ℓ, pro které DLS(ℓ) najde řešení; to je právě hloubka d nejmělčího řešení. (Neexistuje-li řešení a prostor je nekonečný, algoritmus neskončí.)

Vlastnosti. Nechť b je větvicí faktor a d hloubka nejmělčího řešení. Stavy v hloubce i ∈ {1, …, d} se vygenerují (d − i + 1)krát, celkem ∑i=1d (d − i + 1) · bi = O(bd) stavů — stejně jako BFS, ale s pamětí jen O(b · d).

Laicky řečeno

Pustíte DFS, ale zakážete mu jít hlouběji než o jeden krok. Nenašlo nic? Tak o dva kroky, pak o tři… Horní patra procházíte znovu a znovu, ale většina stavů leží vždycky v tom nejhlubším, takže opakování skoro nic nestojí. Dostanete nejkratší cestu jako u BFS a malou paměť jako u DFS.

NIDDFS = ∑i=1d (d − i + 1) · bi

NIDDFS
kolik stavů celé iterativní prohlubování vygeneruje — tím se měří jeho čas
∑i=1d
sčítáme po patrech stromu, od hloubky 1 až po hloubku řešení d — hlouběji se nikdy nejde
(d − i + 1)
kolikrát se patro i projde: v každém kole s limitem ℓ = i, i + 1, …, d — mělká patra se opakují nejčastěji
bi
kolik stavů v patře i je — s hloubkou roste exponenciálně, takže nejhlubší patro, projité jen jednou, převáží všechno ostatní
Pro b = 10, d = 5: NIDDFS = 5 · 10 + 4 · 100 + 3 · 1 000 + 2 · 10 000 + 1 · 100 000 = 50 + 400 + 3 000 + 20 000 + 100 000 = 123 450. BFS vygeneruje 111 110 stavů, opakování tedy stojí jen 123 450/111 110 − 1 ≈ 11 % navíc (příklad z učebnice Russell a Norvig, Artificial Intelligence: A Modern Approach).
def iddfs(start, je_cil, nasledovnici):
    limit = 0
    while True:                            # ℓ = 0, 1, 2, …
        cesta = dls(start, je_cil, nasledovnici, limit)
        if cesta is not None:
            return cesta
        limit += 1

def dls(stav, je_cil, nasledovnici, limit):   # DFS s omezenou hloubkou
    if je_cil(stav):
        return [stav]
    if limit == 0:
        return None                        # hlouběji nesmíme
    for dalsi in nasledovnici(stav):
        cesta = dls(dalsi, je_cil, nasledovnici, limit - 1)
        if cesta is not None:
            return [stav] + cesta
    return None

6. Prohledávání podle ceny (UCS)

Definice — UCS

Vstup: úloha prohledávání P = (S, s0, A, T, G, c). Výstup: řešení s nejmenší cenou, nebo zpráva „řešení neexistuje“.

Během běhu přiřazuje algoritmus každému objevenému stavu n ∈ S číslo g(n) — cenu nejlevnější dosud nalezené cesty z s0 do n. Okraj F je prioritní fronta uspořádaná podle g. Prohledávání podle ceny (UCS, uniform-cost search):

  1. F ← {s0}, g(s0) ← 0, E ← ∅.
  2. Je-li F = ∅, skonči: řešení neexistuje.
  3. Odeber z F stav n s nejmenším g(n).
  4. Je-li n ∈ G, skonči a vrať nalezenou cestu do n (její cena je g(n)).
  5. Přidej n do E. Pro každou akci a ∈ A(n) a n′ = T(n, a) ∉ E: je-li n′ ∉ F, nebo g(n) + c(n, a) < g(n′), nastav g(n′) ← g(n) + c(n, a), zapamatuj si n jako předchůdce n′ a vlož n′ do F. Pokračuj krokem 2.

Vlastnosti. Nechť existuje ε > 0 takové, že c(s, a) ≥ ε pro všechna s ∈ S, a ∈ A(s). Pak UCS rozbaluje stavy v neklesajícím pořadí g, a proto je úplné i optimální. Počet rozbalených stavů je O(b1 + ⌊C*/ε⌋), kde b je větvicí faktor a ⌊x⌋ dolní celá část čísla x.

Laicky řečeno

Místo kroků počítáte peníze. Vždycky pokračujete z místa, kam jste se zatím dostali nejlevněji. Když do cíle dorazíte, víte jistě, že levněji to nejde — všechno levnější jste už prozkoumali. Ve všech grafech s kladnými cenami to dělá známý Dijkstrův algoritmus; UCS je jeho verze, která se zastaví v cíli.

S G
Prohledávání podle ceny prozkoumáno políček: 49 kroků: 25 cena cesty: 25

UCS bahno obešel. Našel cestu se stejným počtem kroků jako BFS, ale s cenou 25 místo 37 — a ta je zaručeně nejlevnější, tedy C* = 25. Platí za to tím, že prohledává slepě do všech stran: neví, kde cíl leží, takže se šíří i směrem od něj.

Příklad — v jakém pořadí UCS rozbaluje a co říká odhad složitosti

Prvních deset rozbalení s hodnotou g: (0, 0) g = 0; (0, 1) g = 1; (1, 0) g = 1; (0, 2) g = 2; (2, 0) g = 2; (0, 3) g = 3; (3, 0) g = 3; (1, 3) g = 4; (4, 0) g = 4; (2, 3) g = 5. Dokud nenarazí na bahno, rozbaluje UCS stejně jako BFS, protože každý krok stojí 1 a g je prostě hloubka. Bahno pak stav „odsune“ o 5 místo o 1 — do bahna se UCS podívá, až když vyčerpá všechno levnější.

Nalezená cesta: 25 kroků po suchu, C* = 25 · 1 = 25. Nejmenší cena akce je ε = 1 a větvicí faktor b = 4, horní odhad je tedy b1 + ⌊C*/ε⌋ = 426 ≈ 4,5 · 1015 stavů. Ve skutečnosti UCS rozbalil 49 políček: odhad počítá s nekonečným stromem, kde se nic neopakuje, kdežto bludiště má jen 83 průchozích políček a uzavřené stavy se znovu nerozbalují.

7. Heuristika: odhad, kudy k cíli

Algoritmy výše jsou neinformované — o cíli vědí jen to, že ho poznají, až na něj šlápnou. Informované algoritmy dostanou navíc odhad, kolik ještě zbývá.

Definice — heuristika

Nechť P = (S, s0, A, T, G, c) je úloha prohledávání. Pro stav n ∈ S označme h*(n) ∈ ℝ≥0 ∪ {∞} nejmenší cenu (přesněji infimum cen) cest ze stavu n do některého cílového stavu (cesta je definovaná stejně jako výše, jen začíná v n místo s0; nevede-li z n do G žádná cesta, je h*(n) = ∞). Heuristika je funkce

h : S → ℝ≥0  taková, že  ∀n ∈ G : h(n) = 0.

Hodnotu h(n) chápeme jako odhad h*(n). Heuristika h je

  1. přípustná (admissible), právě když ∀n ∈ S : h(n) ≤ h*(n);
  2. konzistentní (monotónní), právě když ∀n ∈ S ∀a ∈ A(n) : h(n) ≤ c(n, a) + h(T(n, a)).

Přípustná heuristika tedy skutečnou zbývající cenu nikdy nepřecení; konzistentní heuristika po žádném kroku neklesne o víc, než kolik ten krok stál. Každá konzistentní heuristika je přípustná (věta níže).

Laicky řečeno

Heuristika je kvalifikovaný odhad — jako když v cizím městě víte, že nádraží je „někde na severu“. Přípustná znamená optimistická: nikdy netvrdí, že je to dál, než to ve skutečnosti je. Konzistentní znamená, že si odhad po jednom kroku neprotiřečí — nemůže se zlepšit víc, než kolik jste za ten krok zaplatili.

h(n) ≤ c(n, a) + h(n′)

h(n)
co heuristika slibuje teď, ve stavu n — tenhle slib nesmí být přehnaný
c(n, a)
skutečná cena jednoho kroku do následníka; za ten krok už opravdu zaplatíme
h(n′)
odhad zbytku z následníka. Kdyby součet vpravo byl menší než h(n), přes n′ by vedla cesta levnější, než h(n) tvrdí, a jeden z obou odhadů by byl nesmysl
Trojúhelníková nerovnost: přímý odhad z n nesmí být horší než „jeden krok a odhad odtamtud“.
Věta — konzistentní heuristika je přípustná

Nechť P = (S, s0, A, T, G, c) je úloha prohledávání a h : S → ℝ≥0 konzistentní heuristika. Pak h je přípustná, tj. h(n) ≤ h*(n) pro každé n ∈ S.

Důkaz. Nechť n ∈ S. Je-li h*(n) = ∞, nerovnost platí triviálně. Jinak vezměme libovolnou cestu z n do cíle: n = n0, n1, …, nk ∈ G, kde ni = T(ni−1, ai) a ci = c(ni−1, ai) pro i = 1, …, k. Konzistenci použijeme kkrát za sebou:

h(n0) ≤ c1 + h(n1) ≤ c1 + c2 + h(n2) ≤ … ≤ c1 + … + ck + h(nk).

V cíli je h(nk) = 0, takže h(n) je nejvýš cena této cesty. To platí pro každou cestu z n do cíle, tedy i pro tu nejlevnější, jejíž cena je h*(n). Tedy h(n) ≤ h*(n). ∎

Opačně to neplatí: přípustná heuristika konzistentní být nemusí.

V našem bludišti se nabízí manhattanská vzdálenost — počet políček vodorovně plus svisle, jako když jdete po pravoúhlých ulicích Manhattanu:

h((r, s)) = |r − 8| + |s − 13|

h((r, s))
odhad zbývající ceny z políčka v řádku r a sloupci s
|r − 8|
kolik řádků zbývá k cíli (8, 13). Absolutní hodnota proto, že vzdálenost se počítá stejně, ať je cíl nad, nebo pod námi; svislý tah jich ubere nejvýš jeden
|s − 13|
kolik sloupců zbývá. Sčítáme, protože diagonálně chodit nejde: každý řádek i sloupec stojí aspoň jeden samostatný tah
Ze startu: h((0, 0)) = |0 − 8| + |0 − 13| = 8 + 13 = 21 ≤ C* = 25. Heuristika je konzistentní (a tedy přípustná): jedna akce změní h nejvýš o 1 a stojí nejméně 1.
Laicky řečeno

Spočítáte, kolik políček je cíl dolů a kolik doprava, a sečtete to. Zdi a bahno cestu jen prodlouží nebo prodraží, nikdy nezkrátí — takže odhad je vždycky optimistický, přesně jak má být.

8. Hladové hledání

Definice — hladové hledání

Vstup: úloha prohledávání P = (S, s0, A, T, G, c) a heuristika h : S → ℝ≥0. Výstup: řešení, nebo zpráva „řešení neexistuje“.

Hladové hledání (greedy best-first search) je společná kostra výše, ve které je okraj F prioritní fronta podle

f(n) = h(n):

  1. F ← {s0}, E ← ∅.
  2. Je-li F = ∅, skonči: řešení neexistuje.
  3. Odeber z F stav n s nejmenším h(n) — stav, který je podle odhadu nejblíž cíli.
  4. Je-li n ∈ G, skonči a vrať cestu z s0 do n.
  5. Rozbal n: přidej ho do E a každého následníka n′ ∈ N(n), který není v E ani v F, vlož do F. Pokračuj krokem 2.

Dosavadní cena g(n) se nebere v úvahu. Vlastnosti: hladové hledání není optimální; s hlídáním opakovaných stavů (množinou E) je v konečném prostoru úplné.

Laicky řečeno

Jdete vždycky tam, kde to vypadá nejblíž k cíli, a vůbec neřešíte, kolik vás cesta zatím stála. Rychle to míří správným směrem, ale snadno vás to nachytá — třeba na bahno, které leží „správným směrem“.

S G
Hladové hledání prozkoumáno políček: 33 kroků: 31 cena cesty: 59

Hladové hledání prozkoumá málo políček, jenže jde tam, kde to vypadá blíž, a skončí na dlouhé a drahé cestě — 31 kroků, cena 59.

Příklad — prvních deset kroků hladového hledání

Rozbalená políčka s hodnotou h: (0, 0) 21 → (0, 1) 20 → (0, 2) 19 → (0, 3) 18 → (1, 3) 17 → (2, 3) 16 → (2, 2) 17 → (3, 2) 16 → (4, 2) 15 → (4, 3) 14.

Hladové hledání jde rovnou doprava a dolů, dokud h klesá. V sedmém kroku h na chvíli vzroste (ze 16 na 17), protože z (2, 3) dál doprava ani dolů nevede cesta (zdi) a v okraji už žádné políčko s menším h není. O dva kroky později vstoupí do bahna na (4, 2), protože tudy h zase klesá. Cenu 5 za krok do bahna vůbec nevidí: priorita je jen h, žádné g.

9. A*: to nejlepší z obou světů

Definice — A*

Vstup: úloha prohledávání P = (S, s0, A, T, G, c) a heuristika h : S → ℝ≥0. Výstup: řešení, nebo zpráva „řešení neexistuje“.

A* je UCS (kroky 1–5 jeho definice výše, včetně udržování g), ve kterém je okraj F prioritní fronta podle

f(n) = g(n) + h(n)

místo podle g(n): v kroku 3 se z F odebírá stav s nejmenším f(n). Hodnota f(n) je odhad ceny celé cesty ze startu přes n do cíle. Algoritmus skončí, jakmile odebere z F cílový stav (nebo když je F prázdná).

Věta. Nechť h je konzistentní heuristika a ceny akcí jsou ≥ ε pro nějaké ε > 0. Pak hodnoty f podél každé cesty neklesají, A* rozbaluje stavy v neklesajícím pořadí f a první rozbalený cílový stav leží na optimální cestě s cenou C*. Rozbalí přitom každý stav s f(n) < C* a žádný s f(n) > C*.

Pro h ≡ 0 je A* totéž co UCS; bez g by to bylo hladové hledání.

Laicky řečeno

A* u každého místa sečte, kolik už jste zaplatili a kolik podle odhadu ještě zaplatíte. Díky první části se nenechá nalákat na drahou zkratku, díky druhé neztrácí čas cestami, které vedou od cíle. Když je odhad optimistický, najde vždycky nejlevnější cestu — a prozkoumá přitom jen to, co prozkoumat musí.

f(n) = g(n) + h(n)

f(n)
priorita v okraji: odhad ceny celého řešení, které vede přes n. Rozbaluje se nejmenší, protože to je nejslibnější kandidát na optimum
g(n)
kolik už cesta ze startu do n skutečně stála. Bez něj by A* nevěděl o bahnu, kterým prošel, a choval by se jako hladové hledání
h(n)
odhad, kolik ještě zbývá do cíle. Bez něj by A* nevěděl, kde cíl leží, a šířil by se do všech stran jako UCS
Ze startu: f((0, 0)) = 0 + 21 = 21. Na políčku (6, 13), dva kroky před cílem: f((6, 13)) = 23 + 2 = 25 = C*.
Věta — s konzistentní heuristikou hodnota f podél cesty neklesá

Nechť h je konzistentní heuristika, f = g + h, n ∈ S, a ∈ A(n), n′ = T(n, a) a cesta do n′ vede přes n, takže g(n′) = g(n) + c(n, a). Pak f(n′) ≥ f(n).

Důkaz. f(n′) =g(n) + c(n, a) + h(n′) ≥ g(n) + h(n) = f(n),

kde nerovnost je přesně konzistence h(n) ≤ c(n, a) + h(n′). ∎

Z toho (zde jen náčrtem) plyne, že A* rozbaluje stavy v neklesajícím pořadí f: kdyby později rozbalil stav s menším f, musel by na něj vést okraj se stavem s ještě menším f, který měl přijít na řadu dřív. Když se tedy poprvé rozbalí cíl (kde h = 0 a f = g), žádná levnější cesta už nezbývá.

S G
A* s manhattanskou vzdáleností prozkoumáno políček: 37 kroků: 25 cena cesty: 25

Jak A* postupuje, ukazuje tabulka prvních kroků. U každého rozbaleného políčka je g, h a f, vpravo okraj po rozbalení v pořadí, v jakém ho fronta vydá (číslo za políčkem je jeho f; při shodě jde dřív to, co přibylo dřív; barvy podle vzorce výše):

krokrozbalíghfokraj potom
1(0, 0)02121(0, 1) 21, (1, 0) 21
2(0, 1)12021(1, 0) 21, (0, 2) 21
3(1, 0)12021(0, 2) 21, (2, 0) 21
4(0, 2)21921(2, 0) 21, (0, 3) 21
5(2, 0)21921(0, 3) 21, (3, 0) 21
6(0, 3)31821(3, 0) 21, (1, 3) 21
……
23(8, 9)17421(2, 2) 23, (8, 1) 23, (7, 5) 23, (7, 9) 23, (6, 3) 25, (5, 2) 27
24(2, 2)61723(8, 1) 23, (7, 5) 23, (7, 9) 23, (3, 2) 23, (6, 3) 25, (5, 2) 27
25(8, 1)111223(7, 5) 23, (7, 9) 23, (3, 2) 23, (6, 3) 25, (8, 0) 25, (5, 2) 27

Prvních 23 kroků má f = 21: A* jde po políčkách, kde se každý krok k cíli přiblíží, takže co přibude v g, ubude v h. Na políčku (8, 9) chodba končí zdí, a teprve teď přijdou na řadu políčka s f = 23. Krok po suchu, který se od cíle vzdálí, zvýší g o 1 a h také o 1, takže f o 2; obcházení zdi proto zvedá f po dvojkách. Celkem rozbalil 23 políček s f = 21, 5 políček s f = 23, 9 políček s f = 25. Přesně jak říká věta: všech 28 políček s f < C* = 25 rozbalit musel, z políček s f = 25 jen tolik, než narazil na cíl, a žádné s f > 25.

Stejná cena 25 jako UCS, ale A* prozkoumal 37 políček místo 49. V malém bludišti je to rozdíl pár políček; v mapě s miliony křižovatek je to rozdíl mezi okamžitou odpovědí a minutou čekání. Proto A* (a jeho varianty) najdete v navigacích, ve hrách i v plánování pohybu robotů.

import heapq

def a_star(start, cil, nasledovnici, h):
    okraj = [(h(start), 0, start)]        # (f, g, stav)
    g = {start: 0}
    rodic = {start: None}
    while okraj:
        _, cena, stav = heapq.heappop(okraj)
        if stav == cil:
            return cesta(rodic, cil), cena
        if cena > g[stav]:
            continue                       # zastaralý záznam, už známe levnější
        for dalsi, krok in nasledovnici(stav):
            nova = cena + krok
            if nova < g.get(dalsi, float("inf")):
                g[dalsi] = nova
                rodic[dalsi] = stav
                heapq.heappush(okraj, (nova + h(dalsi), nova, dalsi))
    return None

10. Srovnání algoritmů

Definice — vlastnosti algoritmu

Nechť 𝒜 je algoritmus, který na vstupu dostane úlohu prohledávání P a vrátí řešení, nebo zprávu „řešení neexistuje“. Algoritmus 𝒜 je

  1. úplný, právě když pro každou úlohu P, která má aspoň jedno řešení (Π ≠ ∅), 𝒜 po konečném počtu kroků skončí a vrátí řešení π ∈ Π;
  2. optimální, právě když pro každou úlohu P s Π ≠ ∅ vrátí řešení π s cenou g(π) = C*.

Parametry, na kterých závisí čas a paměť (pro úlohu P = (S, s0, A, T, G, c)):

  1. b = maxs ∈ S |A(s)| — větvicí faktor;
  2. d — hloubka nejmělčího řešení (nejmenší délka cesty z s0 do G);
  3. m — největší hloubka prostoru (největší délka cesty z s0 bez opakování stavů, případně ∞);
  4. C* — optimální cena a ε > 0 — dolní mez cen akcí.

Pro funkce u, v ≥ 0 těchto parametrů píšeme u = O(v), právě když existují konstanty K > 0 a x0 tak, že u(x) ≤ K · v(x) pro všechna x ≥ x0. O(·) tedy udává, jak čas (počet vygenerovaných stavů) nebo paměť (počet stavů držených najednou) nejvýš rostou s těmito parametry, až na konstantní násobek.

Laicky řečeno

Úplný = když cesta existuje, najde ji. Optimální = najde tu nejlevnější. O(bd) znamená „s každým dalším krokem do hloubky se práce znásobí“ — proto na paměti tolik záleží.

AlgoritmusÚplnýOptimálníČasPaměťV našem bludišti
BFSanopři stejných cenáchO(bd)O(bd)75 políček, cena 37
DFSne*neO(bm)O(b·m)32 políček, cena 59
IDDFSanopři stejných cenáchO(bd)O(b·d)—
UCSanoanoO(b1+⌊C*/ε⌋)O(b1+⌊C*/ε⌋)49 políček, cena 25
Hladovéne*neO(bm)O(bm)33 políček, cena 59
A*anoano**podle heuristikypodle heuristiky37 políček, cena 25

* V konečném prostoru s hlídáním opakovaných stavů najdou řešení vždycky. ** S konzistentní heuristikou.

11. Lokální hledání: když nejde o cestu

U některých úloh nás cesta nezajímá — chceme jen dobrý výsledný stav. Rozvrh hodin, rozmístění součástek na desce, nastavení parametrů.

Definice — lokální hledání

Nechť S je neprázdná množina stavů, f : S → ℝ funkce (účelová funkce) a N : S → 2S zobrazení, které každému stavu s ∈ S přiřadí konečnou neprázdnou množinu N(s) ⊆ S ∖ {s} (okolí stavu s, jeho prvky jsou sousedé). Úloha lokálního hledání je najít stav

s* ∈ argmaxs ∈ S f(s) = {s ∈ S : ∀s′ ∈ S : f(s) ≥ f(s′)},

případně aspoň stav s co největší hodnotou f. Stav s ∈ S je

  1. lokální maximum, právě když ∀s′ ∈ N(s) : f(s) ≥ f(s′);
  2. globální maximum, právě když ∀s′ ∈ S : f(s) ≥ f(s′).

Každé globální maximum je i lokální, opačně to neplatí.

Horolezecký algoritmus (hill climbing). Vstup: S, f, N jako výše a počáteční stav s ∈ S. Výstup: lokální maximum.

  1. Vyber s′ ∈ argmaxt ∈ N(s) f(t) (nejlepšího souseda).
  2. Je-li f(s′) ≤ f(s), skonči a vrať s — je to lokální maximum.
  3. Jinak s ← s′ a pokračuj krokem 1.
Laicky řečeno

Účelová funkce říká, jak dobré je řešení — čím výš, tím líp. Okolí jsou řešení, která dostanete malou změnou. Horolezec stojí v mlze na kopci, vidí jen kousek kolem sebe a jde vždycky tam, kde to stoupá. Když už všude kolem klesá, zastaví se — i když opodál může stát mnohem vyšší hora.

lokální maximum globální maximum stav s (poloha 0–1) účelová funkce f(s) (bez jednotky)
Horolezecký algoritmus uvízne na lokálním vrcholu
def horolezec(s, okoli, f):
    while True:
        nejlepsi = max(okoli(s), key=f)    # nejvyšší soused
        if f(nejlepsi) <= f(s):
            return s                       # nikde kolem není výš: vrchol (možná jen lokální)
        s = nejlepsi
Příklad — horolezec z obrázku krok po kroku

Stav je číslo s ∈ ⟨0, 1⟩, okolí jsou dva body o 0,02 vlevo a vpravo: N(s) = {s − 0,02, s + 0,02}. Krajina má dva vrcholy (lokální u s = 0,25, globální u 0,72) a horolezec vychází z s = 0,05.

kroksf(s − 0,02)f(s)f(s + 0,02)
00,050,0600,0700,087
10,070,0700,0870,115
20,090,0870,1150,157
30,110,1150,1570,216
40,130,1570,2160,289
50,150,2160,2890,373
60,170,2890,3730,457
70,190,3730,4570,531
80,210,4570,5310,582
90,230,5310,5820,600
100,250,5820,6000,582

V každém řádku je pravý soused nejvyšší, a tak se jde doprava. V posledním řádku jsou oba sousedé (0,582) nižší než f(0,25) = 0,600 a algoritmus skončí — na lokálním maximu, přestože globální maximum má f(0,72) = 0,950.

Proti uvíznutí se používá náhodný restart (pustit horolezce z různých míst), genetické algoritmy, které kříží a mutují celou populaci řešení, nebo simulované žíhání:

Definice — simulované žíhání

Vstup: S, f, N jako u lokálního hledání, počáteční stav s ∈ S, počáteční teplota T0 > 0, koeficient ochlazování q ∈ (0, 1) a minimální teplota Tmin ∈ (0, T0). Výstup: stav s ∈ S.

  1. T ← T0.
  2. Je-li T ≤ Tmin, skonči a vrať s.
  3. Vyber náhodně (rovnoměrně) souseda s′ ∈ N(s) a spočítej Δ = f(s′) − f(s).
  4. Je-li Δ > 0, polož s ← s′. Je-li Δ ≤ 0, polož s ← s′ s pravděpodobností exp(Δ/T), jinak s ponech.
  5. T ← q · T a pokračuj krokem 2.

„Teplota“ T tedy postupně klesá k nule a s ní i ochota přijmout horšího souseda. (Ochlazování T ← q · T je nejběžnější volba, používají se i jiné rozvrhy.)

Laicky řečeno

Na začátku si horolezec občas dovolí jít i z kopce — tím se dostane z malého vrcholu k většímu. Čím déle hledá, tím je opatrnější, až nakonec jen stoupá.

P(přijmout s′) = exp(Δ/T), když Δ ≤ 0

P(přijmout s′)
pravděpodobnost, že horší soused bude přesto přijat — díky ní se dá z lokálního vrcholu sejít dolů
exp
exponenciála exp(x) = ex dělá z libovolného záporného čísla hodnotu mezi 0 a 1, tedy platnou pravděpodobnost; převzato z fyziky, kde stejný tvar (Boltzmannův faktor) říká, jak pravděpodobný je stav s vyšší energií
Δ = f(s′) − f(s)
o kolik je soused horší (záporné číslo). Čím horší, tím menší šance: malé zhoršení se přijme snadno, velký propad skoro nikdy
T
teplota — jak moc jsme ochotní riskovat. Vysoká T smaže rozdíly (exponent je blízko 0, P blízko 1), nízká je zvětší
Pro zhoršení Δ = −0,1: při T = 1 je P = exp(−0,1/1) = e−0,1 ≈ 0,905, při T = 0,1 je P = exp(−0,1/0,1) = e−1 ≈ 0,368 a při T = 0,01 už P = exp(−0,1/0,01) = e−10 ≈ 0,000 045 — horolezec skoro jen stoupá.
def zihani(s, soused, f, T=1.0, ochlazeni=0.995, T_min=1e-3):
    while T > T_min:
        s2 = soused(s)                     # náhodný soused, ne nejlepší
        delta = f(s2) - f(s)
        if delta > 0 or random() < exp(delta / T):
            s = s2                         # lepší vždy, horší jen někdy
        T *= ochlazeni                     # teplota pomalu klesá
    return s

A tady se kruh uzavírá se strojovým učením. Trénink neuronové sítě je lokální hledání v prostoru jejích vah θ: gradientní sestup opakuje θ ← θ − η ∇L(θ) — krok o velikosti η tím směrem, kterým chybová funkce L klesá nejrychleji. Je to horolezec obrácený vzhůru nohama, který místo nejvyššího vrcholu hledá nejnižší údolí — a potýká se se stejnými problémy: s lokálními minimy a plošinami.

12. Shrnutí

  • Úloha prohledávání je šestice (S, s0, A, T, G, c). Řešení je cesta v grafu stavů, optimální řešení ta nejlevnější.
  • Algoritmy se liší jen tím, který stav z okraje vyberou: fronta (BFS), zásobník (DFS), nejmenší g (UCS), nejmenší h (hladové), nejmenší g + h (A*).
  • BFS najde nejméně kroků, UCS nejnižší cenu, A* nejnižší cenu a přitom prozkoumá nejméně — pokud má konzistentní heuristiku.
  • Když nejde o cestu, ale o výsledek, nastupuje lokální hledání — a od něj je to už jen krok ke gradientnímu sestupu a strojovému učení.

Chcete si algoritmy osahat? Nechte AI agenta napsat vlastní vizualizaci — a pak v kódu hledejte ten jediný řádek, kterým se od sebe liší.

Máte na to jiný názor? Napište na support@coden-app.com.

Agent na vašem počítači, ovládaný z telefonu