Riešenie sudoku genetickým algoritmom

Čo je to sudoku, asi takmer všetci vedia. Väčšina však pravdepodobne nevie, čo je genetický algoritmus alebo genetické programovanie.

Genetický algoritmus je nedeterministická metóda riešenia problému, vychádzajúca z princípov Darwinovej evolučnej teórie. 

Každé riešenie úlohy (aj „zlé“) sa nazýva chromozóm a je tvorené binárnym reťazcom o danej dĺžke, ktorá je rovnaká pre všetky chromozómy danej populácie. Populácia je konečná množina chomozómov. Základná populácia resp. nultá generácia populácie je začiatočný stav riešenia. Vývoj k optimálnemu riešeniu prebieha prirodzeným vývojom populácií. Nultá generácia je vygenerovaná náhodne, vygenerované chromozómy, musia byť riešením problému.

Proces reprodukcie:

  • Výber chromozómov na kríženie či mutáciu (pseudonáhodný výber podľa pravdepodobnosti úmernej jeho fitness)
  • Kríženie chromozómov (výmena podreťazcov, kde môže prebiehať kríženie jednobodovo, či viacbodovo)
  • Mutácia, náhodne zmutujú niektoré gény, mutuje sa s malou pravdepodobnosťou, aby zostala zachovaná genetická informácia

Náhodnosť sa zaisťuje pomocou generovania pseudonáhodných čísel.

Prečo som sa rozhodol pre genetický algoritmus?

Naprogramovať riešenie sudoku klasickým programovaním je „triviálna záležitosť“ a pre mňa ako programátora nie je žiadnou výzvou (?). Na druhej strane  Sudoku predstavuje NP-úplný problém a doteraz sa nepodarilo vyčísliť, koľko existuje riešení pre sudoku 16×16 , o vyšších dimenziách nehovoriac. Pre klasické sudoku 9×9 existuje  6 670 903 752 021 072 936 960 riešení, po odrátaní symetrických pozícií je ich „iba“ 5 472 730 538. Napriek tomu, že riešení je takéto ohromné množstvo, pravdepodobnosť toho, že náhodne rozhodíte 81 číslic od 1 do 9, pričom každá číslica sa vyskytne presne 9 krát a rozloženie číslic bude zodpovedať pravidlám sudoku je mizivá, keďže možných rozmiestnení číslic je odhadom 3.10 × 1037.

Genetický pohľad na mriežku sudoku

Hoci je mriežka dvojrozmerná, môžeme sa na ňu pozerať tak, že je to 81 lineárne usporiadaných buniek od 1 do 81, pričom bunky v prvom riadku sú 1 až 9, v druhom 10..18, až v poslednom 73..81.

Vytvorme triviálneho prarodiča v tvare 123456789 123456789 … 123456789, tohto prarodiča náhodne spermutujme a vytvorme nultú populáciu pozostávajúcu napríklad zo 100 exemplárov (v definitívnom programe si používateľ bude môcť zvoliť veľkosť populácie. Hoci „sedliacky“ rozum navráva, že čím je populácia väčšia, tým skôr bude spieť k optimálnemu riešeniu, po mojich prvotných experimentoch sa zdá, že to tak nie je. 100 členná populácia dosahovala oveľa lepšiu hodnotiacu funkciu než 200 členná, keď som však veľkosť populácie zmenšil na 50, tak zase na tom bola 100 členná populácia oveľa lepšie).

Ako budeme exempláre navzájom krížiť?

