O soluție pentru Sudoku bazată pe inteligență artificială

O soluție pentru Sudoku bazată pe inteligență artificială

Nodul sursă: 3091242

January 29 is National Puzzle Day, and in celebration, we’ve created a fun blog that details how to solve Sudoku using artificial intelligence (AI).

Introducere

Inainte de wordle, Sudoku puzzle a fost furie și este încă foarte popular. În ultimii ani, utilizarea de optimizare metodele de rezolvare a puzzle-ului a fost o temă dominantă. Vedea „Rezolvarea puzzle-ului Sudoku folosind optimizarea în Arkieva".

In current times, the use of AI is focused on machine learning which encompasses a wide range of methods from lasso regression to reinforcement learning. The use of AI has reaparut a aborda complexul programare provocări. O metodă, căutarea cu backtracking, este folosită în mod obișnuit și este perfectă pentru Sudoku.

Acest blog va oferi o descriere detaliată despre cum să utilizați această metodă pentru a rezolva Sudoku. După cum se dovedește, „backtracking” poate fi găsit în motoarele de optimizare și învățare automată și este o piatră de temelie a euristicii avansate pe care Arkieva le folosește pentru programare. Algoritmul este implementat într-un „Array Programming Language”, un limbaj de programare orientat pe funcții care gestionează un set bogat de structuri matrice.

Bazele Sudoku-ului

Din Wikipedia: Sudoku este un puzzle bazat pe logică, combinatoriu de plasare a numerelor. Obiectivul este de a umple o grilă de 9×9 cu cifre, astfel încât fiecare coloană, fiecare rând și fiecare dintre cele nouă subgrile de 3×3 care compun grila (numite și „cutii”, „blocuri”, „regiuni”, sau „sub-pătrate”) conține toate cifrele de la 1 la 9. Setatorul de puzzle oferă o grilă completă parțial, care are de obicei o soluție unică. Puzzle-urile finalizate sunt întotdeauna un tip de pătrat latin cu o constrângere suplimentară asupra conținutului regiunilor individuale. De exemplu, același număr întreg poate să nu apară de două ori în același rând sau coloană a tablei de joc 9×9 sau în oricare dintre cele nouă subregiuni 3×3 ale tablei de joc 9×9.

Tabelul 1 are un exemplu de problemă. Există 9 rânduri și 9 coloane pentru un total de 81 de celule. Fiecare are permisiunea de a avea unul și doar unul dintre cele nouă numere întregi între 1 și 9. În soluția inițială, o celulă fie are o singură valoare - care fixează valoarea din această celulă la acea valoare, fie celula este goală, indicând că avem nevoie pentru a găsi o valoare pentru această celulă. Celula (1,1) are valoarea 2 și celula (6,5) are valoarea 6. Celula (1,2) și celula (2,3) sunt goale, iar algoritmul va găsi o valoare pentru aceste celule.

Complicația

Pe lângă faptul că aparține unui singur rând și coloană, o celulă aparține unei singure casete. Există 1 casete și sunt indicate prin culoare în Tabelul 9. Tabelul 1 utilizează un număr întreg unic între 2 și 1 pentru a identifica fiecare casetă sau grilă. Celulele cu valoarea rândului (9, 1 sau 2) și valoarea coloanei (3, 1 sau 2) aparțin casetei 3. Caseta 1 reprezintă valorile rândurilor (6, 4, 5) și valorile coloanei (6, 7). , 8). ID-ul casetei este determinat de formula BOX_ID = {9x(floor((ROW_ID-3) /1)} + plafon (COL_ID/3). Pentru celula (3), 5,7 = 6x(floor(3-5) ))/1) + plafon (3/8)= 3×3 + 1 = 3+3.

Inima puzzle-ului

Pentru a găsi o valoare întreagă între 1 și 9 pentru fiecare celulă necunoscută, astfel încât numerele întregi de la 1 la 9 să fie utilizate o dată și o singură dată pentru fiecare coloană, fiecare rând și fiecare casetă.

