Algoritme voor sudoku

Sudoku's zijn cijferpuzzels met als belangrijkste kenmerken

* De vakjes (voortaan 'cellen' te noemen) maken elk deel uit van een of
  meer rijen, kolommen en kwadranten (of groepen). In de meest voorkomende 
  vorm, die met 81 vakjes, zijn er 9 rijen (de naast elkaar liggende vakjes)
  9 kolommen (onder elkaar) en 9 groepen van telkens 9 vakjes, in een 
  vierkant: kwadrant.
  In de standaard sudoku maakt elke cel deel uit van een rij, een kolom en 
  een kwadrant. Maar er bestaan varianten waar een cel in meerdere rijen,
  kolommen of kwadranten zit. Standaard worden de cijfers 1--9 gebruikt.
  Maar voor grotere sudoku's worden meer of ook andere letters toegepast,
  bijvoorbeeld voor de hexadoku in het blad Elektor de cijfers 0 tot 9 en
  de letters A tot F. Zelfs tot de Donald Duck schijnt de sudoku al te zijn
  doorgedrongen, maar inplaats van letters of cijfers heb je daar plaatjes.
  Voor het onderliggende programma maakt dat niet uit, in het onderliggende 
  programma worden alleen cijfers gebruikt vanaf 0 tot dimensie-1, dus voor
  een 9X9 is de dimensie 9 en cijfers lopen van 0 tot 8, voor dimensie 16 
  de cijfers 0 .. 15 en voor dimensie 25 de cijfers 0..24.     
* In elke rij ,kolom en kwadrant mag elk van de gebruikte tekens slechts 
  eenmaal voorkomen.
* Er is slechts een oplossing. Als er meerdere zijn, of geen is, klopt de 
  sudoku niet. De oplossing moet uniek zijn.

In een willekeurig vierkant van n X n met (n*n) getallen, (laten we n*n = 'm' noemen) 
waar het niet uitmaakt wat voor getal 1 .. m je in een vakje zet, zijn er m^m 
mogelijkheden. (het teken ^ is bedoeld als: 'verheven tot de macht', bijvoorbeeld 2^3 = 8)
Moet elk vakje een uniek cijfer krijgen dan beperk je het aantal 
permutaties al, in het geval dat n=9 is m=81 en P = 81 X 80 X 79 X ...X 2 X 1,
de wiskundige conventie is dit op te schrijven als 81!, '81 faculteit'.
Als in een vakje niet 1 .. 81 maar 1.. 9 mag staan, dan zijn er 9^81 mogelijkheden.
Wanneer we nu stellen dat in elke rij afzonderlijk, alleen de getallen 1 tot 9
mogen voorkomen, dan is het aantal permutaties in elke rij slechts 9! = 362880.
Met 9 van elkaar onafhankelijke rijen, zijn er 362880^9 combinatiemogelijkheden.
Dit is al een stuk minder dan 9^81, laat staan 81! of zelfs 81^81. Als we nu ook 
nog de beperking toevoegen dat per kolom elk getal 1 .. 9 maar 1X mag voorkomen 
dan worden de combinatiemogelijkheden nog meer beperkt; als het 9X9 vierkant 
wordt opgedeeld in 3X3 vakjes waarin ook elk getal maar 1X voorkomt dan blijven 
slechts weinig combinatiemogelijkheden over. Deze situatie treffen we aan bij de sudoku.
Een sudokuvierkant waarin elk getal 1 .. 9 zonder enige beperking overal mag 
staan, kent 9^81 combinatiemogelijkheden. Om alle informatie over dit vierkant 
te geven moet je alle vakjes een getal geven. Als je stelt dat in elke rij elk 
getal maar een keer mag voorkomen, hoef je per rij het laatste getal niet meegeven 
want dat volgt uit de overige. Hoe meer je de plaatsing van de cijfers beperkt, 
met hoe minder begininformatie te geven je toekunt om het volledige vierkant 
te beschrijven. Hierop berust het principe van sudoku's. Je kunt naast de kwadranten, 
nog extra kwadranten, overlappingen en X figuren en weggelaten hoeken gebruiken 
om nog minder informatie te hoeven meegeven. Een vuistregel die ik heb gevonden is 
dat voor een gemiddeld moeilijke sudoku van m*m, met kwadranten ter grootte van 
n*n (voorbeeld sudoku 9X9 m=9 n=3), er (n-1)^4 gegeven getallen nodig zijn. Dus 
9X9 -->16, 16*16 -->81, 25*25 -->256, 36*36 -->625, 2401-->1296 etcetera. Dit zijn 
richtgetallen, van de meest onderzochte sudoku 9*9 bestaan er op internet lijsten 
met 14 gegeven getallen. Het aantal mogelijke unieke sudoku's (rotaties, spiegelingen,
cijferverwisselingen buiten beschouwing gelaten) is voor de 9*9 standaard sudoku al 
gigantisch, enige miljarden. Dat maakt het oplossen van sudoku's een onbegonnen zaak.
Laat het vooral door de computer doen. 
De enige nuttige toepassing die ik voor sudoku's zie, is encryptie: voor het construeren 
van een nieuwe sudoku is een random getal nodig. Neem voor dat random getal je wachtwoord. 
Ga uit van een standaard sudokupatroon. Wis cijfers door met het random getal 
kandidaten te kiezen die weg kunnen. Dan houd je een patroon over van gewiste cellen en 
cellen met een cijfer erin, en dat '0=gewist 1=blijft' staan patroon kun je vervolgens 
gebruiken om je bericht te versleutelen. 
Een 49X49 sudoku zal een puzzel opleveren met ongeveer even veel gewiste als gegeven 
cellen, maar ook dan nog is het aantal puzzels dat uit een patroon is te maken, gigantisch.
Zoveel dat ze niet zijn voor te berekenen en op te slaan. Ter plekke berekenen uit de 
meest voorkomende wachtwoorden, kost teveel tijd. Vanuit de meest voorkomende wachtwoorden 
voorberekenen en de resultaten opslaan is te doen, maar dat is met bestaande encoderingen
als AES en PGP ook al mogelijk.                          