V aktuálnej populácii majme 100 (poprípade n) potencionálnych rodičov. Pseudonáhodne 1000 krát (10 x n) vylosujme „otca“ a „matku“ (pričom otec nemôže byť zároveň matkou) a losujme, ktorú časť genetickej informácie získa potomok od otca a ktorú od matky (pre jednoduchosť, nech sú exempláre bezpohlavné, potom môžeme hovoriť o rodičovi1 a rodičovi2. Týchto 1000 potomkov ohodnoťme funkciou životaschopnosti a pre splodenie ďalšej generácie zachovajme 100 najživotaschopnejších – vždy len 100 potomkov dosiahne pohlavnú zrelosť a reprodukčnú schopnosť. Aby sa zachovala kvalitná genetická informácia, do ďalšieho kola reprodukcie zachovajme 20 exemplárov s doteraz najlepšou životaschopnosťou. Potom  ďalšiu generáciu bude plodiť 20 exemplárov z predchádzajúcej populácie a 80 z aktuálnej (v konečnom programe si používateľ bude môcť zvoliť percento zachovania rodičov).

Aká časť genetickej informácie sa bude dediť?

Môžeme dediť jednotlivé bunky, to znamená, že pre každú bunku budeme losovať, či sa zdedí informácia od otca alebo od matky, alebo sa budú dediť jednotlivé riadky, stĺpce či štvorce. Pri dedení jednotlivých buniek sa síce genetická informácia zachováva, ale spätne po niekoľkých generáciách nikto nezistí od ktorého predka informácia pochádza. Druhou možnosťou je, že budeme dediť nejaké 9 členné úseky. Prvotne ťažko odhadnúť, ktorý spôsob dedenia bude efektívnejší. Preto otestujeme jednobodové i 9 bodové dedenie. Intuícia mi nahovára, že možno elementárnou jednotkou dedičnej infromácie by v sudoku 9×9 mala byť trojica buniek, v sudoku 16×16 štvorica, atď. Otestujeme preto i takýto prístup. Nultá populácia bude spoločná pre všetky tri metódy dedenia a otestujeme, ktorá metóda vedie po m generáciách k lepšej hodnotiacej funkcii.

Hodnotiaca funkcia

Hodnotiaca funkcia by mala merať o koľko sa potomok líši od optimálneho riešenia. Optimálne riešenie je dané pravidlami sudoku – v každom riadku, stĺpci a štvorci je práve jedna číslica daného druhu. V klasickom sudoku 9×9 sú to číslice 1 až 9. V sudoku 16×16 navyše pribúdajú číslice 0, A, B, C, D, E, F. Ak riadok, stĺpec alebo štvorec spĺňa pravidlá sudoku, tak ho hodnotiaca fukcia ohodnotí nulou. Ak je nejaká číslica v riadku, stĺpci, štvorci 2x či viackrát, tak každý výskyt číslice navyše prispieva hodnotou jedna. Nevýskyt číslice hodnotiť nebudeme, keďže číslica navyše spôsobuje nevýskyt nejakej inej číslice. Hodnotiaca funkcia potom pozostáva z troch hodnotení:

  • horizontálne – hodnotenie po riadkoch
  • vertikálne – hodnotenie po stĺpcoch
  • štvorcové – hodnotenie po štvorcoch

Pokiaľ umožníme úplné voľné dedenie, malo by navyše pribudnúť globálne vyhodnotenie z hľadiska odchýlky počtu jednotlivých číslic od ideálu. V sudoku 9×9 by sa každá číslica mala vyskytnúť práve 9 krát. Keďže odchýlka od tohto ideálu spôsobuje nemožnosť nájdenia optimálneho riešenia, prohibujme ju vyššou váhou napríklad váhou 3. Druhou možnosťou je obmedzenie jedného stupňa voľnosti napríklad tak, že riadky musia vždy spĺňať podmienku jedinečnosti výskytu každej cifry. Dosiahneme to úpravou generovania nultej populácie a dedením celých riadkov od jednotlivých rodičov.

Musí popri dedení a krížení nastúpiť aj mutácia?

V prvotnej populácii by musela nastať veľmi nepravdepodobná zhoda okolností, aby už prvotná genetická informácia potencionálne obsahovala optimálne riešenie problému. Ak budeme dediť celé riadky a populácia bude mať veľkosť 100, tak 100 prarodičov bude obsahovať 900 riadkov. Všetkých možných riadkov však je 9! (9 faktoriál čo predstavuje 362880 možných riadkov).  Že sa medzi týmito prvotne vygenerovanými riadkami bude vyskytovať konkrétny riadok na konkrétnom mieste, ktorý je riešením problému má pravdepodobnosť 900/9! a že tam bude všetkých 9 má pravdepodobnosť (900/9!)9. Ak hľadáme ľubovoľné riešenie, tak je pravdepodobnosť omnoho vyššia, napriek tomu je však stále veľmi malá.

Literatúra:

Každému udeľujem právo voľne použiť informácie z tohoto článku, s podmiekou uvedenia zdrojov.

P.S. Ako to súvisí s bridžom? Pripravujem si  pôdu na naprogramovanie nedeterministických riešení bridžových problémov, či už v rámci licitácie, obrany alebo zohrávky. Je veľmi pravdepodobné, že mnou skúmané cesty sú slepými uličkami, ak to tak naozaj je, tak moji nasledovníci budú vedieť, kade cesta nevedie a ak to tak nie je, …