Să ne uităm la celula (1,3) care este goală. Rândul 1 are deja valorile 2 și 7. Acestea nu sunt permise în această celulă. Coloana 3 are deja valorile 3, 5,6, 7,9. Acestea nu sunt permise. Caseta 1 (galben) are valorile 2, 3 și 8. Acestea nu sunt permise. Următoarele valori nu sunt permise (2,7); (3, 5, 6, 7, 9); (2, 3, 8). Valorile unice nepermise sunt (2, 3, 5, 6, 7, 8, 9). Singurele valori candidate sunt (1,4).

O abordare a soluției ar fi să atribuiți temporar 1 celulei (1,3) și apoi să încercați să găsiți valori candidate pentru o altă celulă.

O soluție de backtracking: pornirea componentelor

Structura matricei

Locul de pornire este de a decide asupra unei structuri de matrice pentru a stoca problema sursă și pentru a sprijini căutarea. Tabelul 3 are această structură matrice. Coloana 1 este un ID întreg unic pentru fiecare celulă. Valorile variază de la 1 la 81. Coloana 2 este ID-ul rândului celulei. Coloana 3 este ID-ul coloanei celulei. Coloana 4 este ID-ul casetei. Coloana 5 este valoarea din celulă. Observați că o celulă fără valoare primește valoarea zero în loc de goală sau nulă. Aceasta menține aceasta o „matrice numai cu numere întregi” – mult superioară pentru performanță.

În APL, această matrice ar fi stocată într-o matrice bidimensională în care forma este de 2 pe 81. Să presupunem că elementele din Tabelul 5 sunt stocate în variabila MAT. Exemple de funcții sunt:

Comanda MAT[1 2 3;]prinde primele 3 rânduri de MAT
1 1 1 1 2
2 1 2 1 0
3 1 3 1 0
MAT[1 2 3; 4 5] asigură rândurile 1, 2, 3 și doar coloanele 4 și 5
1
1
1
(MAT[;5]=0)/MAT[;1] găsește toate celulele care au nevoie de o valoare.

INTRODUCEȚI TABELUL 3

Verificare de sănătate: duplicate

Înainte de a începe căutarea, este esențial să verificați starea de spirit! Aceasta este soluția de pornire fezabilă. Este posibil pentru Sudoku, dacă există acum duplicate în orice rând, coloană sau casetă. Soluția de pornire curentă, de exemplu, 1 este fezabilă. Tabelul 4 are un exemplu în care soluția de pornire are duplicate. Rândul 1 are două valori 2. Zona 1 are două valori 2. Funcția „SANITY_DUPE” se ocupă de această logică.

Verificare sanitate: Opțiuni pentru celule fără valori

O informație foarte utilă ar fi toate valorile posibile pentru o celulă fără valoare. Dacă nu există candidați, atunci acest puzzle nu poate fi rezolvat. O celulă nu poate lua o valoare care este deja revendicată de vecinul său. Folosind Tabelul 1 pentru Celulă (1,3,'1' – acest ultim 1 este boxul), vecinii săi sunt rândul 1, coloana 3 și caseta 1. Rândul 1 are valorile (2,7); coloana 3 are valorile (3,5,6,7,9); caseta 1 are valorile (2,3,8). Celula (1,3.1) nu poate lua următoarele valori (2,3,5,6,7,8,9). Singurele opțiuni pentru celulă (1,3,1) sunt (1,4). Pentru celula (4,1,2), valorile 1, 2, 3, 5, 6, 7, 9 sunt deja folosite în rândul 4, coloana 1 și/sau caseta 4. Singurele valori candidate sunt (4,8) . Uncțiunea „SANITY_CAND” se ocupă de această logică.

Tabelul 5 prezintă candidații, de exemplu, 1 la începutul procesului de căutare. Dacă unei celule i s-a atribuit deja o valoare în condițiile de pornire (Tabelul 1), atunci această valoare este repetată și afișată cu roșu. Dacă o celulă care are nevoie de o valoare are o singură opțiune, aceasta este afișată în alb. Celula (8,7,9) are valoarea unică 7 în alb. (2,5,8,4,3) sunt revendicate de coloana vecină 7. (1, 2, 9) de rândul 8. (3,2,6) de caseta 9. Numai valoarea 7 este nerevendicată.