Bij aanvang van de puzzel wordt slechts een klein deel van de cijfers gegeven. De rest 
moet door logisch redeneren worden ingevuld. Omdat een computer dat redeneren
heel goed kan, is het voordehandliggend om een programma te maken.

Dat kan uiteraard op meerdere manieren. Een mogelijkheid gaat als volgt.

Defineer een array van minimaal 81 X 6.
'81' is voor het aantal elementen (cellen), '6' voor het aantal velden. 
Als er meer cellen zijn, moeten het meer dan 81 zijn, dat spreekt.
De velden zijn als volgt (uiteraard is men hier vrij in; maar om het algoritme 
uit te kunnen leggen moet je bepaalde vooronderstellingen doen)
 - veld 0 bevat volgnummer van het teken, bv. 0..8, of -1 als de inhoud nog 
   onbekend is. 
 - veld 1 een bitfield waarin de voor deze cel mogelijke tekens '1' zijn 
   gesteld, anders '0'. Als de inhoud van de cel reeds bekend is, is slechts 
   het corresponderende bit gezet.
   In het voorbeeld worden deze bitfields geimplementeerd als een double 
   word met de bits als machten van twee.
   Stel de cel heeft waarde 5, dan heeft het double word de waarde 
   2^(n-1)=2^4=16. (-1: de puzzel begint te tellen vanaf 1, de implemetatie vanaf 0)
   Als bv. de waarde 3,7,of 9 kan zijn, is de waarde: 
         2^2+2^6+2^8=4+64+256=324.
 - veld 2 een bitfield waarin een bit is gezet als er sprake is voor deze cel 
   van een 'hidden single'. Anders moet het bitfield de waarde 0 of -1 krijgen.
 - veld 3 bevat het nummer van de rij.
 - veld 4 is het nummer van de kolom
 - veld 5 is het nummer van het kwadrant.
 
Voor de snelheid is het handig om nog voor elke rij,kolom en kwadrant een 
bitfield te hebben waarin de OF functie van alle reeds bekende cellen in die
rij,kolom of kwadrant,respectievelijk. Een gezet bit geeft aan dat het 
bijbehorende getal niet meer beschikbaar is. Dat zijn dus 3 X 9 bitfields.

Het eerste algoritme bekijkt de situatie voor een stap, en maakt onderdeel 
uit van het tweede:
1. Alle rij- kolom- en kwadrant bitfields worden op 0 gezet.
2. Als eerste worden alle cellen bezocht; indien ze bekend zijn wordt opgezocht
wat hun rij,kolom en kwadrant zijn en wordt het bitfield van deze rij,kolom 
en kwadrant ge-ORRed met het bitfield van deze cel.
Voorbeeld: in rij 4 zijn reeds de getallen 1,2,5 en 7 bekend. Het bitfield 
van rij 4 wordt 2^0 +2^1 +2^4 +2^6 =83.
Hierna zijn alle bitfields bekend.

