Bežné labyrinty, ktoré deti lúštia v časopisoch pre deti však majú znázornený aj začiatok, aj koniec labyrintu. Naša optimalizácia sa teda zameria aj na zohľadnenie pozície cieľa. Zvoľme si teda problém, ktorý je vhodný pre A* algoritmus. Teda hľadáme bludisko, kde poznáme začiatok a koniec labyrintu. Veľmi pekným takýmto problémom je 12ta úloha v Advent of Code 2022.
Majme labyrint v ktorom sa môžeme pohybovať len vertikálne a horizontálne a navštíviť môžeme len tie susedné pozície, ktoré sú maximálne o 1 meter vyššie, rovnako vysoké alebo akokoľvek nižšie ako aktuálna pozícia. Výška je určená písmenom, teda napríklad b > a. Znaky S a E reprezentujú začiatok a koniec labyrintu.
Riešenie tohto labyrintu vyzerá nasledovne:
Tento labyrint má veľkosť len 7x5. Labyrint, ktorý chceme vyriešiť v Advent Of Code probléme, je však oveľa väčší.
Dijkstrov algoritmus si zo zoznamu vrcholov, ktoré chceme navštíviť vždy vybral ten, ktorý sme tam vložili ako prvý. A* vyberá taký vrchol zo zoznamu vrcholov, ktorého kombinovaná vzdialenosť od štartu a cieľa je najnižšia (vzdialenosť od cieľa len odhadujeme, keďže ju prirodzene nepoznáme). Teda ak je vrchol vzdialený od štartu 1 a od cieľa 4 uprednostníme ho pred vrcholom, ktorý je od štartu vzdialený 1 a od cieľa 6.
Z bodov a, b, c, d, ktoré sú rovnako vzdialené od S si vyberieme bod b, pretože je najbližšie k cieľu.
Ako teda bude náš algoritmus vyzerať?
V zozname vrcholov si potrebujeme držať aj nejaké údaje navyše. Minimálne z nich musí byť jasná vzdialenost_S a aktuálna pozícia
Takýto popis algoritmu pre nami riešený problém stačí. Treba si však uvedomiť, že bežne neriešime pohyb po mriežke v štyroch smeroch, ale skôr problémy hľadania najkratšej cesty medzi dvoma mestami. V týchto problémoch sú dĺžky ciest (hrán) rôzne. V takom prípade položky v zozname vrcholov V musíme aktualizovať ak sa ukáže, že nie sú ideálne. Teda bod 2.c sa rozšíri o overovanie, či sa daný vrchol už nenachádza v niektorej existujúcej ceste a v takom prípade prepočítame, či ho netreba aktualizovať.
Do nášho zoznamu vrcholov si budeme ukladať objekty, ktoré poznajú už prejdenú cestu. Z tejto cesty vieme určiť dĺžku už prejdenej trasy ako aj pozíciu na ktorej sa práve nachádzam
Heuristickú vzdialenosť tohto vrcholu od koncovej pozície môžeme určiť ako manhattanovskú vzdialenosť. Teda nasledovne:
Pozn. v priloženom výslednom kóde bude zohľadnená aj súradnica z, teda nadmorská výška
A teraz k samotnému A* algoritmu. Poďme v cykle hľadať najlepší vrchol (2a). Samozrejme zoradzovanie alebo zotriedenie v každej iterácii je zbytočné, ale je to najčitateľnejšie riešenie.
Následne zistíme, či sme už dosiahli koniec.
A nakoniec pridáme vhodných susedov do zoznamu na prehľadanie.
Finálne riešenie (s miernymi optimalizáciami) sú priložené v nasledujúcom gist-e. Vo finálnom riešení je A* implementovaný aj s implementačnými detailami, spomenutými vyššie.
Hľadanie najkratšej cesty je ideálne aj vizualizovať. Myslím, že je čas postaviť vedľa seba Dijkstru a A*. Verím, že rozdiel v rýchlosti spozorujete hneď:
Posledné 2 články boli o algoritmoch, ktoré výrazne neevokujú správanie umelej inteligencie. Sú však veľmi dôležité pre pochopenie toho, akým spôsobom dokážeme pracovať s grafmi, ktoré sú podkladom pre akýkoľvek algoritmus, ktorý sa správa inteligentne. Či už sa jedná o BFS, DFS, Beam search, MonteCarlo alebo vytrénovanú neurónovú sieť. A vlastne… to sú práve tie algoritmy, ktoré by som vám chcel ukázať…