Verificarea sanității: În căutarea conflictelor

Informațiile care identifică toate opțiunile pentru celulele care au nevoie de o valoare (publicate în Tabelul 4), ne permit să identificăm un conflict înainte de a începe procesul de căutare. Un conflict apare atunci când două celule care au nevoie de o valoare au doar un candidat, valoarea candidată este aceeași și cele două celule sunt vecine. Din tabelul 4 știm că singura celulă care are nevoie de o valoare și are un singur candidat este celula (8,7,9). De exemplu 1, nu există conflict.

Ce ar fi un conflict? Dacă singura valoare posibilă pentru celula (3,7,3) a fost 7 (în loc de 1, 6, 7), atunci există un conflict. Celula (8,7) și celula (3,7) sunt vecine – aceeași coloană. Totuși, dacă singura valoare posibilă pentru celula (4,9,2) ar fi 7 (în loc de 1, 2, 7), acesta nu ar fi un conflict. Aceste celule nu sunt vecine.

Rezumatul verificării sanității

  1. Dacă există duplicate, soluția de pornire nu este fezabilă.
  2. Dacă o celulă care are nevoie de o valoare, nu are candidați, atunci nu există o soluție posibilă pentru acest puzzle. Lista de valori candidate pentru fiecare celulă poate fi utilizată pentru a reduce spațiul de căutare – atât pentru backtracking, cât și pentru optimizare.
  3. Capacitatea de a găsi conflicte identifică puzzle-ul nu este fezabil – nu are soluție – fără un proces de căutare. În plus, identifică „celulele cu probleme”.

O soluție de întoarcere: procesul de căutare

Cu structurile de date de bază și verificarea corectă, ne îndreptăm atenția asupra procesului de căutare. Tema recurentă este, din nou, crearea de structuri de date pentru a sprijini căutarea.

Urmărirea căutării

Matricea Urmăritor ține evidența sarcinilor făcute

  1. Col 1 este contorul
  2. Col 2 este numărul de opțiuni disponibile pentru a fi atribuite acestei celule
    • 1 înseamnă 1 opțiune disponibilă, 2 înseamnă două opțiuni etc.
    • 0 înseamnă – nicio opțiune disponibilă sau resetare la 0 (fără valoare atribuită) și întoarcere
  3. Col 3 este celula căruia i-a fost atribuit un număr de index de valoare (de la 1 la 81)
  4. Col 4 este valoarea atribuită celulei din istoricul urmăririi
    • O valoare de 9999 înseamnă că această celulă a fost atunci când a fost găsită fundătura
    • Valoarea unui număr întreg între 1 și 9 inclusiv este valoarea atribuită acestei celule în acest moment al procesului de căutare.
    • O valoare de 0 înseamnă că această celulă are nevoie de o atribuire

Matricea de urmărire este utilizată pentru a sprijini procesul de căutare. Matricea TRACKHIST are aceeași structură ca și trackerul, dar păstrează istoricul întregului proces de căutare. Tabelul 6 are parte din TRACKHIST de exemplu 1. Este explicat mai detaliat într-o secțiune ulterioară.

În plus, matricea PAV (un vector al unui vector), ține evidența valorilor atribuite anterior acestei celule. Acest lucru asigură că nu revizuim o soluție eșuată – similar cu ceea ce se face în TABU.

Există un proces opțional de jurnal în care procesul de căutare scrie fiecare pas.

Pornirea Căutării

Odată cu evidența contabilă și verificarea corectă, acum putem începe procesul de căutare. Pașii sunt:

  1. Au mai rămas celule care au nevoie de o valoare? – dacă nu, atunci am terminat.
  2. Pentru fiecare celulă care are nevoie de o valoare, găsiți toate opțiunile candidate pentru fiecare celulă. Tabelul 4 are aceste valori la începutul procesului de soluție. La fiecare iterație, aceasta este actualizată pentru a se adapta valorilor atribuite celulelor.
  3. Evaluați opțiunile în această ordine.
    • Dacă o celulă are ZERO opțiuni, atunci instituiți backtracking
    • Găsiți toate celulele cu o singură opțiune, selectați una dintre aceste celule, faceți această atribuire,
      1. și actualizați tabelul de urmărire, soluția actuală și PAV.
    • Dacă toate celulele au mai multe opțiuni, atunci selectați o celulă și o valoare și actualizați
      1. și actualizați tabelul de urmărire, soluția actuală și PAV