3.Nu worden weer alle cellen bezocht maar alleen bitfields van de onbekende 
cellen ingevuld. In velden 3,4 en 5 staat wat de rij-kolom-en kwadrantnummers
zijn; de bitfields hiervan zeggen welke getallen al vergeven zijn.
Maximaal is te vergeven 2^9-1=511. Van elke onbekende cel wordt het bitfield
in veld 1 dus 

   511 AND NOT(bitfiled_rij OR bitfield_kolom OR bitfield_kwadrant)
Deze waarde wordt in veld 1 ingevuld.
Als er maar 1 bit overblijft, wijst dit bit aan welk cijfer je moet gaan
invullen. Stel het is 32, dan is het in te vullen cijfer :(2log 32)+1=6
Zo'n cel met een enkele mogelijkheid tot invullen heet een 'single'.
De test op het voorkomen van een enkel bit in een (double)word is:
 dword == dword AND (-dword)
Stel dword=128 =%00000000000000000000000010000000
- 128   =       %11111111111111111111111110000000
AND-functie is  %00000000000000000000000010000000     128=128  uniek

Voorbeeld van een bitfield dat geen single bevat:
Stel dword=123  %00000000000000000000000001111011
-123=           %11111111111111111111111110000101
AND-functie     %00000000000000000000000000000001      123 <> 1 niet uniek

De bewerking getal AND(-getal) komt uit het count-trailing-zeroes algoritme
(het is de eerste bewerking die een getal omzet in 2^(trailing_zeroes); in
ctz wordt dit getal vermenigvuldigd en bits 27--31 van het resultaat gebruikt
voor een opzoek in een tabel)
Als dus voor veld 1 geldt, bitfield== bitfield AND-bitfield, kan je opzoeken
welk bit het is (met het trailing-zeroes algoritme bijvoorbeeld) en
dit invullen in veld 0.

Als het bitfield 0 is, zijn er twee mogelijkheden:
Als dit de 81e zet is, zijn alle getallen gevonden
Als het nog niet de 81e zet is, is er ergens eerder een verkeerde waarde 
ingevuld en moest bij die eerdere zet een andere waarde worden ingevuld. Je 
moet alle invullingen vanaf daar tot hier,ongedaan maken en terug naar die
eerdere stap.

Als in het bitfield meer dan 1 gezet bit heeft betekent het dat de cel meerdere
keuzemogelijkheden heeft. Vandaar een tweede test:
4. Je kan nu per rij,kolom en kwadrant kijken, of er wellicht van al de 
onbekende cellen in die rij,kolom of kwadrant, wellicht een bitfield in 
veld 1 staat met een uniek bit. Dat wil zeggen, een bit gezet dat alleen in 
deze cel voorkomt. Omdat elk teken eenmaal in elke rij/kolom/kwadrant moet 
voorkomen, weten we zeker dat het teken corresponderend met dit unieke bit 
in deze cel hoort. Dit wordt een 'hidden single' genoemd.
Hidden singles kan je vinden door het bitfield uit veld 1 van een cel te 
nemen en alle bits eruit weg te laten die ook voorkomen in de bitfields van
cellen in dezelfde rij,zelfde kolom of zelfde kwadrant. Als het resultaat 
dan uit een enkel uniek bit bestaat dan is het een hidden single, en kan de 
bijbehorende waarde worden ingevuld. Als het resultaat 0 is, is er geen 
fout gemaakt.
Een hidden single kan heel goed alleen voorkomen in (bv) de rij, terwijl
de kolom en/of het kwadrant geen hidden singles laten zien. Een hidden single
hoeft dus slechts in een van de 3 dimensies aanwezig zijn.
Voor de hidden singles is het nodig een extra structuur te hebben met per
rij,kolom en kwadrant een linked list met de in die rij/kolom/kwadrant 
aanwezige cellen. Om een bitfield 'af te vinken' zoek je rijnummer op, gaat 
naar de lijst met cellen in die rij en doet 
bitfield[cell]:=bitfield[cell]AND NOT bitfield[rijgenoot van cel]
Test wel of het rij-element ongelijk is aan de cel die je onder handen hebt
(anders wis je alle bits!)

