• No results found

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

N/A
N/A
Protected

Academic year: 2021

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

Copied!
46
0
0

Loading.... (view fulltext now)

Full text

(1)

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

(2)
(3)

Referat

Syftet med den här rapporten var att undersöka un- der vilka förutsättningar en regelbaserad algoritm eventu- ellt skulle kunna vara effektivare för att lösa sudokupus- sel än Donald Knuths totalsökningsalgoritm Dancing links.

Förutsättningarna som testades var pusselstorlek och pus- selsvårighetsgrad. Den regelbaserade lösarens regler imple- menterades utifrån ett antal vanliga lösningstekniker som människor brukar använda sig av och referenslösaren base- rades på Knuths egna pseudokod för algoritmen Dancing links. De två lösarna implementerades i Java och deras re- spektive körtider för varje pussel plus övrig information om körningen och de testade pusslen sparades.

Resultatet visade att den regelbaserade lösaren klara- de av att lösa de flesta pusslen testade i rimlig tid, och de största pusslen med högre svårighetsgrad snabbare än Dan- cing 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.

(4)

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 diffi- culty 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.

(5)

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

(6)

Litteraturförteckning 27

Bilagor 27

A Källkod 29

A.1 DLX.java . . . 29 A.2 HumanSolver.java . . . 32

(7)

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 har1, 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 algo- ritmiskt 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 NP- fullstä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 an- greppssättet med regelbaserade tekniker kan vara bättre än den mer teoretiskt för- ankrade 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.

1Se avsnitt 2.1 för en genomgång av reglerna.

(8)

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 till- rä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.

(9)

Kapitel 2

Bakgrund

2.1 Sudoku

Sudoku är ett logikpussel. Den vanligaste varianten består av en kvadrat som inne- hå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 avance- rade 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.

(10)

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

(11)

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 omfor- mulering 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. Ut- placering 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

(12)

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.

(13)

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.

(14)

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.

(15)

Kapitel 3

Metod

3.1 Metodval

Det fanns tre huvudsakliga anledningar till valet av Dancing links som referensal- goritm. 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årig- heten 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 imple- menteras 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.

(16)

KAPITEL 3. METOD

3.2.1 Dancing links

Implementationen av Dancing links följer nära det upplägg som beskrivs i av- snitt 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 para- metrar 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 regelbase- rade 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 algorit- merna 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

(17)

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 stor- lekarna 4x4, 9x9, 16x16, 25x25, 36x36, 49x49 och 64x64. Dessa hämtades från da- tabasen 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 stor- lekarna 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.

(18)

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ät- ning 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.

(19)

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äm- fö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.

(20)

KAPITEL 4. RESULTAT

Figur 4.1. Regelbaserad lösare: Procent lösta pussel per svårighetsgrad.

(21)

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årig- hetsgrad 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.

(22)

KAPITEL 4. RESULTAT

Figur 4.3.Svårighetsgrad 1.

Figur 4.4.Svårighetsgrad 2.

(23)

4.3. KÖRTIDSFÖRDELNING

4.3 Körtidsfördelning

Nedan redovisas körtidsfördelningarna för några olika svårighetsgrader och pussel- storlekar.

Körtiden för pussel av svårighetsgrad 0 och storlek 16x16 var ungefär normalför- delad, 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.

(24)

KAPITEL 4. RESULTAT

Figur 4.6.Regelbaserad: Svårighetsgrad 0, pusselstorlek 16x16.

Figur 4.7.Regelbaserad: Svårighetsgrad 1, pusselstorlek 25x25.

(25)

4.3. KÖRTIDSFÖRDELNING

Figur 4.8.Dancing links: Svårighetsgrad 2, pusselstorlek 36x36.

(26)

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 pus- selstorlek.

(27)

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 pusselstor- lekarna 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 tidskomplexi- tet. 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 anled- ning 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 än- nu 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.

(28)

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ördelnings- diagram, 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årighets- grader 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 överhuvudta- get, 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 beskriv- ning av vilka metoder en regelbaserad lösare ska använda. Däremot är undersök- ningen 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 samman- hanget. 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

(29)

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.

(30)
(31)

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.

(32)
(33)

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.

(34)
(35)

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 root ;