Vom folosi Tabelul 6 care face parte din istoricul procesului de soluție (numit TRACKHIST) pentru a ilustra fiecare pas.

În prima iterație (CTR=1), celula 70 (rândul 8, col. 7, caseta 9) este selectată pentru a primi o valoare. Există doar candidatul (7), iar această valoare este atribuită celulei 70. În plus, valoarea 7 este adăugată vectorului de valori atribuite anterior (PAV) pentru celula 70.

În a doua iterație, celulei 30 i se atribuie valoarea 1. Această celulă a avut două valori candidate. Cea mai mică valoare candidată este atribuită celulei (doar o regulă arbitrară pentru a face logica ușor de urmat).

Procesul de identificare a unei celule care are nevoie de o valoare și de atribuire a unei valori funcționează bine până la iterația (CTR) 20. Celula 9 are nevoie de valoare, dar numărul de candidați este ZERO. Există două opțiuni:

  • Nu există o soluție la acest puzzle.
  • Anulăm (dați înapoi) unele dintre sarcini și luăm o altă cale.

Am căutat alocarea celulei cea mai apropiată de aceasta, unde exista mai multe opțiuni. În acest exemplu, acest lucru a avut loc la iterația 18, unde celulei 5 i se atribuie valoarea 3, dar au existat două valori candidate pentru celula 5 - valorile 3 și 8.

Între celula 5 (CTR = 18) și celula 9 (CTR = 20), celulei 8 i se atribuie valoarea 4 (CTR = 19). Repunem celulele 8 și 5 în lista „am nevoie de o valoare”. Aceasta este capturată în a doua și a treia intrare CTR=20, unde valoarea este setată la 0. Valoarea 3 este păstrată în vectorul PAV pentru celula 5. Adică motorul de căutare nu poate atribui valoarea 3 celulei 5.

Motorul de căutare pornește din nou pentru a identifica o valoare pentru celula 5 (cu 3 nu mai este opțiune) și atribuie valoarea 8 celulei 5 (CTR=21). Continuă până când toate celulele au o valoare sau există o celulă fără opțiune și nu există cale de întoarcere. Soluția este afișată în tabelul 7.

Observați, acolo unde există mai mult de un candidat pentru o celulă, aceasta este o șansă de procesare paralelă.

Comparație cu soluția de optimizare MILP

La nivelul suprafeței, reprezentarea puzzle-ului Sudoku este dramatic diferită. Abordarea AI folosește numere întregi și, prin orice măsură, este o reprezentare mai strictă și mai intuitivă. În plus, verificatoarele de sanitate oferă informații utile pentru a face o formulare mai puternică. Reprezentarea MILP este nesfârșită binare (0/1). Cu toate acestea, binarele sunt reprezentări puternice, având în vedere puterea soluțiilor moderne MILP.

Cu toate acestea, pe plan intern, soluția MILP nu păstrează binarele, ci folosește o metodă cu matrice rare pentru a elimina stocarea tuturor zerourilor. În plus, algoritmii de rezolvare a binarelor nu apar decât în ​​anii 1980 și 1990. Lucrarea din 1983 de Crowder, Johnson și Padberg raportează una dintre primele soluții practice de optimizare cu binare. Ei observă importanța preprocesării inteligente și a metodelor de ramificare și legate ca fiind esențiale pentru o soluție de succes.

Explozia recentă a utilizării programării cu constrângeri și a software-ului, cum ar fi rezolvator local a clarificat importanța utilizării metodelor AI cu metodele originale de optimizare, cum ar fi programarea liniară și cele mai mici pătrate.

Timestamp-ul:

Mai mult de la Arkieva