Deze procedure kan je net zolang doorlopen tot alle cellen gevonden zijn;
maar in sommige gevallen kom je dan niet uit omdat op zeker moment er
geen singles of hidden singles te vinden zijn. Op dat moment kan je meer 
geavanceerde tests toepassen (locked candidates, naked pairs,X-wing,XY-wing 
etc) maar het nadeel is dat dit zeer rekenintensief is, en hun voorkomen is
niet gegarandeerd. Dus dan stopt je programma alsnog, en wordt heel traag,

Een alternatief is om 'brute kracht' toe te passen. Je programma moet deze 
methode altijd hebben want (1) vaak zijn puzzels niet op andere manier op te 
lossen of vereisen ze een zeldzame methode die je niet hebt toegevoegd en 
(2) als je methodes toevoegt is het goed om ze te controleren met methodes 
die altijd werken, zoals 'brute force'. En de methode is relatief simpel te 
implementeren, anders dan bv. Xwing of naked multiples etc. Dus begin daarmee.
Vul van alle mogelijkheden een waarde in, en zie of je zo de puzzel op kan lossen. 
Zo niet (omdat je voortijdig cellen tegenkomt met 0 bits gezet in het bitfield), 
ga dan terug naar de laatste cel waar je een keuze maakte, doe die keuze teniet 
en kies de volgende mogelijkheid, probeer daarmee tot het eind te komen.

Hiervoor werd een extra array gemaakt van 81 X 2 elementen: todo[0..80,0..1]
veld0 wijst naar cell
veld1 bevat OF het aantal bits in bitfield veld 1; OF een kopie van bitfield
veld 1. Als voorbeeld wordt de tweede mogelijkheid gebruikt.
Aan het begin worden alle cellen bekeken en als cel onbekend is (ie. niet 
ingevuld) dan wordt celnummer toegevoegd aan de todo lijst. De lijst is korter
dan 81, namelijk 81-aantal_bekende_cellen. Lijstlengte = max_todo.

zet alle onbekende cellen in todo[0..max_todo-1,0] ;zet todo[0..max_todo,1]=0
running=TRUE; pointer=0
 while running
   if (pointer>=max_todo) OR (pointer<0) then
       running=FALSE
         else
   zet alle rij/kolom/kwadrant bitfields 0    ... zie hierboven
   vul mbv bekende cellen, rij/kolom/kwadrant bitfields in
   vul van alle cellen =todo[pointer..max_todo-1,0] veld 1 (bitfield) in
      vul het aantal bits dat is gezet, in in todo[n,1]
   doe van al deze cellen, voorzover bitfield >1 bits gezet, test op 
       hidden single; indien hidden single, vul dan ook '1' in in todo[n,1]
   n:=pointer
   while n<max_todo
   als todo[pointer..max_todo-1,1] is:
       indien 0:  repeat;pointer:=pointer-1
                         zet cel uit todo[pointer,0] veld 0:=-1 d.i.'onbekend'
                     until todo[pointer,1]<>0
                     veld 0 van cel uit todo[pointer,0] wordt waarde volgend                                         uit bitsetting van todo[pointer,1]; kies 
                     bijvoorbeeld het laagste bit en clear dit 
                     bit uit het bitfield, als resultaat dan '0' is 
                     zet dan deze '0' in todo[pointer,1] 
                     anders het geclearde bitfield. 
                     pointer:=pointer+1;n=max_todo \* om loop af te breken
         indien 1:  swap todo[n,0], todo[pointer,0] 
                    veld 0 van cel (= todo[pointer,0]) wordt waarde volgend                                         uit bitsetting van veld 1 of veld 2 .
                    Dus tussen 0 en 8.
                    todo[pointer,1]=0
                    pointer:=pointer+1
      }
     n:=n+1;endwhile 
    indien GEEN 0 of 1 gevonden DAN           \*brute force nodig
      {   n:=pointer;a:=9;i:=pointer
         while n<max_todo  {
          if a> todo[pointer..max_todo,1] a=todo[n,0];i=n
          n:=n+1;endwhile        \* vind cel met minste aantal mogelijke 
                                 invullingen - lukraak de eerste nemen 
                                 werkt ook
            if i<>pointer  swap todo[i,0],todo[pointer,0]
                   veld 0 van cel uit todo[pointer,0] wordt waarde volgend                                         uit bitsetting van veld 1; kies laagste of 
                   hoogste bit en clear dit bit uit het bitfield, 
                   zet dit geclearde bitfield in todo[pointer,1].
                   pointer:=pointer+1;
                            }
      }
      
   endwhile \* while running
        if pointer>= max_todo ...gevonden; 
        if pointer<0 .. geen oplossing gevonden.
 