12 private i n t 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 ) ; 2425 t h i s. root = om. root ;

26 t h i s. s i z e = p . g e t S i z e ( ) ;

27 t h i s. solutionThingy = new DataObject [ s i z e ∗ s i z e ] ; 28 t h i s. s o l v e d = f a l s e ;

29 t h i s. s e a r c h C a l l s = 0 ;

30 }

3132 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

(36)

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 " ) ; 3839 search ( 0 ) ;

40

41 return s e a r c h C a l l s ;

42 }

4344 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 the s o l u t i o n , f i n d the 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 the names o f the columns .

52 ∗/

53 f o r ( int k = 0 ; k < solutionThingy . length ; k++) { 54 DataObject r = solutionThingy [ k ] ;

55 S t r i n g names = " " ;

56 DataObject o = r ;

57 f o r ( int i = 0 ; i < 4 ; i++) {

58 names += o .C.N;

59 o = o .R;

60 }

6162 Matcher mCell = pCell . matcher ( names ) ; 63 Matcher mValue = pValue . matcher ( names ) ; 64 mCell . f i n d ( ) ;

65 mValue . f i n d ( ) ;

6667 i n t row = I n t e g e r . valueOf ( mCell . group ( 1 ) ) − 1 ; 68 i n t column = I n t e g e r . valueOf ( mCell . group ( 2 ) ) − 1 ; 69 i n t value = I n t e g e r . valueOf ( mValue . group ( 1 ) ) ; 7071 s . s e t C e l l ( row , column , value ) ;

72 }

7374 return s ;

75 }

76

77 private void search ( int k ) { 78 i f ( root .R == root ) {

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 the 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

(37)

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 the 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 i n t minSize = I n t e g e r .MAX_VALUE;

97 f o r ( ColumnObject c = ( ColumnObject ) root .R; c != root ; 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 ) ;

105106 f o r ( 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 f o r ( DataObject j = r .R; j != r ; j = j .R) { 110 coverColumn ( j .C) ;

111 }

112

113 search ( k+1) ;

114 i f ( s o l v e d ) return ; 115

116 f o r ( DataObject j = r . L ; j != r ; j = j . L) { 117 uncoverColumn ( j .C) ;

118 }

119 }

120121 uncoverColumn ( s e l e c t e d C o l ) ;

122 }

123124 private void coverColumn ( ColumnObject column ) { 125 column .R. L = column . L ;

126 column . L .R = column .R;

127128 f o r ( DataObject i = column .D; i != column ; i = i .D) { 129 f o r ( DataObject j = i .R; j != i ; j = j .R) {

130 j .D.U = j .U;

131 j .U.D = j .D;

132133 j .C. S−−;

134 }

135 }

136 }

137

138 private void uncoverColumn ( ColumnObject column ) { 139 f o r ( DataObject i = column .U; i != column ; i = i .U) {

(38)

BILAGA A. KÄLLKOD

140 f o r ( DataObject j = i . L ; j != i ; j = j . L) {

141 j .C. S++;

142143 j .D.U = j ;

144 j .U.D = j ;

145 }

146 }

147148 column .R. L = column ; 149 column . L .R = column ;

150 }

151 }

A.2 HumanSolver.java

1 package kexjobb . human ; 23 import java . u t i l . HashSet ; 4 import java . u t i l . I t e r a t o r ; 56 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 ;

