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í.
Série Základy AI Díl 21 z 83
- Mapa umělé inteligence: co je co a jak to do sebe zapadá
- Stručná historie umělé inteligence: od Turinga po jazykové modely
- Data, model, trénink, inference: základní pojmy na jednom příkladu
- Jak číst matematický zápis: symboly, indexy, sumy a kvantifikátory
- Množiny a relace: stavebnice, ze které se skládají definice v AI
- Funkce a zobrazení: proč je každý model v AI funkce
- Logika a důkazy: pravidla, podle kterých se v AI uvažuje
- Grafy a stromy: struktura, na které stojí hledání i neuronové sítě
- Lineární algebra pro strojové učení: vektory, matice a proč AI hlavně násobí
- Derivace, gradient a řetízkové pravidlo: jak model zjistí, kterým směrem se učit
- Gradientní sestup: jak se model učí
- Co je pravděpodobnost: od hodu kostkou ke Kolmogorovovým axiomům
- Podmíněná pravděpodobnost a Bayesova věta: jak změnit odhad, když se dozvíte něco nového
- Náhodná veličina a distribuční funkce: jedna funkce, která o rozdělení řekne všechno
- Funkce hustoty a pravděpodobnostní funkce: proč má spojitá veličina v bodě pravděpodobnost nula
- Střední hodnota, rozptyl a kovariance: kde rozdělení leží, jak je široké a co s čím souvisí
- Přehled pravděpodobnostních rozdělení: Bernoulli, binomické, Poisson, rovnoměrné, normální a exponenciální
- Zákon velkých čísel a centrální limitní věta: proč se průměry chovají tak předvídatelně
- Statistika a odhady parametrů: maximální věrohodnost, MAP a intervaly spolehlivosti
- Teorie informace: entropie, křížová entropie a KL divergence — odkud se berou ztrátové funkce
- Prohledávání stavového prostoru: BFS, DFS, UCS a A* s animacemi
- Hry a protivník: minimax, alfa-beta ořezávání a Monte Carlo tree search
- Splňování omezujících podmínek: backtracking, propagace omezení a AC-3 na sudoku a mapě krajů
- Logika a znalostní systémy: dopředné a zpětné řetězení, rezoluce a expertní systémy
- Plánování: STRIPS, svět kostek, heuristiky z relaxace a plánování v AI agentech
- Lokální hledání a simulované žíhání: obchodní cestující přes krajská města
- Genetické algoritmy: populace, křížení, mutace a výběr na batohu na výlet
- Evoluční strategie a genetické programování: od pravidla jedné pětiny po Keplerův zákon
- Optimalizace mravenčí kolonií: jak feromonová stopa najde krátkou cestu
- Optimalizace hejnem částic: setrvačnost, vlastní zkušenost a zkušenost hejna
- Neuroevoluce: evoluce vah i tvaru neuronových sítí a algoritmus NEAT
- Fuzzy množiny a funkce příslušnosti: jak zapsat „trochu teplo“
- Fuzzy logika a pravděpodobnost: proč „trochu teplo“ neznamená „pravděpodobně teplo“
- Fuzzy logika: t-normy, implikace a pravidla KDYŽ–PAK
- Fuzzy řízení: Mamdani a Sugeno od ventilátoru po kyvadlo na vozíku
- Fuzzy shlukování: algoritmus fuzzy c-means a body mezi shluky
- Neuro-fuzzy systémy (ANFIS): fuzzy pravidla, která se učí z dat
- Markovovy řetězce: přechodová matice, stacionární rozdělení a PageRank
- Skryté Markovovy modely: Viterbiho algoritmus a nepoctivé kasino
- Bayesovské sítě: proč je tráva mokrá a co z toho plyne pro postřikovač
- Kalmanův filtr: jak z nepřesných měření odhadnout, kde auto je a jak rychle jede
- Druhy strojového učení: s učitelem, bez učitele, s částečným učitelem a další
- Přeučení a regularizace: proč model, který zná data nazpaměť, selže
- Vyhodnocení modelu: křížová validace, F1 a křivka ROC
- Shlukování a snížení dimenze: k-means, PCA a t-SNE
- Klasifikace a regrese: dvě úlohy učení s učitelem
- Lineární regrese: nejmenší čtverce geometricky i maticově
- Polynomiální a nelineární regrese: jak přímku ohnout
- Logistická regrese: sigmoida, šance a křížová entropie
- Vícetřídní klasifikace a softmax: od dvou tříd k mnoha
- Naivní Bayesův klasifikátor: spamový filtr, který funguje lépe, než by měl
- Ztrátové funkce: co přesně model minimalizuje
- Rozhodovací stromy a k nejbližších sousedů: modely bez vzorců
- Ansámbly: náhodný les a gradient boosting
- SVM a jádrový trik: nejširší hranice a jak ji ohnout
- Signál a vzorkování: proč platí Nyquistův teorém
- Fourierova transformace: rozklad signálu na frekvence
- Filtry a konvoluce: vyhlazení, hrany a odstranění šumu
- Zvuk a řeč: spektrogram, melová škála a MFCC
- Obraz jako signál: pixely, barvy, konvoluce a hrany
- Perceptron a umělý neuron: nejjednodušší neuronová síť a co neumí
- Neuronová síť od nuly: zpětné šíření chyby krok za krokem
- Aktivace, inicializace a normalizace: drobnosti, na kterých hluboká síť stojí
- Konvoluční sítě: jak síť poznává obrázky
- Rekurentní sítě: paměť pro posloupnosti a mizející gradient
- LSTM a GRU: brány, které rozhodují, co si síť zapamatuje
- Autoenkodéry: síť, která se učí data stlačit a zase rozbalit
- Embeddingy a word2vec: jak se slovo stane vektorem
- Mechanismus pozornosti: jak se model naučí, kam se dívat
- Transformery a velké jazykové modely: architektura krok za krokem
- Tokenizace: jak se text rozseká na kousky pro model
- Předtrénování a škálování: proč větší model a víc dat funguje
- Doladění a RLHF: jak se z doplňovače textu stane asistent
- Prompty, kontext a RAG: jak modelu dát správné informace
- Variační autoenkodéry (VAE): nové obrázky ze spojitého latentního prostoru
- Generativní adversariální sítě (GAN): padělatel proti detektivovi
- Difúzní modely: jak se z čistého šumu stane obrázek
- Markovovy rozhodovací procesy: stavy, akce, odměny a Bellmanovy rovnice
- Q-learning a DQN: učení hodnoty akcí od tabulky po neuronovou síť
- Policy gradient a PPO: učení přímo strategie
- Jak funguje AI agent: jazykový model ve smyčce s nástroji
- Programování s AI agenty: zadání, testy a kontrola
- Vyhodnocení a bezpečnost AI systémů: jak měřit a jak hlídat
Celá osnova série 83 z 83 témat hotovo
- 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:
- S je neprázdná množina; její prvky nazýváme stavy;
- s0 ∈ S; tento stav nazýváme počáteční stav;
- 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“;
- T : 𝒟 → S je přechodová funkce; pro (s, a) ∈ 𝒟 je stav s′ = T(s, a) následník stavu s (po akci a);
- G ⊆ S je množina cílových stavů;
- 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.
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í.
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:
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
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*.
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ů:
- 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};
- 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.
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):
- F ← (s0), E ← ∅.
- Je-li F prázdná, skonči: řešení neexistuje.
- Odeber z F stav s, který do ní přibyl nejdřív.
- Je-li s ∈ G, skonči a vrať cestu z s0 do s.
- 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.
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.
V bludišti je vlna vidět dobře — rozlévá se všemi směry, dokud nenarazí na cíl:
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):
- F ← (s0), E ← ∅.
- Je-li F prázdný, skonči: řešení neexistuje.
- Odeber z F stav s, který do něj přibyl naposledy (vrchol zásobníku).
- Je-li s ∈ G, skonči a vrať cestu z s0 do s.
- 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.
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.
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).
| krok | BFS rozbalí | fronta potom | DFS rozbalí | zásobník potom |
|---|---|---|---|---|
| 1 | A | B C | A | B C |
| 2 | B | C D E | B | D E C |
| 3 | C | D E F G | D | H I E C |
| 4 | D | E F G H I | H | I E C |
| 5 | E | F G H I | I | E C |
| 6 | F | G H I | E | C |
| 7 | G | H I J | C | F G |
| 8 | H | I J | F | G |
| 9 | I | J | G | J |
| 10 | J | — | 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:
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):
- ℓ ← 0.
- Spusť DLS(ℓ).
- Našlo-li DLS(ℓ) řešení, skonči a vrať ho.
- ℓ ← ℓ + 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).
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í
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):
- F ← {s0}, g(s0) ← 0, E ← ∅.
- Je-li F = ∅, skonči: řešení neexistuje.
- Odeber z F stav n s nejmenším g(n).
- Je-li n ∈ G, skonči a vrať nalezenou cestu do n (její cena je g(n)).
- 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.
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.
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
- přípustná (admissible), právě když ∀n ∈ S : h(n) ≤ h*(n);
- 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).
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
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
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):
- F ← {s0}, E ← ∅.
- Je-li F = ∅, skonči: řešení neexistuje.
- Odeber z F stav n s nejmenším h(n) — stav, který je podle odhadu nejblíž cíli.
- Je-li n ∈ G, skonči a vrať cestu z s0 do n.
- 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é.
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“.
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í.
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
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á.
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):
| krok | rozbalí | g | h | f | okraj potom |
|---|---|---|---|---|---|
| 1 | (0, 0) | 0 | 21 | 21 | (0, 1) 21, (1, 0) 21 |
| 2 | (0, 1) | 1 | 20 | 21 | (1, 0) 21, (0, 2) 21 |
| 3 | (1, 0) | 1 | 20 | 21 | (0, 2) 21, (2, 0) 21 |
| 4 | (0, 2) | 2 | 19 | 21 | (2, 0) 21, (0, 3) 21 |
| 5 | (2, 0) | 2 | 19 | 21 | (0, 3) 21, (3, 0) 21 |
| 6 | (0, 3) | 3 | 18 | 21 | (3, 0) 21, (1, 3) 21 |
| … | … | ||||
| 23 | (8, 9) | 17 | 4 | 21 | (2, 2) 23, (8, 1) 23, (7, 5) 23, (7, 9) 23, (6, 3) 25, (5, 2) 27 |
| 24 | (2, 2) | 6 | 17 | 23 | (8, 1) 23, (7, 5) 23, (7, 9) 23, (3, 2) 23, (6, 3) 25, (5, 2) 27 |
| 25 | (8, 1) | 11 | 12 | 23 | (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
- ú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í π ∈ Π;
- 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)):
- b = maxs ∈ S |A(s)| — větvicí faktor;
- d — hloubka nejmělčího řešení (nejmenší délka cesty z s0 do G);
- m — největší hloubka prostoru (největší délka cesty z s0 bez opakování stavů, případně ∞);
- 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.
Ú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í | Čas | Paměť | V našem bludišti |
|---|---|---|---|---|---|
| BFS | ano | při stejných cenách | O(bd) | O(bd) | 75 políček, cena 37 |
| DFS | ne* | ne | O(bm) | O(b·m) | 32 políček, cena 59 |
| IDDFS | ano | při stejných cenách | O(bd) | O(b·d) | — |
| UCS | ano | ano | O(b1+⌊C*/ε⌋) | O(b1+⌊C*/ε⌋) | 49 políček, cena 25 |
| Hladové | ne* | ne | O(bm) | O(bm) | 33 políček, cena 59 |
| A* | ano | ano** | podle heuristiky | podle heuristiky | 37 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
- lokální maximum, právě když ∀s′ ∈ N(s) : f(s) ≥ f(s′);
- 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.
- Vyber s′ ∈ argmaxt ∈ N(s) f(t) (nejlepšího souseda).
- Je-li f(s′) ≤ f(s), skonči a vrať s — je to lokální maximum.
- Jinak s ← s′ a pokračuj krokem 1.
Úč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.
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.
| krok | s | f(s − 0,02) | f(s) | f(s + 0,02) |
|---|---|---|---|---|
| 0 | 0,05 | 0,060 | 0,070 | 0,087 |
| 1 | 0,07 | 0,070 | 0,087 | 0,115 |
| 2 | 0,09 | 0,087 | 0,115 | 0,157 |
| 3 | 0,11 | 0,115 | 0,157 | 0,216 |
| 4 | 0,13 | 0,157 | 0,216 | 0,289 |
| 5 | 0,15 | 0,216 | 0,289 | 0,373 |
| 6 | 0,17 | 0,289 | 0,373 | 0,457 |
| 7 | 0,19 | 0,373 | 0,457 | 0,531 |
| 8 | 0,21 | 0,457 | 0,531 | 0,582 |
| 9 | 0,23 | 0,531 | 0,582 | 0,600 |
| 10 | 0,25 | 0,582 | 0,600 | 0,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.
- T ← T0.
- Je-li T ≤ Tmin, skonči a vrať s.
- Vyber náhodně (rovnoměrně) souseda s′ ∈ N(s) a spočítej Δ = f(s′) − f(s).
- Je-li Δ > 0, polož s ← s′. Je-li Δ ≤ 0, polož s ← s′ s pravděpodobností exp(Δ/T), jinak s ponech.
- 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.)
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ší
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.