Utvärdering av sudokulösare baserade på mänskliga lösningstekniker

Relevanta dokument
Bakgrund och motivation. Definition av algoritmer Beskrivningssätt Algoritmanalys. Algoritmer. Lars Larsson VT Lars Larsson Algoritmer 1

Sudokulösare: Jämförelse av körtider för backtracking och mänskliga strategier

Kompilering och exekvering. Föreläsning 1 Objektorienterad programmering DD1332. En kompilerbar och körbar java-kod. Kompilering och exekvering

Programmering med Java. Grunderna. Programspråket Java. Programmering med Java. Källkodsexempel. Java API-exempel In- och utmatning.

Tentamen Datastrukturer (DAT036)

Föreläsning 7 Innehåll. Rekursion. Rekursiv problemlösning. Rekursiv problemlösning Mönster för rekursiv algoritm. Rekursion. Rekursivt tänkande:

Lösning av Sudoku med mänskliga strategier

Handledare: Mikael Goldmann

Avbildningar och hashtabeller. Koffman & Wolfgang kapitel 7, mestadels avsnitt 2 4

Föreläsning 9 Innehåll. Söndra och härska. Fibonaccitalen. Söndra och härska. Divide and conquer teknik för att konstruera rekursiva algoritmer.

if (n==null) { return null; } else { return new Node(n.data, copy(n.next));

TDDI16 Datastrukturer och algoritmer. Algoritmanalys

Datastrukturer. föreläsning 3. Stacks 1

Anmälningskod: Lägg uppgifterna i ordning. Skriv uppgiftsnummer (gäller B-delen) och din kod överst i högra hörnet på alla papper

Dugga Datastrukturer (DAT036)

Tentamen Programmeringsteknik II Inledning. Anmälningskod:

Föreläsning 9 Innehåll. Söndra och härska. Fibonaccitalen. Söndra och härska. Divide and conquer teknik för att konstruera rekursiva algoritmer.

Klassen BST som definierar binära sökträd med tal som nycklar och enda data. Varje nyckel är unik dvs förekommer endast en

F4. programmeringsteknik och Matlab

F11 - Rekursion. ID1004 Objektorienterad programmering Fredrik Kilander

Tentamen Programmering fortsättningskurs DIT950

Lösningsförslag till tentamen i EDA011, lördagen den 16 december 2006

Listor. Koffman & Wolfgang kapitel 2, avsnitt , och 2.9

Föreläsning 5 Innehåll

TENTAMEN: Algoritmer och datastrukturer. Läs detta! Uppgifterna är inte avsiktligt ordnade efter svårighetsgrad.

Datastrukturer och algoritmer

Föreläsning 2 Datastrukturer (DAT037)

TENTAMEN I PROGRAMMERING. På tentamen ges graderade betyg:. 3:a 24 poäng, 4:a 36 poäng och 5:a 48 poäng

Samlingar, Gränssitt och Programkonstruktion! Förelasning 11!! TDA540 Objektorienterad Programmering!

Lite om felhantering och Exceptions Mer om variabler och parametrar Fält (eng array) och klassen ArrayList.

public static void mystery(int n) { if (n > 0){ mystery(n-1); System.out.print(n * 4); mystery(n-1); } }

Här ska jag presentera en variant

Tentamen Datastrukturer (DAT037)

Föreläsning 4 Innehåll. Abstrakta datatypen lista. Implementering av listor. Abstrakt datatypen lista. Abstrakt datatyp

Lösningsförslag tentamen FYTA11 Java

Föreläsning 2. Länkad lista och iterator

Tentamen, EDA501/EDAA20 Programmering M MD W BK L

TDDC30. Objektorienterad programmering i Java, datastrukturer och algoritmer. Föreläsning 4 Erik Nilsson, Institutionen för Datavetenskap, LiU

F5 Selektion och iteration. ID1004 Objektorienterad programmering Fredrik Kilander

Algoritmer, datastrukturer och komplexitet

Teoretisk del. Facit Tentamen TDDC (6)

Lösningsförslag. Programmeringsmetodik, KV: Java och OOP. 17 januari 2004

Algoritmer, datastrukturer och komplexitet

Datastrukturer, algoritmer och programkonstruktion (DVA104, VT 2015) Föreläsning 6

Tentamen Datastrukturer, DAT037 (DAT036)

Lösningsförslag till tentamen Datastrukturer, DAT037,

Föreläsning 4. ADT Kö Kö JCF Kö implementerad med en cirkulär array Kö implementerad med en länkad lista

Introduktion till algoritmer - Lektion 1 Matematikgymnasiet, Läsåret Lektion 1

Lösningsförslag till exempeltenta 1

TDDC30 Programmering i Java, Datastrukturer och Algoritmer Lektion 2. Länkade listor Stackar Köer MyList Iteratorer Lab 2 Exceptions Paket

Algoritmer, datastrukturer och komplexitet

Upplägg. Binära träd. Träd. Binära träd. Binära träd. Antal löv på ett träd. Binära träd (9) Binära sökträd (10.1)

Exempel: Förel Rekursion III Nr 14. Uno Holmer, Chalmers,

Tentamen Programmeringsteknik II Skrivtid: Hjälpmedel: Java-bok (vilken som helst) Skriv läsligt! Använd inte rödpenna!

Föreläsning 11: Rekursion

Tentamen TEN1 HI

Objektorienterad programmering i Java, datastrukturer och algoritmer. Föreläsning 4 Jonas Lindgren, Institutionen för Datavetenskap, LiU

Tentamen i Objektorienterad modellering och diskreta strukturer

Algoritmer, datastrukturer och komplexitet

Föreläsning 5 Innehåll. Val av algoritm och datastruktur. Analys av algoritmer. Tidsåtgång och problemets storlek

F6 Objektorienterad design. ID1004 Objektorienterad programmering Fredrik Kilander

Grafer, traversering. Koffman & Wolfgang kapitel 10, avsnitt 4

Föreläsning 7. Träd och binära sökträd

Introduktion till algoritmer - Lektion 4 Matematikgymnasiet, Läsåret Lektion 4

Tentamen. Programmeringsmetodik, KV: Java och OOP. 17 januari 2002

Tentamen Datastrukturer för D2 DAT 035

TDDE10 m.fl. Objektorienterad programmering i Java Föreläsning 6 Erik Nilsson, Institutionen för Datavetenskap, LiU

DAI2 (TIDAL) + I2 (TKIEK)

Föreläsning Datastrukturer (DAT037)

Tentamen för kursen Objektorienterad programvaruutveckling GU (DIT010)

Föreläsning 8 Datastrukturer (DAT037)

Algoritmer, datastrukturer och komplexitet

Exempel på listor (klassen ArrayList). Ett exempel med fält. Avbildning är en speciell typ av lista HashMap.

Övning vecka 6. public void method2() { //code block C method3(); //code block D }//method2

Föreläsning 2 Datastrukturer (DAT037)

En studie om styrkor och svagheter hos sudokulösande algoritmer

F9 - Polymorfism. ID1004 Objektorienterad programmering Fredrik Kilander

Klassdeklaration. Metoddeklaration. Parameteröverföring

Kurskod D0010E Datum Skrivtid 5tim

TDA550 Objektorienterad programvaruutveckling IT, forts. kurs Övning vecka 2

Abstrakt datatyp. -Algoritmer och Datastrukturer- För utveckling av verksamhet, produkter och livskvalitet.

Tentamen ID1004 Objektorienterad programmering May 29, 2012

732G Linköpings universitet 732G11. Johan Jernlås. Översikt. Repetition. Muddy. Funktioner / metoder. Punktnotation. Evalueringsordning

Föreläsning 3. Iteration while-satsen

Examination i. PROGRAMMERINGSTEKNIK F1/TM1 TIN212 (Dugga) Dag: Onsdag Datum: Tid: (OBS 3 tim) Rum: V

Programkonstruktion och Datastrukturer

F12 - Collections. ID1004 Objektorienterad programmering Fredrik Kilander

Föreläsning 13. Rekursion

Imperativ programmering. Föreläsning 4

Föreläsning 4: Kombinatorisk sökning

Föreläsning 9 Datastrukturer (DAT037)

Tentamen Datastrukturer D DAT 035/INN960

Föreläsning 9: Turingmaskiner och oavgörbarhet. Turingmaskinen. Den maximalt förenklade modell för beräkning vi kommer använda är turingmaskinen.

Blockkedjor. en introduktion för datavetare. Rikard Hjort, 24 maj 2019

TDDC30 Programmering i Java, Datastrukturer och Algoritmer Lektion 2. Laboration 2 Datastrukturer En liten uppgift Frågor

Föreläsning 4 Innehåll

Föreläsning 3. Stack

Föreläsning 6: Introduktion av listor

Föreläsning 2. Länkad lista och iterator

Transkript:

Utvärdering av sudokulösare baserade på mänskliga lösningstekniker En jämförelse med Dancing links som referensalgoritm ERICA BRONGE JAKOB SUNDH Kandidatexamensrapport vid CSC, DD143X Handledare: Alexander Kozlov Examinator: Örjan Ekeberg

Referat Syftet med den här rapporten var att undersöka under vilka förutsättningar en regelbaserad algoritm eventuellt skulle kunna vara effektivare för att lösa sudokupussel än Donald Knuths totalsökningsalgoritm Dancing links. Förutsättningarna som testades var pusselstorlek och pusselsvårighetsgrad. Den regelbaserade lösarens regler implementerades utifrån ett antal vanliga lösningstekniker som människor brukar använda sig av och referenslösaren baserades på Knuths egna pseudokod för algoritmen Dancing links. De två lösarna implementerades i Java och deras respektive körtider för varje pussel plus övrig information om körningen och de testade pusslen sparades. Resultatet visade att den regelbaserade lösaren klarade av att lösa de flesta pusslen testade i rimlig tid, och de största pusslen med högre svårighetsgrad snabbare än Dancing links. Slutsatsen som kunde dras var att även om det fanns fall då den regelbaserade lösaren var bättre så var den dels besvärligare att implementera och dels sämre för den vanligaste pusselstorleken 9x9, vilket begränsade dess användningsområden.

Abstract Evaluation of Sudoku Solvers Based on Human Solving Techniques The purpose of this report was to investigate if there existed any particular conditions for which a rule based algorithm could be more effective at the task of solving sudoku puzzles than the total search algorithm Dancing Links by Donald Knuth. The conditions investigated were puzzle difficulty and puzzle size. The rule based solver was implemented with a number of sudoku solving techniques for humans as rules and the reference solver was based on Knuth s own pseudo code for Dancing Links. Both of the solvers were implemented in Java and information about the puzzles tested in addition to the run time for every puzzle was recorded. The result showed that the rule based solver managed to solve most of the tested puzzles in a reasonable amount of time, and the biggest puzzles with higher level of difficulty even faster than Dancing Links. The conclusion was that even though there existed conditions for which the rule based solver was faster, the fact that it was slower for the most common puzzle size of 9x9 and also much more cumbersome to implement limited its usefulness.

Innehåll 1 Introduktion 1 1.1 Syfte och mål............................... 1 1.2 Problemformulering........................... 2 1.3 Viktiga termer.............................. 2 2 Bakgrund 3 2.1 Sudoku.................................. 3 2.2 Undersökta algoritmer.......................... 4 2.2.1 Exakt mängdtäckning och Dancing links............ 4 2.2.2 Mänskliga tekniker för sudokulösning.............. 6 3 Metod 9 3.1 Metodval................................. 9 3.2 Implementation.............................. 9 3.2.1 Dancing links........................... 10 3.2.2 Regelbaserad lösare....................... 10 3.3 Körtidstester............................... 10 3.3.1 Testfall.............................. 11 3.3.2 Testmiljö............................. 12 3.3.3 Utförande............................. 12 4 Resultat 13 4.1 Olösta pussel............................... 13 4.1.1 Regelbaserad lösare....................... 13 4.1.2 Dancing links........................... 13 4.2 Jämförelse av körtid........................... 15 4.3 Körtidsfördelning............................. 17 4.4 Metodanrop för Dancing links...................... 20 5 Analys 21 5.1 Resultatanalys.............................. 21 5.2 Metodanalys............................... 22 6 Slutsats 25

Litteraturförteckning 27 Bilagor 27 A Källkod 29 A.1 DLX.java................................. 29 A.2 HumanSolver.java............................ 32

Kapitel 1 Introduktion De senaste åren har logikpusslet sudoku blivit ett väldigt vanligt förekommande inslag både i dagstidningar och på nätet. Möjligheten att skapa pussel med mycket varierande svårighetsgrad gör pusslet tilltalande för en väldigt bred publik. När människor löser sudokupussel använder de sig oundvikligen av tekniker som baserar sig på de regler som sudoku har 1, oavsett om de är medvetna om det eller ej. Ett exempel skulle kunna vara den här raden innehåller redan symbolen 1, så den kan inte stå i den här cellen. Många sådana tekniker har idag definierats algoritmiskt och givits namn så som X-Wings och det finns otaliga webbsidor [12][1][7] där dessa presenteras för den entusiastiske sudokulösaren. 1.1 Syfte och mål I den här rapporten kommer en implementation av en algoritm som använder sig utav några sådana tekniker utvärderas med målet att se hur den presterar på pussel av olika svårighetsgrad och även storlek. För att det ska gå att utvärdera resultatet kommer även en referensalgoritm implementeras, Dancing links. Den bygger på att se sudoku som en instans till problemet exakt mängdtäckning [2], ett känt NPfullständigt problem, och den kan teoretiskt sett lösa alla pussel, men det är inte säkert att den kan göra det i rimlig tid. Den regelbaserade lösaren å andra sidan sidan är en heuristisk algoritm eftersom den inte säkert kan lösa alla pussel, man kanske kan göra det snabbt. Målet med den här rapporten är därför att ta reda på om det mänskliga angreppssättet med regelbaserade tekniker kan vara bättre än den mer teoretiskt förankrade algoritmen Dancing links. Genom att ta reda på detta så är förhoppningen att det ska kunna ge mer insikt i problematiken kring svåra problem tack vare användandet av ett populärt och lättillgängligt tidsfördriv som exempel. 1 Se avsnitt 2.1 för en genomgång av reglerna. 1

KAPITEL 1. INTRODUKTION 1.2 Problemformulering Mer specifikt ska följande frågor försöka besvaras: Finns det några särskilda förutsättningar, så som storlek eller svårighetsgrad, för vilka den regelbaserade lösaren är snabbare än Dancing links? Löser den regelbaserade lösaren tillräckligt stor andel pussel och dessa tillräckligt snabbt för att vara en bra konkurrent till Dancing links? Hur svårt är det att implementera de olika algoritmerna och hur påverkar det ett eventuellt val mellan dem? 1.3 Viktiga termer cell En ruta i ett pussel. Ett 9x9-pussel består av 81 celler. symbol Det som fylls i i en cell. Vanligtvis siffrorna 1-9 för 9x9-pussel, men för större pussel krävs annan representation. kandidat Siffra som är möjlig att placera i en cell utan att bryta mot någon sudokuregel. box Delmängd av ett sudokupussel. I ett 9x9-pussel är varje box 3x3 celler stor, men storleken på en box varierar med pusselstorleken. 2

Kapitel 2 Bakgrund 2.1 Sudoku Sudoku är ett logikpussel. Den vanligaste varianten består av en kvadrat som innehåller 9x9 små celler. Dessa celler är grupperade i 3x3 stora boxar. Vissa av cellerna har någon av symbolerna 1-9 ifyllda, och målet med pusslet är att fylla i resterande celler så att varje box, rad och kolumn innehåller symbolerna 1-9 exakt en gång. Se figur 2.1 för ett exempel på ett sudokupussel i standardformat. Det är också möjligt att skapa större pussel enligt samma princip. Ett äkta sudoku har endast en möjlig lösning. Figur 2.1. Ett sudokupussel av standardstorlek. Svårighetsgraden för ett sudokupussel bedöms vanligtvis utifrån hur pass avancerade lösningsmetoder som krävs för att pusslet ska vara möjligt att lösa[11]. Antalet från början givna symboler säger således inte nödvändigtvis något om pusslets svårighetsgrad [10], även om det uppenbarligen är lättare att lösa ett pussel där bara 5 celler är tomma istället för 60. 3

KAPITEL 2. BAKGRUND 2.2 Undersökta algoritmer Nedan följer en beskrivning av referensalgoritmen Dancing links och de mänskliga lösningtekniker som den regelbaserade lösaren bygger på. För en mer detaljerad motivering till valet av dessa algoritmer, se avsnitt 3.1. 2.2.1 Exakt mängdtäckning och Dancing links Ett sätt att lösa sudoku är att se det som en instans till problemet exact cover eller exakt mängdtäckning (vår översättning). Givet en samling delmängder S hörandes till en mängd X, går det att välja ut ett antal av dessa delmängder på ett sådant sätt att varje element i X finns med i exakt en delmängd [8]? Problemet som ska lösas är alltså att försöka täcka elementen i en mängd med delmängder så att dessa inte överlappar. All information nedan om Algorithm X och implementationen med Dancing links-tekniken kommer från Knuths ursprungliga artikel [9]. Algorithm X En av de mest kända problemet på är att representera det med hjälp av en matris och sedan använda sig av Donald Knuths Algorithm X. Delmängderna S motsvaras av raderna i matrisen och varje element i mängden X översätts till en kolumn. Om elementet j ingår i delmängden i ges detta av en etta på plats (i, j) i matrisen och om elementet inte finns med i delmängden ges detta av en nolla på samma ställe. Det ursprungliga problemet går nu att formulera på följande sätt: går det att välja ut ett antal rader ur matrisen på ett sådant sätt att det står exakt en etta i varje kolumn? Givet en sådan matris följer sedan Algorithm X den generella arbetsgång som kan ses i algoritm 1. input: Matris A if A är tom then problemet löst; avsluta framgångsrikt else välj en kolumn c (deterministiskt) välj en rad r (icke-deterministiskt) så att A(r,c)= 1 inkludera r i den partiella lösningen for varje kolumn j sådan att A(r,j)= 1 do ta bort kolumn j från A for varje rad i sådan att A(i,j)= 1 do ta bort rad i från A end end anropa algoritmen rekursivt på den reducerade matrisen A end Algorithm 1: Algorithm X 4

2.2. UNDERSÖKTA ALGORITMER De element som finns med i någon delmängd tas alltså bort (borttagandet av kolumnerna) och likaså tas de delmängder som innehåller redan täckta element bort (borttagandet av raderna). Avslutas algoritmen framgångsrikt med en tom matris har alla element täckts in och endast av en delmängd. Svaret på beslutsproblemet är då alltså ja, det går och lösningen ges av de utvalda delmängderna (raderna). Sudoku som exakt mängdtäckning För att kunna utnyttja lösningsmetoden ovan för sudoku krävs först en omformulering av problemet till samma format, det vill säga matrisen som användes i föregående avsnitt. Omformuleringen nedan exemplifieras med ett sudokupussel av storleken 9x9, men resonemanget kan enkelt överföras till pussel av annan storlek. Det hela bygger på att villkoren som en sudokulösning måste uppfylla görs om till kolumner i matrisen. Först och främst krävs att det finns en symbol i var och en av de 81 cellerna, vilket ger de 81 första kolumnerna. Dessa följs av 81 kolumner som motsvarar villkoren att varje rad måste innehålla symbolerna 1-9 exakt en gång, en kolumn för varje möjlig kombination av symbol och rad. På samma sätt får man 81 kolumner för kolumnvillkoren och 81 kolumner för boxvillkoren. Totalt utgörs matrisen alltså av 324 kolumner [2]. En rad i matrisen representerar nu ett sätt att placera ut en symbol i pusslet. Kolumner med ettor i denna rad motsvarar de villkor utplaceringen uppfyller. Utplacering av symbolen 3 i första cellen, längst uppe till vänster i pusslet kommer till exempel ha fyra kolumner med ettor i matrisen. En för att cell 1 är fylld, en för att symbolen 3 finns med i rad 1, en för att symbolen 3 finns med i kolumn 1 och en för att symbolen 3 finns med i box 1. Ett olöst pussel kommer att ha en rad för redan utplacerade symboler, medan tomma celler ger upphov till nio rader; en för varje möjlig symbol i den cellen. Kan man sedan välja ut rader så att det står precis en etta i varje kolumn är alltså alla villkoren för en sudokulösning uppfyllda och de raderna ger då den sökta lösningen [2]. Implementation av Algorithm X med tekniken Dancing links Ett effektivt sätt att implementera Algorithm X är med en teknik som på engelska heter Dancing links [9]. Länkarna är i detta fall de i en dubbellänkad lista. Vill man ta bort ett element x ur en dubbellänkad lista görs detta enkelt genom att sätta L[R[x]] = L[x] och R[L[x]] = R[x], där L[α] och R[α] betecknar elementet före, respektive efter α. Principen bakom Dancing links-tekniken är att man lika enkelt kan återinföra x på samma position i listan genom att igen sätta L[R[x]] = x och R[L[x]] = x. Givet en kolumn, så väljer man i Algorithm X sedan en rad som täcker in den kolumnen och tar bort både rader och kolumner som täcker in samma villkor från matrisen. Med den nya matrisen anropar man sedan algoritmen igen rekursivt. Ska man göra detta med alla möjliga rader som täcker in en viss kolumn och på så sätt bygga upp ett sökträd, måste man efter det rekursiva anropen införa de borttagna 5

KAPITEL 2. BAKGRUND Figur 2.2. Tre element i en dubbellänkad lista. raderna och kolumnerna igen. Det är här tekniken ovan kommer in i bilden. Om ettorna i matrisen representeras som objekt med länkar till föregående och nästa objekt, både radvis och kolumnvis, samt att hela matrisen har huvudelement för varje kolumn kan borttagning- och återinföringssteget göras mycket effektivt, se figur 2.3. När inga huvudelement kvarstår är alla kolumner täckta och en lösning är funnen. Figur 2.3. Borttagning av kolumn A och uppdatering av länkar. Källa: [9] 2.2.2 Mänskliga tekniker för sudokulösning I detta avsnitt presenteras ett antal av de vanligaste teknikerna människor använder sig av för att lösa pussel av standardstorlek. Dessa tekniker går att anpassa för större pussel. Information om samtliga av de dessa förutom En möjlig box kommer från Sudoku of the Day [12] och de svenska benämningarna på dessa olika tekniker är våra egna översättningar. 6

2.2. UNDERSÖKTA ALGORITMER Minnesnoteringar Den här tekniken utgör grunden för att lösa mer avancerade sudokupussel. För varje cell antecknas vilka siffror som är möjliga kandidater för just den cellen. Det gör det väldigt enkelt att se vilka celler som har endast en kandidat, som då direkt kan fyllas i. En möjlig cell Man väljer en rad, kolumn eller 3x3 box och undersöker vilka siffror som saknas. Sedan går man igenom siffrorna en i taget och kollar i hur många av den radens, kolumnens eller boxens tomma celler som det går att placera varje siffra i utan att det blir en konflikt som bryter mot pusslets regler. Om det bara finns en sådan cell så kan man placera siffran där. En möjlig rad eller kolumn Ibland kan det löna sig att försöka utesluta vissa siffror som kandidater för vissa celler. För att göra detta så kan man leta efter boxar där alla möjliga celler för en viss siffra ingår i samma rad eller kolumn. Om de gör det så vet man att för den boxen så måste just den siffran finnas någonstans i den raden eller kolumnen och då kan den siffran strykas som kandidat från alla tomma celler i samma rad eller kolumn i de andra två boxarna. En möjlig box Om samtliga möjliga celler för en siffra i en viss rad eller kolumn finns i endast en av boxarna på den raden/kolumnen så vet man att i den boxen så måste den siffran finnas på just den raden/kolumnen. Man kan därmed stryka den siffran som kandidat för alla celler i de andra två raderna/kolumnerna i den boxen [5]. Nakna par/tripplar Man väljer en rad, kolumn eller box att titta på, och sen letar man efter celler som innehåller samma kandidater. Ett exempel på ett par är om en rad till exempel har två celler som innehåller kandidaterna 3 och 7, då vet man att dessa två kandidater måste placeras i någon av dessa celler och de kan därför strykas som kandidater från alla andra celler i raden. Ett mindre uppenbart exempel på en naken trippel är om du i en rad har tre celler som innehåller kandidaterna {4,5}, {6,4}, {5,6} respektive. Eftersom de tre cellerna tillsammans enbart innehåller de tre kandidaterna 4, 5 och 6 måste de fördelas mellan just de tre cellerna och de kan därmed strykas som kandidater för alla andra celler i raden. 7

KAPITEL 2. BAKGRUND Dolda par, tripplar Precis som i föregående metod så väljer man en rad, kolumn eller box att undersöka. Sedan försöker man finna par eller tripplar av kandidater som endast finns i två eller tre celler. Om man hittar några, så kan man stryka övriga kandidater i dessa celler. Till exempel om man i en rad har två stycken celler med kandidaterna {2,8} och {2,8,9} och 2 och 8 inte finns som kandidater i någon annan cell i den raden så vet man att 9 inte kan vara möjlig som kandidat i den andra cellen. 8

Kapitel 3 Metod 3.1 Metodval Det fanns tre huvudsakliga anledningar till valet av Dancing links som referensalgoritm. För det första är den garanterad att kunna lösa alla pussel, givet tillräckligt med tid. Vid en avvägning mellan körtid och lösbarhet förenklar det resonemanget och gör det lättare att komma fram till en välgrundad slutsats. Jämfört med att ha en annan heuristisk algoritm som referens ger Dancing links även, som nämnts ovan, ett bra tillfälle att analysera de för- och nackdelar som finns hos algoritmen för ett NP-fullständigt problem och ställa dessa mot de som finns hos en heuristisk algoritm för samma problem. För det tredje är tekniken som Dancing links bygger på relativt effektiv jämfört med andra backtrackingtekniker [6]. Det i sin tur ställer högre krav på den regelbaserade lösaren i jämförelsen och ger därmed mer vikt åt eventuella slutsatser i dess fördel. Ett annat övervägande som gjordes var om de mänskliga lösningsteknikerna istället kunde utvärderas bättre med en undersökning där testpersoner fick lösa pussel för hand. Detta alternativ förkastades dock, framförallt på grund av svårigheten med att utforma och genomföra pålitliga tester när människor är inblandade. Jämförelse av två algoritmer implementerade och körda i samma testmiljö ansågs därmed vara ett sätt att öka pålitligheten hos resultatet. 3.2 Implementation För att kunna testa och jämföra de två algoritmerna beslutades att de skulle implementeras i Java. Att valet av programmeringsspråk föll på Java grundade sig mest på vår tidigare erfarenhet av språket. Slutsatsen var därför att det skulle medföra minst problem med arbetet och därmed också mer konsekventa resultat. 9

KAPITEL 3. METOD 3.2.1 Dancing links Implementationen av Dancing links följer nära det upplägg som beskrivs i avsnitt 2.2.1 och eftersom algoritmen finns så tydligt beskriven uppstod inga större svårigheter och andra frågor att ta ställning till vid genomförandet. De enda parametrar som kunde varieras var ordningen som kolumnerna valdes i och den ordning som raderna lades in i matrisen. Baserat på det resonemang som förs av Knuth [9] gjordes valet att i varje rekursivt anrop att välja den kolumn som innehåller minst antal rader för att försöka minimera storleken på det sökträd som uppstår i sökandet efter en lösning. Baserat på hur algoritmen testar att inkludera rader i lösningen gjordes även en annan optimering, specifik för sudokupussel. Som kan ses i algoritm 1 gås raderna i matrisen igenom uppifrån och ned för varje kolumn. En i taget provas de som en del i lösningen. För ett sudokupussel motsvaras en rad av att placera en viss symbol i en viss cell. Den optimering som därför gjordes var att lägga de rader som motsvarar redan ifyllda celler överst i matrisen. Dessa väljs alltså först och eftersom de måste ingå i lösningen innebär det att de första nivåerna i sökträdet aldrig utgörs av felaktiga val som sedan måste göras om, därmed minskas storleken på sökträdet, det totala antalet rekursiva anrop och den totala körtiden. Källkoden för Dancing links-lösaren finns i bilaga A.1. 3.2.2 Regelbaserad lösare Det övergripande tillvägagångssättet som användes för implementationen av den regelbaserade lösaren var att skapa en metod för var och en av de olika teknikerna presenterade i avsnitt 2.2.2. Dessa gavs sedan en ordning baserat på deras koppling till pusslens svårighetsgrad; de tekniker som bara krävs för svårare pussel lades sist och mer använda tekniker först. Algoritmen byggdes sedan med en huvudslinga. Varje varv av slingan består av körning av de olika teknikmetoderna en och en i ordning. Uppdaterar någon av dem pusslet körs slingan från början igen, annars provas nästa metod. Detta görs till dess att pusslet är löst eller ingen av metoderna kan göra några ändringar. På detta sätt körs bara de metoder som verkligen krävs för att lösa pusslet och förhoppningsvis ger det även en effektivare algoritm. Källkoden för den regelbaserade lösaren finns i bilaga A.2. 3.3 Körtidstester Jämförelsen av de två algoritmernas snabbhet gjordes genom att låta båda algoritmerna lösa en uppsättning testpussel och för varje pussel mäta den tid det tog för algoritmen att lösa det. Alla tester utfördes på en bärbar Acer Aspire 3830T med en Intel Core i5-2410m processor (2.3 GHz) och 4 GB minne. Operativsystemet var 64-bitars version av Arch Linux (kernel 3.13.7-1) och som JRE användes OpenJDK 10

3.3. KÖRTIDSTESTER JRE 1.7.0_51. Antalet program som kördes i bakgrunden försökte minimeras, för att på så sätt ge mer konsekventa resultat. Ett speciellt testramverk användes också för att göra testerna och därmed resultaten mer pålitliga, se avsnitt 3.3.2. Dessa åtgärder ansågs göra metoden tillräckligt tillförlitlig för det tänkta syftet. 3.3.1 Testfall För att testa algoritmerna användes en uppsättning av totalt 524 pussel av storlekarna 4x4, 9x9, 16x16, 25x25, 36x36, 49x49 och 64x64. Dessa hämtades från databasen på webbsidan http://www.menneske.no med målet att få 100 stycken av varje storlek och en bra spridning av svårighetsgrader, men för de två största storlekarna fanns dock bara 18 respektive 6 pussel. Alla pussel valdes ut slumpmässigt och ingen hänsyn togs då till fördelningen av svårighetsgrader, vilket bland annat resulterade i att pussel av svårighetsgrad 3 helt saknas. En översikt av testfallen som erhölls kan ses i figur 3.1. Svårighetsgraden på dessa pussel var på förhand bestämda baserat på vilka metoder som skulle krävas för att lösa dem. Ett antagande som gjordes var att alla de hämtade pusslen var korrekta med exakt en giltig lösning. Figur 3.1. Storlek och svårighetsgrad för de pussel som användes vid testerna. 11

KAPITEL 3. METOD 3.3.2 Testmiljö Förutom de allmänna fallgropar som finns vid utvärdering av en algoritms körtid finns det även ytterligare faktorer som man måste ta hänsyn till när man skriver och utför tester i Java. Eftersom javaprogram körs på en virtuell maskin som vid körning på egen hand kan optimera med bland annat JIT-kompilering, där JIT står för Just In Time, måste testerna anpassas efter detta [4]. Då konstruktionen av ett bra testramverk som adresserar alla fallgropar ansågs ligga utom ramen för detta projekt användes istället ett färdigt sådant ramverk utvecklat av Brent Boyer [3]. Dess generella struktur presenteras nedan. Först bestäms den ungefärliga tidsåtgången för en körning av koden som ska testas. Denna används sedan för att göra en uppvärmningskörning i 10 sekunder. På så sätt antas Javas virtuella maskin ha optimerat koden så mycket den kan. Efter det görs de faktiska testerna som rapporteras som resultat. Ett på förhand bestämt antal mätningar görs och sedan beräknas ett medelvärde av dessa. I det här fallet gjordes 20 mätningar. För att minimera tidtagningens inverkan på den uppmätta tiden, består varje mätning av ett upprepat antal exekveringar av koden som ska testas. Detta antal beräknas utifrån den första uppmätta tidsåtgången och det faktum att varje mätning ska pågå i minst en sekund. Tiden för en mätning fås sedan som medelvärdet av de upprepade körningarna. Vidare görs mellan varje mätning också ett försök att tvinga den virtuella maskinen att köra sin skräphanterare, för att undvika att den gör det under mätningens gång. Den enda ändringen som är gjord av detta ramverk är att se om första körningen tar längre tid än 30 sekunder. I det fallet gjordes bedömningen att upprepade tester skulle ta allt för lång tid och detta enda värde är det som rapporterades. Eftersom de flesta variationer är försumbara sett till storleksordningen av den uppmätta tiden så ansågs det räcka. 3.3.3 Utförande Först kördes den regelbaserade lösaren på alla testpussel för att fastställa vilka som den faktiskt kunde lösa. Körtidstester gjordes sedan för båda lösarna på enbart dessa pussel och för varje pussel rapporterades medelvärdet av de 20 mätningarna som resultat. För Dancing links noterades även antalet rekursiva metodanrop som krävdes för att lösa pusslet. När det visade sig att Dancing links tog alldeles för lång tid på sig att lösa vissa av de större pusslen sattes en gräns på 500 000 000 rekursiva metodanrop innan körningen avbröts och inget resultat rapporterades för dessa pussel. 12

Kapitel 4 Resultat 4.1 Olösta pussel De pussel som av olika anledningar inte gick att lösa redovisas i detta avsnitt. Notera att endast pussel som gick att lösa med båda lösarna är inkluderade i körtidsjämförelserna. 4.1.1 Regelbaserad lösare Eftersom den regelbaserade lösaren är en heuristik som består av ett begränsat antal implementerade metoder så var det förväntat att den inte skulle klara av att lösa alla pussel. Totalt klarade lösaren av att lösa 443 av 524 testade pussel. Som man kan se i figur 4.1, så är de olösta pusslen framförallt de pussel med svårighetsgrad högre än 4. Det fanns dock inte så många pussel med svårighetsgrad högre än 5, se figur 3.1. 4.1.2 Dancing links Eftersom Dancing links inte tilläts ta sig obegränsat med tid för att lösa varje pussel så var det vissa som inte kunde lösas. Totalt löstes 418 av 443 testade pussel. 13

KAPITEL 4. RESULTAT Figur 4.1. Regelbaserad lösare: Procent lösta pussel per svårighetsgrad. 14

4.2. JÄMFÖRELSE AV KÖRTID 4.2 Jämförelse av körtid Nedan redovisas jämförelse av körtid mellan den regelbaserade lösaren och dancing links-lösaren, för varje pusselstorlek och svårighetsgrad. Man kan tydligt se i figur 4.2 att Dancing links i medel var snabbare än den regelbaserade lösaren när det gäller svårighetsgrad 0, med undantag för den absolut största storleken. Detsamma gäller för svårighetsgrad 1, se figur 4.3. För svårighetsgrad 1 visar det sig dock att i bästa fall så var den regelbaserade snabbare än Dancing links för pussel av storlek 25x25. När man kommer till svårighetsgrad 2 däremot, figur 4.4, så kan man klart se att för de större storlekarna 25x25 och 36x36 så klarade den regelbaserade lösaren av att lösa dem markant snabbare än Dancing links. Figur 4.2. Svårighetsgrad 0. 15

KAPITEL 4. RESULTAT Figur 4.3. Svårighetsgrad 1. Figur 4.4. Svårighetsgrad 2. 16

4.3. KÖRTIDSFÖRDELNING 4.3 Körtidsfördelning Nedan redovisas körtidsfördelningarna för några olika svårighetsgrader och pusselstorlekar. Körtiden för pussel av svårighetsgrad 0 och storlek 16x16 var ungefär normalfördelad, se figur 4.5 och 4.6. Körtiden som krävdes för den regelbaserade lösaren för att lösa pussel av storlek 25x25 och svårighetsgrad 1 följde däremot inte alls någon normalfördelning. Istället var den indelad i två större toppar, en vid körtid omkring 5 000 µs och en vid körtid omkring 25 000 µs, se figur 4.7. I figur 4.9 så kan man se att antalet rekursiva anrop som krävdes vid lösning med Dancing links alltid var minsta möjliga för svårighetsraderna 0 och 1, det vill säga att de gav upphov till ett rakt sökträd. Figur 4.5. Dancing links: Svårighetsgrad 0, pusselstorlek 16x16. 17

KAPITEL 4. RESULTAT Figur 4.6. Regelbaserad: Svårighetsgrad 0, pusselstorlek 16x16. Figur 4.7. Regelbaserad: Svårighetsgrad 1, pusselstorlek 25x25. 18

4.3. KÖRTIDSFÖRDELNING Figur 4.8. Dancing links: Svårighetsgrad 2, pusselstorlek 36x36. 19

KAPITEL 4. RESULTAT 4.4 Metodanrop för Dancing links Nedan redovisas resultat som påvisar sambandet mellan svårighetsgrad och antal metodanrop som Dancing links använt. Figur 4.9. Dancing links: Antal rekursiva metodanrop per svårighetsgrad och pusselstorlek. 20

Kapitel 5 Analys I detta avsnitt kommer analys och diskussion av den använda metoden och de data som presenterats i föregående avsnitt att genomföras. 5.1 Resultatanalys Som tidigare påpekat så kan man i figur 4.4 se tydligt att för de två större pusselstorlekarna så är den regelbaserade lösaren avsevärt mycket snabbare än Dancing links när det gäller medelvärdet för körtiderna för de pussel som var av svårighetsgrad 2. Att storleken ökar påverkar naturligtvis körtiden för båda algoritmerna eftersom storleken på indata ökar och ingen av algoritmerna har en konstant tidskomplexitet. Eftersom den regelbaserade algoritmen inte har en exponentiell tidskomplexitet medan Dancing links i egenskap av totalsökning för ett NP-fullständigt problem har det, så är det dessutom att förvänta sig att storleken kommer påverka Dancing links mer. Detta går också att se i till exempel figur 4.2 där Dancing links är snabbare för de mindre pusselstorlekarna, men där skillnaden i körtid mellan de båda lösarna minskar för varje storlek tills de i princip är likvärdiga för den största storleken. Eftersom inga pussel av svårighetsgrad 0 gav upphov till några mer komplicerade sökträd än helt raka, se figur 4.9, så ger därför denna graf en väldigt bra bild av hur endast storleken påverkade körtiden för Dancing links. Vidare så är det intressant att se hur pusslets svårighetsgrad också påverkade körtiden för Dancing links mycket mer än för den regelbaserade lösaren. En anledning till att Dancing links var sämre för de svårare pusslen är som nämndes ovan att dessa pussel gav upphov till större sökträd än de lite lättare pusslen, vilket alltså speglas av det kraftigt ökade antalet rekursiva metodanrop som syns i figur 4.9. När pusslen var så pass stora så påverkade dessutom storleken på sökträdet körtiden ännu mer. I figur 4.8 så kan man se hur de flesta pusslen samlas i en topp med kortare körtid, vilket med största sannolikhet motsvarar pussel som inte gav upphov till ett så stort sökträd, medan något enstaka pussel var sådant att det gav upphov till ett mycket större sökträd och därmed också en mycket längre körtid vilket syns i den andra lilla toppen. 21

KAPITEL 5. ANALYS Något som sticker ut en del i figur 4.3 är den stora spridningen i lösningstid för den regelbaserade lösaren för pussel av storlek 25x25. Ser man på dess fördelningsdiagram, figur 4.7, så ser man att det inte är frågan om någon normalfördelning. Orsaken till detta är dock okänd, men en hypotes är att topparna beror på stora skillnader i antalet från början ifyllda rutor i pusslen, vilket är en faktor som ingen data samlats in om under testerna. Värt att nämna är också det faktum att det inte gick lösa alla pussel med Dancing links eftersom vissa tog så otroligt lång tid att det blev nödvändigt att avbryta dem efter ett tag. Det är ett problem eftersom Dancing links teoretiskt sätt ska klara av att lösa alla pussel och detta inte speglas i resultatet, men vi är övertygade om att hade vi haft tid för att låta lösaren stå och lösa pusslen i flera dagar så skulle de förr eller senare bli lösta. Eftersom fokus i undersökningen låg på att testa om den regelbaserade lösaren kunde vara bättre så påverkade inte strykningen av dessa pussel resultatet av körtidsjämförelsen på något oväntat sätt. Ser man till resultaten redovisade i figurerna 4.2, 4.3 och 4.4 så är det i vilket fall som helst uppenbart att för sudokupussel av standardstorleken 9x9 så lönade det sig aldrig bättre att använda den regelbaserade lösaren än att använda Dancing links. 5.2 Metodanalys Ett problem med undersökningen var det ojämna antalet pussel av olika svårighetsgrader och av olika storlek. Till exempel så innehöll undersökningen mindre än 20 pussel var av storlekarna 49x49 och 64x64, vilka dessutom var uppdelade på flera olika storlekar. Därmed så finns risken att resultat beräknade på de storlekarna inte är så representativa utan råkar vara specialfall. För svårighetsgrad högre än 2 så saknar vi nästan resultat helt. Inga pussel med svårighetsgrad 3 fanns överhuvudtaget, och av svårighetsgrad 4 var det endast 10 st som gick att lösa. Fördelningen av svårighetsgrader hade kunnat förbättras om pusslen hade valts ut med det i åtanke istället för att ha slumpats fram. Eftersom de pussel som det för den regelbaserade lösaren tog mer än 30 sekunder att lösa bara löstes en gång istället för 20 gånger som övriga pussel så är säkerheten i tidsmätningen för de pusslen teoretiskt sett mycket sämre än för övriga. Det är svårt att säga något om regelbaserade lösare i allmänhet jämfört med Dancing links eftersom det inte finns någon standardiserad algoritm eller beskrivning av vilka metoder en regelbaserad lösare ska använda. Däremot är undersökningen ett bra exempel på vilka fördelar som kan finnas med att använda heuristiker för att lösa svåra problem med stora probleminstanser. Eftersom den regelbaserade lösaren lyckades lösa majoriteten av alla testade pussel (443 av 524) så var den dessutom en tillräckligt bra heuristik för att enligt oss vara meningsfull i sammanhanget. Med fler implementerade lösningstekniker skulle den dessutom kunna göras ännu bättre och kanske rent utav lösa även de svåraste pusslen. Dessutom går det inte att utesluta att antagandet om testpusslens korrekthet var felaktigt och att 22

5.2. METODANALYS vissa pussel därför inte kunde lösas av den anledningen. Ytterligare något som är värt att nämna är att den regelbaserade lösaren visade sig vara mycket mer omständig att implementera än Dancing links. Det kan vara viktigt när man väljer algoritm att implementera, och speciellt om man planerar att implementera fler tekniker för den regelbaserade lösaren för att tillåta den att klara av svårare pussel. 23

Kapitel 6 Slutsats Den viktigaste slutsatsen man kan dra av resultaten och analysen är att det fanns förutsättningar för vilka den regelbaserade lösaren var effektivare än Dancing links och att den faktiskt hade en tillräckligt bra lösningsfrekvens för att anses vara meningsfull. Förutsättningarna som krävdes var mer specifikt svårare pussel än svårighetsgrad 1 och större pussel än storlek 16x16. Resultatet kan bedömas vara ganska tillförlitligt, men en viss risk för att det är missvisande finns på grund av ett lite väl begränsat antal pussel av vissa storlekar och svårighetsgrader och att alla pussel inte testades mer än en gång var med den regelbaserade lösaren. En annan viktig slutsats är också att det är svårt att säga något om regelbaserade lösare i allmänhet utifrån vårt resultat då det inte finns något entydigt sätt att implementera den typen av lösare. Dessutom kan konstateras att det krävdes betydligt mycket mer arbete för att implementera den regelbaserade lösaren än för Dancing links, trots att ett mycket begränsat antal regler implementerades. Detta i kombination med faktumet att Dancing links endast fick problem med de största storlekarna gör att i de flesta fall så skulle den algoritmen förmodligen vara att föredra för den som intresserar sig för att lösa pussel av den mest tillgängliga standardstorleken 9x9. 25

Litteraturförteckning [1] Simon Armstrong. Sadman software - sudoku solving techniques. http: //www.sadmansoftware.com/sudoku/solvingtechniques.htm. Hämtad 25 april 2014. [2] David Austin. Puzzling over exact cover problems. http://www.ams.org/ samplings/feature-column/fcarc-kanoodle. Hämtad 26 februari 2014. [3] Brent Boyer. Java benchmarking framework. http://www.ellipticgroup. com/html/benchmarkingarticle.html. Hämtad 29 mars 2014. [4] Brent Boyer. Robust java benchmarking. http://www.ibm.com/ developerworks/library/j-benchmark1/, 2008. Hämtad 25 februari 2014. [5] Vegard Hanssen. Menneske sudoku oppgaver. http://www.menneske.no/ sudoku/reducingmethodssb.html. Hämtad 28 mars 2014. [6] Hirosi Hitotumatu and Kohei Noshita. A technique for implementing backtrack algorithms and its application. Information Processing Letters, 8(4):174 175, 1979. [7] Angus Johnson. Solving sudoku. http://angusj.com/sudoku/hints.php. Hämtad 25 april 2014. [8] Richard M. Karp. Reducibility among combinatorial problems. In Complexity of Computer Computations, pages 85 103. Springer US, 1972. [9] Donald E. Knuth. Dancing links. In Millennial Perspectives in Computer Science, pages 187 214. Palgrave, 2000. [10] T. Mantere and J. Koljonen. Solving, rating and generating sudoku puzzles with ga. In Evolutionary Computation, 2007. CEC 2007. IEEE Congress on, pages 1382 1389, Sept 2007. [11] SudokuGrader. Sudoku grading theory. http://www.sudokugrader.com/ category/theory/, 2009. Hämtad 29 april 2014. [12] Howard Tomlinson. Sudoku of the day. http://www.sudokuoftheday.com/. Hämtad 25 februari 2014. 27

Bilaga A Källkod A.1 DLX.java 1 package kexjobb. dlx ; 2 3 import java. u t i l. regex. Matcher ; 4 import java. u t i l. regex. Pattern ; 5 6 import kexjobb. common. Puzzle ; 7 import kexjobb. common. S o l u t i o n ; 8 import kexjobb. common. S o l v e r ; 9 10 public class DLX implements S o l v e r { 11 private ColumnObject r o o t ; 12 private int s i z e ; 13 private boolean s o l v e d ; 14 private DataObject [ ] solutionthingy ; 15 private long s e a r c h C a l l s ; 16 17 public DLX( ) { 18 19 } 20 21 @Override 22 public void loadpuzzle ( Puzzle p ) { 23 ObjectMatrix om = ObjectMatrix. frompuzzle ( p ) ; 24 25 this. r o o t = om. r o o t ; 26 this. s i z e = p. g e t S i z e ( ) ; 27 this. solutionthingy = new DataObject [ s i z e s i z e ] ; 28 this. s o l v e d = f a l s e ; 29 this. s e a r c h C a l l s = 0 ; 30 } 31 32 public long s o l v e ( ) { 33 s o l v e d = f a l s e ; 34 s e a r c h C a l l s = 0 ; 35 29

BILAGA A. KÄLLKOD 36 i f ( s o l v e d ) 37 throw new I l l e g a l S t a t e E x c e p t i o n ( " Already s o l v e d " ) ; 38 39 s e a r c h ( 0 ) ; 40 41 return s e a r c h C a l l s ; 42 } 43 44 public S o l u t i o n g e t S o l u t i o n ( ) { 45 S o l u t i o n s = new S o l u t i o n ( loadedpuzzle. getname ( ), s i z e, loadedpuzzle. g e t D i f f i c u l t y ( ) ) ; 46 Pattern pcell = Pattern. compile ( "R(\\ d+)c(\\ d+) " ) ; 47 Pattern pvalue = Pattern. compile ( " S (\\ d+) " ) ; 48 49 / 50 For each row added to t h e s o l u t i o n, f i n d t h e columns b e l o n g i n g to t h a t row 51 and i n f e r c e l l and symbol from t h e names o f t h e columns. 52 / 53 for ( int k = 0 ; k < solutionthingy. l e n g t h ; k++) { 54 DataObject r = solutionthingy [ k ] ; 55 S t r i n g names = " " ; 56 DataObject o = r ; 57 for ( int i = 0 ; i < 4 ; i ++) { 58 names += o.c.n; 59 o = o.r; 60 } 61 62 Matcher mcell = pcell. matcher ( names ) ; 63 Matcher mvalue = pvalue. matcher ( names ) ; 64 mcell. f i n d ( ) ; 65 mvalue. f i n d ( ) ; 66 67 int row = I n t e g e r. valueof ( mcell. group ( 1 ) ) 1 ; 68 int column = I n t e g e r. valueof ( mcell. group ( 2 ) ) 1 ; 69 int value = I n t e g e r. valueof ( mvalue. group ( 1 ) ) ; 70 71 s. s e t C e l l ( row, column, value ) ; 72 } 73 74 return s ; 75 } 76 77 private void s e a r c h ( int k ) { 78 i f ( r o o t.r == r o o t ) { 79 // A l l columns covered, p u z z l e s o l v e d. 80 s o l v e d = true ; 81 return ; 82 } 83 84 // Count t h e number o f c a l l s to t h i s method as a measure o f p u z z l e c o m p l e x i t y. 85 s e a r c h C a l l s ++; 86 30

A.1. DLX.JAVA 87 // I n f i n i t y p r e v e n t i o n 88 i f ( s e a r c h C a l l s > 500000000) { 89 s e a r c h C a l l s = 1; 90 s o l v e d = true ; 91 return ; 92 } 93 94 // Choose t h e column with f e w e s t o b j e c t s 95 ColumnObject s e l e c t e d C o l = null ; 96 int minsize = I n t e g e r.max_value; 97 for ( ColumnObject c = ( ColumnObject ) r o o t.r; c!= r o o t ; c = ( ColumnObject ) c.r) { 98 i f ( c. S < minsize ) { 99 minsize = c. S ; 100 s e l e c t e d C o l = c ; 101 } 102 } 103 104 covercolumn ( s e l e c t e d C o l ) ; 105 106 for ( DataObject r = s e l e c t e d C o l.d; r!= s e l e c t e d C o l ; r = r.d) { 107 solutionthingy [ k ] = r ; 108 109 for ( DataObject j = r.r; j!= r ; j = j.r) { 110 covercolumn ( j.c) ; 111 } 112 113 s e a r c h ( k+1) ; 114 i f ( s o l v e d ) return ; 115 116 for ( DataObject j = r. L ; j!= r ; j = j. L) { 117 uncovercolumn ( j.c) ; 118 } 119 } 120 121 uncovercolumn ( s e l e c t e d C o l ) ; 122 } 123 124 private void covercolumn ( ColumnObject column ) { 125 column.r. L = column. L ; 126 column. L.R = column.r; 127 128 for ( DataObject i = column.d; i!= column ; i = i.d) { 129 for ( DataObject j = i.r; j!= i ; j = j.r) { 130 j.d.u = j.u; 131 j.u.d = j.d; 132 133 j.c. S ; 134 } 135 } 136 } 137 138 private void uncovercolumn ( ColumnObject column ) { 139 for ( DataObject i = column.u; i!= column ; i = i.u) { 31

BILAGA A. KÄLLKOD 140 for ( DataObject j = i. L ; j!= i ; j = j. L) { 141 j.c. S++; 142 143 j.d.u = j ; 144 j.u.d = j ; 145 } 146 } 147 148 column. R. L = column ; 149 column. L.R = column ; 150 } 151 } A.2 HumanSolver.java 1 package kexjobb. human ; 2 3 import java. u t i l. HashSet ; 4 import java. u t i l. I t e r a t o r ; 5 6 import kexjobb. common. Puzzle ; 7 import kexjobb. common. S o l u t i o n ; 8 import kexjobb. common. S o l v e r ; 9 10 public class HumanSolver implements S o l v e r { 11 private CandidateGrid cg ; 12 private int s i z e ; 13 private int boxsize ; 14 private int [ ] order = { 2, 3, 4, 5, 6 } ; 15 private int methodneeded ; 16 17 public HumanSolver ( ) { 18 19 } 20 21 @Override 22 public void loadpuzzle ( Puzzle p ) { 23 this. loadedpuzzle = p ; 24 cg = CandidateGrid. frompuzzle ( p ) ; 25 26 this. s i z e = p. g e t S i z e ( ) ; 27 this. boxsize = p. getboxsize ( ) ; 28 } 29 30 public void setorder ( int [ ] order ) { 31 this. order = order ; 32 } 33 34 @Override 35 public long s o l v e ( ) { 36 methodneeded = 0 ; 32

A.2. HUMANSOLVER.JAVA 37 38 boolean s o l v e d = f a l s e ; 39 int method = 0 ; 40 41 updatecandidates ( ) ; 42 while (! s o l v e d ) { 43 boolean updated = f a l s e ; 44 45 // Handle with care 46 updated = checksinglecandidate ( ) ; 47 i f ( updated ) { 48 updatecandidates ( ) ; 49 } 50 51 for ( int i : order ) { 52 updated = f a l s e ; 53 54 switch ( i ) { 55 case 2 : 56 updated = c h e c k S i n g l e P o s i t i o n ( ) ; 57 i f ( updated ) updatecandidates ( ) ; 58 break ; 59 case 3 : 60 updated = checkcandidatelines ( ) ; 61 break ; 62 case 4 : 63 updated = checksinglebox ( ) ; 64 break ; 65 case 5 : 66 updated = checknakedpairs ( ) ; 67 break ; 68 case 6 : 69 updated = checkhiddenpairs ( ) ; 70 break ; 71 } 72 73 i f ( updated ) { 74 method = i ; 75 break ; 76 } 77 } 78 79 i f (! updated ) { 80 s o l v e d = cg. i s S o l v e d ( ) ; 81 break ; 82 } 83 84 85 i f ( method > methodneeded ) methodneeded = method ; 86 s o l v e d = cg. i s S o l v e d ( ) ; 87 } 88 89 i f ( s o l v e d ) 90 return methodneeded ; 33

BILAGA A. KÄLLKOD 91 else 92 return 1; 93 } 94 95 @Override 96 public S o l u t i o n g e t S o l u t i o n ( ) { 97 S o l u t i o n s = new S o l u t i o n ( loadedpuzzle. getname ( ), s i z e, loadedpuzzle. g e t D i f f i c u l t y ( ) ) ; 98 99 for ( int i = 0 ; i < s i z e ; i ++) { 100 for ( int j = 0 ; j < s i z e ; j++) { 101 s. s e t C e l l ( i, j, cg. g e t C e l l ( i, j ). g e t S o l u t i o n ( ) ) ; 102 } 103 } 104 105 return s ; 106 } 107 108 private void updatecandidates ( ) { 109 for ( C e l l c : cg ) { 110 i f (! c. i s S o l v e d ( ) ) { 111 I t e r a t o r <I n t e g e r > i t = c. getcandidates ( ). i t e r a t o r ( ) ; 112 while ( i t. hasnext ( ) ) { 113 int candidate = i t. next ( ) ; 114 115 i f (! cg. c a n d i d a t e P o s s i b l e ( c, candidate ) ) { 116 i t. remove ( ) ; 117 } 118 } 119 } 120 } 121 } 122 123 / 124 125 / 126 private boolean checksinglecandidate ( ) { 127 boolean updated = f a l s e ; 128 129 for ( C e l l c e l l : cg ) { 130 i f (! c e l l. i s S o l v e d ( ) && c e l l. getcandidates ( ). s i z e ( ) == 1) { 131 c e l l. s e t S o l v e d ( ) ; 132 cg. c e l l S o l v e d ( ) ; 133 updated = true ; 134 } 135 } 136 137 return updated ; 138 } 139 140 / 141 142 / 143 private boolean c h e c k S i n g l e P o s i t i o n ( ) { 34

A.2. HUMANSOLVER.JAVA 144 boolean updated = f a l s e ; 145 146 for ( int i = 0 ; i < s i z e ; i ++) { 147 C e l l [ ] [ ] s u b s e t s = { cg. getrow ( i ), cg. getcolumn ( i ), cg. getbox ( i ) } ; 148 149 // Try f o r e a c h row, column and box 150 for ( C e l l [ ] s u b s e t : s u b s e t s ) { 151 152 HashSet<I n t e g e r > setcandidates = new HashSet<I n t e g e r >() ; 153 for ( C e l l c : s u b s e t ) { 154 setcandidates. addall ( c. getcandidates ( ) ) ; 155 } 156 157 for ( I n t e g e r n : setcandidates ) { 158 C e l l t a r g e t = null ; 159 160 for ( C e l l c : s u b s e t ) { 161 i f (! c. i s S o l v e d ( ) && c. getcandidates ( ). c o n t a i n s ( n ) ) { 162 i f ( t a r g e t == null ) { // No t a r g e t c e l l found y e t 163 t a r g e t = c ; 164 } 165 else { // Second t a r g e t c e l l found 166 t a r g e t = null ; 167 break ; 168 } 169 } 170 } 171 172 i f ( t a r g e t!= null ) { // E x a c t l y 1 t a r g e t 173 t a r g e t. s e t S o l u t i o n ( n ) ; 174 cg. c e l l S o l v e d ( ) ; 175 updated = true ; 176 removeconflictingcandidates ( t a r g e t ) ; 177 } 178 } 179 } 180 } 181 182 return updated ; 183 } 184 185 / 186 187 / 188 private boolean checkcandidatelines ( ) { 189 boolean updated = f a l s e ; 190 191 for ( int i = 0 ; i < s i z e ; i ++) { 192 C e l l [ ] box = cg. getbox ( i ) ; 193 194 @SuppressWarnings ( " unchecked " ) 195 HashSet<I n t e g e r >[] candidaterows = new HashSet [ s i z e + 1 ] ; 196 @SuppressWarnings ( " unchecked " ) 197 HashSet<I n t e g e r >[] candidatecolumns = new HashSet [ s i z e + 1 ] ; 35

BILAGA A. KÄLLKOD 198 199 for ( C e l l c e l l : box ) { 200 for ( I n t e g e r c : c e l l. getcandidates ( ) ) { 201 i f ( candidaterows [ c ] == null ) candidaterows [ c ] = new HashSet< I n t e g e r >() ; 202 i f ( candidatecolumns [ c ] == null ) candidatecolumns [ c ] = new HashSet<I n t e g e r >() ; 203 204 candidaterows [ c ]. add ( c e l l. getrow ( ) ) ; 205 candidatecolumns [ c ]. add ( c e l l. getcolumn ( ) ) ; 206 } 207 } 208 209 I n t e g e r [ ] subsetindex = new I n t e g e r [ 1 ] ; 210 for ( int candidate = 0 ; candidate < s i z e + 1 ; candidate++) { 211 HashSet<I n t e g e r > rows = candidaterows [ candidate ] ; 212 i f ( rows == null ) continue ; 213 else i f ( rows. s i z e ( ) == 1) { 214 rows. toarray ( subsetindex ) ; 215 216 C e l l [ ] row = cg. getrow ( subsetindex [ 0 ] ) ; 217 for ( C e l l c : row ) { 218 i f ( c. getbox ( boxsize )!= i ) { 219 updated = c. removecandidate ( candidate ) ; 220 } 221 } 222 } 223 } 224 225 for ( int candidate = 0 ; candidate < s i z e + 1 ; candidate++) { 226 HashSet<I n t e g e r > columns = candidatecolumns [ candidate ] ; 227 i f ( columns == null ) continue ; 228 else i f ( columns. s i z e ( ) == 1) { 229 columns. toarray ( subsetindex ) ; 230 231 C e l l [ ] column = cg. getcolumn ( subsetindex [ 0 ] ) ; 232 for ( C e l l c : column ) { 233 i f ( c. getbox ( boxsize )!= i ) { 234 updated = c. removecandidate ( candidate ) ; 235 } 236 } 237 } 238 } 239 } 240 241 return updated ; 242 } 243 244 / 245 246 / 247 private boolean checksinglebox ( ) { 248 boolean updated = f a l s e ; 249 36

A.2. HUMANSOLVER.JAVA 250 for ( int i = 0 ; i < s i z e ; i ++) { 251 C e l l [ ] row = cg. getrow ( i ) ; 252 253 @SuppressWarnings ( " unchecked " ) 254 HashSet<I n t e g e r >[] candidateboxes = new HashSet [ s i z e + 1 ] ; 255 256 for ( C e l l c e l l : row ) { 257 for ( I n t e g e r c : c e l l. getcandidates ( ) ) { 258 i f ( candidateboxes [ c ] == null ) candidateboxes [ c ] = new HashSet<I n t e g e r >() ; 259 260 candidateboxes [ c ]. add ( c e l l. getbox ( boxsize ) ) ; 261 } 262 } 263 264 I n t e g e r [ ] subsetindex = new I n t e g e r [ 1 ] ; 265 for ( int candidate = 0 ; candidate < s i z e + 1 ; candidate++) { 266 HashSet<I n t e g e r > boxes = candidateboxes [ candidate ] ; 267 i f ( boxes == null ) continue ; 268 else i f ( boxes. s i z e ( ) == 1) { 269 boxes. toarray ( subsetindex ) ; 270 271 C e l l [ ] box = cg. getbox ( subsetindex [ 0 ] ) ; 272 for ( C e l l c : box ) { 273 i f ( c. getrow ( )!= i ) { 274 updated = c. removecandidate ( candidate ) ; 275 } 276 } 277 } 278 } 279 } 280 281 for ( int i = 0 ; i < s i z e ; i ++) { 282 C e l l [ ] column = cg. getcolumn ( i ) ; 283 284 @SuppressWarnings ( " unchecked " ) 285 HashSet<I n t e g e r >[] candidateboxes = new HashSet [ s i z e + 1 ] ; 286 287 for ( C e l l c e l l : column ) { 288 for ( I n t e g e r c : c e l l. getcandidates ( ) ) { 289 i f ( candidateboxes [ c ] == null ) candidateboxes [ c ] = new HashSet<I n t e g e r >() ; 290 291 candidateboxes [ c ]. add ( c e l l. getbox ( boxsize ) ) ; 292 } 293 } 294 295 I n t e g e r [ ] subsetindex = new I n t e g e r [ 1 ] ; 296 for ( int candidate = 0 ; candidate < s i z e + 1 ; candidate++) { 297 HashSet<I n t e g e r > boxes = candidateboxes [ candidate ] ; 298 i f ( boxes == null ) continue ; 299 else i f ( boxes. s i z e ( ) == 1) { 300 boxes. toarray ( subsetindex ) ; 301 37

BILAGA A. KÄLLKOD 302 C e l l [ ] box = cg. getbox ( subsetindex [ 0 ] ) ; 303 for ( C e l l c : box ) { 304 i f ( c. getcolumn ( )!= i ) { 305 updated = c. removecandidate ( candidate ) ; 306 } 307 } 308 } 309 } 310 } 311 312 return updated ; 313 } 314 315 / 316 317 / 318 private boolean checknakedpairs ( ) { 319 boolean updated = f a l s e ; 320 321 for ( int i = 0 ; i < s i z e ; i ++) { 322 C e l l [ ] [ ] s u b s e t s = g e t S u b s e t s ( i ) ; 323 324 for ( C e l l [ ] s u b s e t : s u b s e t s ) { 325 326 for ( int c1 = 0 ; c1 < s u b s e t. l e n g t h ; c1++) { 327 HashSet<I n t e g e r > c1candidates = s u b s e t [ c1 ]. getcandidates ( ) ; 328 i f ( c1candidates. s i z e ( )!= 2) continue ; 329 330 for ( int c2 = c1 + 1 ; c2 < s u b s e t. l e n g t h ; c2++) { 331 HashSet<I n t e g e r > c2candidates = s u b s e t [ c2 ]. getcandidates ( ) ; 332 333 i f ( c1candidates. e q u a l s ( c2candidates ) ) { 334 for ( int c = 0 ; c < s u b s e t. l e n g t h ; c++) { 335 i f ( c == c1 c == c2 ) continue ; 336 337 updated = s u b s e t [ c ]. removecandidates ( c2candidates ) ; 338 } 339 } 340 } 341 } 342 } 343 } 344 345 return updated ; 346 } 347 348 / 349 350 / 351 private boolean checkhiddenpairs ( ) { 352 boolean updated = f a l s e ; 353 354 for ( int i = 0 ; i < s i z e ; i ++) { 355 C e l l [ ] [ ] s u b s e t s = g e t S u b s e t s ( i ) ; 38