10 public class HumanSolver implements S o l v e r {9 11 private CandidateGrid cg ;

12 private i n t s i z e ; 13 private i n t boxSize ;

14 private i n t[ ] order = { 2 , 3 , 4 , 5 , 6 } ; 15 private i n t methodNeeded ;

1617 public HumanSolver ( ) { 1819 }

2021 @Override

22 public void loadPuzzle ( Puzzle p ) { 23 t h i s. loadedPuzzle = p ;

24 cg = CandidateGrid . fromPuzzle ( p ) ; 2526 t h i s. s i z e = p . g e t S i z e ( ) ;

27 t h i s. boxSize = p . getBoxSize ( ) ;

28 }

2930 public void setOrder ( int [ ] order ) { 31 t h i s. order = order ;

32 }

33

34 @Override

35 public long s o l v e ( ) { 36 methodNeeded = 0 ;

(39)

A.2. HUMANSOLVER.JAVA

3738 boolean s o l v e d = f a l s e ; 39 i n t method = 0 ;

4041 updateCandidates ( ) ; 42 while ( ! s o l v e d ) {

43 boolean updated = f a l s e ; 4445 // Handle with care

46 updated = checkSingleCandidate ( ) ; 47 i f ( updated ) {

48 updateCandidates ( ) ;

49 }

50

51 f o r ( int i : order ) { 52 updated = f a l s e ; 5354 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 }

7879 i f ( ! updated ) {

80 s o l v e d = cg . i s S o l v e d ( ) ;

81 break;

82 }

8384

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 ;

(40)

BILAGA A. KÄLLKOD

91 e l s e

92 return −1;

93 }

9495 @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 f o r ( int i = 0 ; i < s i z e ; i++) { 100 f o r ( 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 }

104105 return s ;

106 }

107

108 private void updateCandidates ( ) { 109 f o r ( 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 <Integer > i t = c . getCandidates ( ) . i t e r a t o r ( ) ; 112 while ( i t . hasNext ( ) ) {

113 i n t 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 }

122123 /∗∗

124

125 ∗/

126 private boolean checkSingleCandidate ( ) { 127 boolean updated = f a l s e ;

128

129 f o r ( 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 }

136137 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 ( ) {

(41)

A.2. HUMANSOLVER.JAVA

144 boolean updated = f a l s e ; 145

146 f o r ( 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 f o r ( C e l l [ ] subset : s u b s e t s ) {

151152 HashSet<Integer > setCandidates = new HashSet<Integer >() ; 153 f o r ( C e l l c : subset ) {

154 setCandidates . addAll ( c . getCandidates ( ) ) ;

155 }

156157 f o r ( I n t e g e r n : setCandidates ) { 158 C e l l t a r g e t = null ;

159

160 f o r ( C e l l c : subset ) {

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 e l s e { // 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 }

171172 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 }

181182 return updated ;

183 }

184

185 /∗∗

186

187 ∗/

188 private boolean checkCandidateLines ( ) { 189 boolean updated = f a l s e ;

190191 f o r ( int i = 0 ; i < s i z e ; i++) { 192 C e l l [ ] box = cg . getBox ( i ) ; 193

194 @SuppressWarnings ( " unchecked " )

195 HashSet<Integer >[] candidateRows = new HashSet [ s i z e + 1 ] ; 196 @SuppressWarnings ( " unchecked " )

197 HashSet<Integer >[] candidateColumns = new HashSet [ s i z e + 1 ] ;

(42)

BILAGA A. KÄLLKOD

198199 f o r ( C e l l c e l l : box ) {

200 f o r ( I n t e g e r c : c e l l . getCandidates ( ) ) {

201 i f ( candidateRows [ c ] == null ) candidateRows [ c ] = new HashSet<

Integer >() ;

202 i f ( candidateColumns [ c ] == null ) candidateColumns [ c ] = new HashSet<Integer >() ;

203204 candidateRows [ c ] . add ( c e l l . getRow ( ) ) ; 205 candidateColumns [ c ] . add ( c e l l . getColumn ( ) ) ;

206 }

207 }

208209 I n t e g e r [ ] subsetIndex = new I n t e g e r [ 1 ] ;

210 f o r ( int candidate = 0 ; candidate < s i z e + 1 ; candidate++) { 211 HashSet<Integer > rows = candidateRows [ candidate ] ;

212 i f ( rows == null ) continue ; 213 e l s e i f ( rows . s i z e ( ) == 1) { 214 rows . toArray ( subsetIndex ) ;

215216 C e l l [ ] row = cg . getRow ( subsetIndex [ 0 ] ) ; 217 f o r ( C e l l c : row ) {

218 i f ( c . getBox ( boxSize ) != i ) {

219 updated |= c . removeCandidate ( candidate ) ;

220 }

221 }

222 }

223 }

224225 f o r ( int candidate = 0 ; candidate < s i z e + 1 ; candidate++) { 226 HashSet<Integer > columns = candidateColumns [ candidate ] ; 227 i f ( columns == null ) continue ;

228 e l s e i f ( columns . s i z e ( ) == 1) { 229 columns . toArray ( subsetIndex ) ;

230231 C e l l [ ] column = cg . getColumn ( subsetIndex [ 0 ] ) ; 232 f o r ( C e l l c : column ) {

233 i f ( c . getBox ( boxSize ) != i ) {

234 updated |= c . removeCandidate ( candidate ) ;

235 }

236 }

237 }

238 }

239 }

240241 return updated ;

242 }

243

244 /∗∗

245

246 ∗/

247 private boolean checkSingleBox ( ) { 248 boolean updated = f a l s e ;

249

(43)

A.2. HUMANSOLVER.JAVA

250 f o r ( int i = 0 ; i < s i z e ; i++) { 251 C e l l [ ] row = cg . getRow ( i ) ; 252253 @SuppressWarnings ( " unchecked " )

254 HashSet<Integer >[] candidateBoxes = new HashSet [ s i z e + 1 ] ; 255256 f o r ( C e l l c e l l : row ) {

257 f o r ( I n t e g e r c : c e l l . getCandidates ( ) ) {

258 i f ( candidateBoxes [ c ] == null ) candidateBoxes [ c ] = new HashSet<Integer >() ;

259

260 candidateBoxes [ c ] . add ( c e l l . getBox ( boxSize ) ) ;

261 }

262 }

263264 I n t e g e r [ ] subsetIndex = new I n t e g e r [ 1 ] ;

265 f o r ( int candidate = 0 ; candidate < s i z e + 1 ; candidate++) { 266 HashSet<Integer > boxes = candidateBoxes [ candidate ] ;

267 i f ( boxes == null ) continue ; 268 e l s e i f ( boxes . s i z e ( ) == 1) { 269 boxes . toArray ( subsetIndex ) ;

270271 C e l l [ ] box = cg . getBox ( subsetIndex [ 0 ] ) ; 272 f o r ( C e l l c : box ) {

273 i f ( c . getRow ( ) != i ) {

274 updated |= c . removeCandidate ( candidate ) ;

275 }

276 }

277 }

278 }

279 }

280

281 f o r ( int i = 0 ; i < s i z e ; i++) { 282 C e l l [ ] column = cg . getColumn ( i ) ; 283284 @SuppressWarnings ( " unchecked " )

285 HashSet<Integer >[] candidateBoxes = new HashSet [ s i z e + 1 ] ; 286287 f o r ( C e l l c e l l : column ) {

288 f o r ( I n t e g e r c : c e l l . getCandidates ( ) ) {

289 i f ( candidateBoxes [ c ] == null ) candidateBoxes [ c ] = new HashSet<Integer >() ;

290

291 candidateBoxes [ c ] . add ( c e l l . getBox ( boxSize ) ) ;

292 }

293 }

294295 I n t e g e r [ ] subsetIndex = new I n t e g e r [ 1 ] ;

296 f o r ( int candidate = 0 ; candidate < s i z e + 1 ; candidate++) { 297 HashSet<Integer > boxes = candidateBoxes [ candidate ] ;

298 i f ( boxes == null ) continue ; 299 e l s e i f ( boxes . s i z e ( ) == 1) { 300 boxes . toArray ( subsetIndex ) ; 301

References

Related documents

ida_itemname plottime ida_username. ida_itemname

Horisontal skala 1 : 1000 DATUM RITNINGSNUMMER FÖRVALTNING BLAD NÄSTA BLAD ÄNDR.

Horisontal skala 1 : 1000 DATUM RITNINGSNUMMER FÖRVALTNING BLAD NÄSTA BLAD ÄNDR.

Horisontal skala 1 : 1000 DATUM RITNINGSNUMMER FÖRVALTNING BLAD NÄSTA BLAD ÄNDR.

ida_itemname plottime ida_username. ida_itemname

ida_itemname plottime ida_username. ida_itemname

Ny bebyggelse är välkommen i bygden, men bör i huvudsak ske i anslutning till nuvarande bebyggelse, detta för att hålla samman byarna och samtidigt utnyttja den tekniska

Hodnocení celkového vzhledu oděvních textilií je poměrně složitá metodika. Zasahuje do ní spousta faktoru a některé z nich jsou subjektivní záležitostí, kterou není