Verdere implementatie:
  simpel: voor invoer en uitvoer een tekstfile gebruiken met comma-separated 
  cel waarden. Uitvoer in dezelfde vorm. Het is wel handig om voor elke rij 
  een nieuwe regel te gebruiken.
  ingewikkelder:  multitasking window applicatie. Invoer kan gebeuren door 9 
  icons als draggable objecten te ontwerpen; en de sudoku als een window 
  met 81 icons. Opslepen van een van de 9 draggable objecten geeft de 
  81 icons hun waarde. Een extra icon 'Solve' lost de sudoku op; 
  vult de arrays in met de uit de 81 icons af te leiden waarden, enzovoorts. 
  Aan het einde worden de 81 iconen ge-updated met de gevonden waarden.
  nog ingewikkelder: andere sudoku vormen met meer dan 1 rij/kolom/quadrant 
  per cel, diagonaalsudoku,vormsudoku.
  
'count trailing zeroes' algoritme:
zoals boven reeds aangestipt produceert de functie 'getal AND -getal' een 
ander getal dat bestaat uit een enkele 1 en overigens alleen nullen. 
Het getal heeft evenveel trailing nullen (aan de lsb kant) als het 
oorspronkelijke getal. In feite is het getal een macht van twee, 
2^(trailing_zeroes). Maar we willen een simpel nummer hebben om te weten 
hoeveel het aantal trailing zeroes is, geen macht. 
Er bestaan getallen (te vinden door getallen 0 tot 2^32-1 op de eigenschap 
te testen) met de merkwaardigheid dat ze in alle 32 mogelijke shifts 
naar links, in de bits 27--31 telkens een andere bitcombinatie hebben 
staan. Dus zet (2^trailingz.) om in zo'n eindresultaat door deze te 
vermenigvuldigen met dit rare getal, en vervolgens het getal 0..31 in 
de bits 27..31 te gebruiken als index in een opzoektabel.
Voorbeeld: het getal 0x4653ADF  ---   de bijbehorende tabel is:
0,1,2,6,3,11,7,16,4,14,12,21,8,23,19,28,31,5,10,15
0x4653ADF    %00000100011001010011101011011111
left-shifts produceren in bits 27..31 achtereenvolgens
0,1,2,4,8,17,3,6,12,25,18,5,10,20,9,18,7,14,29,26,21,11,22,13,27,23,
15,31,30,28,24,16. Tabel [0] moet 0 geven, Tabel[17] moet 5 geven etc.
Opzoeken in de tabel geeft de reeks 0..31. Voor vermenigvuldiging met het
magische getal moet je het te onderzoeken getal testen of het 0 is, want 
in dat geval is het aantal trailingzeroes =32 (uiteraard). 

a=getal AND -getal
IFa=0 --> result=32 else result=tabel[a>>>27]   >>> :: logical shift right.

Een simpel programmaatje dat alle getallen 0 tot 2^32-1 test, wijst uit dat er 
4096 van deze magische getallen zijn; dus 0x4654ADF is niet uniek. Wel heeft 
ieder van deze getallen zijn eigen opzoektabel. 
Het voorvoegsel 0x geeft een hexadecimale notatie aan. Omdat de meeste mensen
nu eenmaal 10 vingers hebben, is het gebruik ontstaan te tellen met cijfers 0 
tot 9 en zodra de 10-grens wordt overschreden door te tellen met 1, maar die 
1 voor de al bestaande getallen te zetten. De '1' ervoor heeft dus 10 X zoveel 
gewicht als het getal dat volgt. Computers werken met binaire getallen (omdat 
je sommen dan kunt omzetten in logische bewerkingen EXCLUSIEVE_OF en EN, Engels:
 EOR en AND). Die zijn te schrijven met de cijfers 0 en 1 (bijvoorbeeld, 
27 is %11011) alleen krijg je dan zeer lange getallenreeksen. Hexadecimaal, 
rekenen in veelvouden van 16, is een compromis tussen herkenbaar (simpel om 
te zetten in binair) en behapbaar (2^32-1 zijn maar 8 cijfers en letters). 
De 16 gebruikte getallen zijn 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E en F.     




