Add files via upload
This commit is contained in:
1 parent
a0a7574365
commit
028aef4070
4 files changed
+49476
-49462
No files matched your search
Binary file not shown.
+72
-53
@@ -1,6 +1,14 @@
|
|||||||
import java.io.*;
|
import java.io.BufferedReader;
|
||||||
import java.util.*;
|
import java.io.IOException;
|
||||||
import java.util.concurrent.*;
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
@@ -25,7 +33,7 @@ import java.util.stream.IntStream;
|
|||||||
** That makes us more likely to fail early if we guess wrong.
|
** That makes us more likely to fail early if we guess wrong.
|
||||||
** - To be able to back up, we need to keep the grid from the previous recursive level.
|
** - To be able to back up, we need to keep the grid from the previous recursive level.
|
||||||
** To save garbage collection, we pre-allocate one grid per level (81 levels) in a `gridpool`.
|
** To save garbage collection, we pre-allocate one grid per level (81 levels) in a `gridpool`.
|
||||||
** - We can work in parallel in multiple threads, each with its own grodpool.
|
** - We can work in parallel in multiple threads, each with its own gridpool.
|
||||||
**
|
**
|
||||||
** Do constraint propagation with `arcConsistent`, `dualConsistent`, and `nakedPairs`.
|
** Do constraint propagation with `arcConsistent`, `dualConsistent`, and `nakedPairs`.
|
||||||
**/
|
**/
|
||||||
@@ -62,6 +70,7 @@ public class Sudoku {
|
|||||||
|
|
||||||
private volatile boolean headerPrinted = false;
|
private volatile boolean headerPrinted = false;
|
||||||
private static final int WORK_BLOCK_SIZE = 16;
|
private static final int WORK_BLOCK_SIZE = 16;
|
||||||
|
private static final int BOX_SIZE = 3;
|
||||||
|
|
||||||
/** Mutable state owned by exactly one solver thread. **/
|
/** Mutable state owned by exactly one solver thread. **/
|
||||||
static final class WorkerState {
|
static final class WorkerState {
|
||||||
@@ -77,7 +86,7 @@ public class Sudoku {
|
|||||||
|
|
||||||
// Warm up the JIT code cache by solving a puzzle a few times
|
// Warm up the JIT code cache by solving a puzzle a few times
|
||||||
String puzzle = "........8..3...4...9..2..6.....79.......612...6.5.2.7...8...5...1.....2.4.5.....3";
|
String puzzle = "........8..3...4...9..2..6.....79.......612...6.5.2.7...8...5...1.....2.4.5.....3";
|
||||||
s.solveListWithWorkers(Collections.nCopies(1000, s.parseGrid(puzzle)), 1);
|
s.solveListWithWorkers(Collections.nCopies(9999, s.parseGrid(puzzle)), 1);
|
||||||
|
|
||||||
// Process the options and filenames
|
// Process the options and filenames
|
||||||
for (String arg : args) {
|
for (String arg : args) {
|
||||||
@@ -116,7 +125,7 @@ public class Sudoku {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Use nThreads workers to pull puzzle rids off the list and solve them.
|
/** Use nThreads workers to pull puzzle grids off the list and solve them.
|
||||||
Keep track of the number of backtracks each worker does and return the sum. **/
|
Keep track of the number of backtracks each worker does and return the sum. **/
|
||||||
long solveListWithWorkers(List<int[]> grids, int nThreads) {
|
long solveListWithWorkers(List<int[]> grids, int nThreads) {
|
||||||
int nGrids = grids.size();
|
int nGrids = grids.size();
|
||||||
@@ -130,9 +139,15 @@ public class Sudoku {
|
|||||||
}
|
}
|
||||||
return worker.backtracks;
|
return worker.backtracks;
|
||||||
}).toList();
|
}).toList();
|
||||||
return pool.invokeAll(tasks).stream().mapToLong(f -> {
|
long totalBacktracks = 0;
|
||||||
try { return f.get(); } catch (Exception e) { throw new RuntimeException(e); }
|
for (var future : pool.invokeAll(tasks)) {
|
||||||
}).sum();
|
try {
|
||||||
|
totalBacktracks += future.get();
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
throw new RuntimeException("Solver worker failed", e.getCause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return totalBacktracks;
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
System.err.println("Solver thread was interrupted.");
|
System.err.println("Solver thread was interrupted.");
|
||||||
@@ -244,12 +259,12 @@ public class Sudoku {
|
|||||||
** and try--that is, search recursively--every possible digit for the square. **/
|
** and try--that is, search recursively--every possible digit for the square. **/
|
||||||
int[] search(int[] grid, WorkerState worker, int level) {
|
int[] search(int[] grid, WorkerState worker, int level) {
|
||||||
if (grid == null) return null;
|
if (grid == null) return null;
|
||||||
int s = select_square(grid);
|
int square = selectSquare(grid);
|
||||||
if (s == -1) return grid; // All squares filled — puzzle is solved.
|
if (square == -1) return grid; // All squares filled — puzzle is solved.
|
||||||
for (int candidates = grid[s]; candidates != 0; candidates &= candidates - 1) {
|
for (int candidates = grid[square]; candidates != 0; candidates &= candidates - 1) {
|
||||||
int d = candidates & -candidates; // lowest set bit
|
int digitMask = candidates & -candidates; // lowest set bit
|
||||||
System.arraycopy(grid, 0, worker.gridpool[level], 0, grid.length);
|
System.arraycopy(grid, 0, worker.gridpool[level], 0, grid.length);
|
||||||
var result = search(fill(worker.gridpool[level], s, d), worker, level + 1);
|
var result = search(fill(worker.gridpool[level], square, digitMask), worker, level + 1);
|
||||||
if (result != null) return result;
|
if (result != null) return result;
|
||||||
++worker.backtracks;
|
++worker.backtracks;
|
||||||
}
|
}
|
||||||
@@ -277,7 +292,7 @@ public class Sudoku {
|
|||||||
|
|
||||||
/** Choose the unfilled square with the fewest possible values.
|
/** Choose the unfilled square with the fewest possible values.
|
||||||
** Return -1 if all squares are filled (puzzle complete). **/
|
** Return -1 if all squares are filled (puzzle complete). **/
|
||||||
int select_square(int[] grid) {
|
int selectSquare(int[] grid) {
|
||||||
int square = -1;
|
int square = -1;
|
||||||
int min = N + 1;
|
int min = N + 1;
|
||||||
for (int s : SQUARES) {
|
for (int s : SQUARES) {
|
||||||
@@ -293,11 +308,11 @@ public class Sudoku {
|
|||||||
|
|
||||||
|
|
||||||
/** Fill grid[s] = d. Return grid, or return null if this creates a contradiction. **/
|
/** Fill grid[s] = d. Return grid, or return null if this creates a contradiction. **/
|
||||||
int[] fill(int[] grid, int s, int d) {
|
int[] fill(int[] grid, int square, int digitMask) {
|
||||||
if (grid == null || (grid[s] & d) == 0) return null;
|
if (grid == null || (grid[square] & digitMask) == 0) return null;
|
||||||
grid[s] = d;
|
grid[square] = digitMask;
|
||||||
for (int p : PEERS[s]) {
|
for (int peer : PEERS[square]) {
|
||||||
if (!eliminate(grid, p, d)) return null;
|
if (!eliminate(grid, peer, digitMask)) return null;
|
||||||
}
|
}
|
||||||
return grid;
|
return grid;
|
||||||
}
|
}
|
||||||
@@ -306,10 +321,12 @@ public class Sudoku {
|
|||||||
/** Eliminate digit d as a possibility for grid[s].
|
/** Eliminate digit d as a possibility for grid[s].
|
||||||
** Run all three constraint-propagation routines.
|
** Run all three constraint-propagation routines.
|
||||||
** Return false if a contradiction is detected. **/
|
** Return false if a contradiction is detected. **/
|
||||||
boolean eliminate(int[] grid, int s, int d) {
|
boolean eliminate(int[] grid, int square, int digitMask) {
|
||||||
if ((grid[s] & d) == 0) return true; // Already eliminated
|
if ((grid[square] & digitMask) == 0) return true; // Already eliminated
|
||||||
grid[s] -= d;
|
grid[square] &= ~digitMask;
|
||||||
return arc_consistent(grid, s) && dual_consistent(grid, s, d) && naked_pairs(grid, s);
|
return arcConsistent(grid, square)
|
||||||
|
&& dualConsistent(grid, square, digitMask)
|
||||||
|
&& nakedPairs(grid, square);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -317,25 +334,26 @@ public class Sudoku {
|
|||||||
|
|
||||||
/** Check arc consistency: either s has multiple possibilities, or its single
|
/** Check arc consistency: either s has multiple possibilities, or its single
|
||||||
** remaining value can be filled without contradiction. **/
|
** remaining value can be filled without contradiction. **/
|
||||||
boolean arc_consistent(int[] grid, int s) {
|
boolean arcConsistent(int[] grid, int square) {
|
||||||
int count = NUM_DIGITS[grid[s]];
|
int count = NUM_DIGITS[grid[square]];
|
||||||
return count >= 2 || (count == 1 && fill(grid, s, grid[s]) != null);
|
return count >= 2 || (count == 1 && fill(grid, square, grid[square]) != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** After eliminating d from grid[s], ensure d still has at least one valid
|
/** After eliminating d from grid[s], ensure d still has at least one valid
|
||||||
** position in each of s's units. If exactly one remains, fill it. **/
|
** position in each of s's units. If exactly one remains, fill it. **/
|
||||||
boolean dual_consistent(int[] grid, int s, int d) {
|
boolean dualConsistent(int[] grid, int square, int digitMask) {
|
||||||
for (int[] u : UNITS[s]) {
|
for (int[] unit : UNITS[square]) {
|
||||||
int dPlaces = 0;
|
int possiblePlaces = 0;
|
||||||
int dPlace = -1;
|
int onlyPlace = -1;
|
||||||
for (int s2 : u) {
|
for (int otherSquare : unit) {
|
||||||
if ((grid[s2] & d) > 0) {
|
if ((grid[otherSquare] & digitMask) != 0) {
|
||||||
if (++dPlaces > 1) break;
|
if (++possiblePlaces > 1) break;
|
||||||
dPlace = s2;
|
onlyPlace = otherSquare;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (dPlaces == 0 || (dPlaces == 1 && fill(grid, dPlace, d) == null)) {
|
if (possiblePlaces == 0
|
||||||
|
|| (possiblePlaces == 1 && fill(grid, onlyPlace, digitMask) == null)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -345,18 +363,19 @@ public class Sudoku {
|
|||||||
|
|
||||||
/** If two squares in a unit share exactly the same two possible values, eliminate
|
/** If two squares in a unit share exactly the same two possible values, eliminate
|
||||||
** those values from every other square in that unit. **/
|
** those values from every other square in that unit. **/
|
||||||
boolean naked_pairs(int[] grid, int s) {
|
boolean nakedPairs(int[] grid, int square) {
|
||||||
if (!runNakedPairs || NUM_DIGITS[grid[s]] != 2) return true;
|
if (!runNakedPairs || NUM_DIGITS[grid[square]] != 2) return true;
|
||||||
int val = grid[s];
|
int pairMask = grid[square];
|
||||||
for (int s2 : PEERS[s]) {
|
for (int peer : PEERS[square]) {
|
||||||
if (grid[s2] == val) {
|
if (grid[peer] == pairMask) {
|
||||||
for (int[] u : UNITS[s]) {
|
for (int[] unit : UNITS[square]) {
|
||||||
if (member(s2, u)) {
|
if (member(peer, unit)) {
|
||||||
int d = HIGHEST_DIGIT[val];
|
int firstDigitMask = HIGHEST_DIGIT[pairMask];
|
||||||
int d2 = val - d;
|
int secondDigitMask = pairMask & ~firstDigitMask;
|
||||||
for (int s3 : u) {
|
for (int otherSquare : unit) {
|
||||||
if (s3 != s && s3 != s2) {
|
if (otherSquare != square && otherSquare != peer) {
|
||||||
if (!eliminate(grid, s3, d) || !eliminate(grid, s3, d2)) {
|
if (!eliminate(grid, otherSquare, firstDigitMask)
|
||||||
|
|| !eliminate(grid, otherSquare, secondDigitMask)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -374,7 +393,7 @@ public class Sudoku {
|
|||||||
/** Read one puzzle per line from filename and return a list of puzzle grids.
|
/** Read one puzzle per line from filename and return a list of puzzle grids.
|
||||||
** if -reverse option is true, also include the reversed puzzle. **/
|
** if -reverse option is true, also include the reversed puzzle. **/
|
||||||
List<int[]> readPuzzlesFromFile(String filename) throws IOException {
|
List<int[]> readPuzzlesFromFile(String filename) throws IOException {
|
||||||
try (var in = new BufferedReader(new FileReader(filename))) {
|
try (BufferedReader in = Files.newBufferedReader(Path.of(filename))) {
|
||||||
var grids = new ArrayList<int[]>(400);
|
var grids = new ArrayList<int[]>(400);
|
||||||
String gridstring;
|
String gridstring;
|
||||||
while ((gridstring = in.readLine()) != null) {
|
while ((gridstring = in.readLine()) != null) {
|
||||||
@@ -455,11 +474,11 @@ public class Sudoku {
|
|||||||
/** Return a String representing one row of the grid. **/
|
/** Return a String representing one row of the grid. **/
|
||||||
String rowString(int[] grid, int r) {
|
String rowString(int[] grid, int r) {
|
||||||
var row = new StringBuilder(30);
|
var row = new StringBuilder(30);
|
||||||
for (int s = r * 9; s < (r + 1) * 9; ++s) {
|
for (int s = r * N; s < (r + 1) * N; ++s) {
|
||||||
int nd = NUM_DIGITS[grid[s]];
|
int candidateCount = NUM_DIGITS[grid[s]];
|
||||||
char cell = nd == 9 ? '.' : nd != 1 ? '?' : (char)('1' + Integer.numberOfTrailingZeros(grid[s]));
|
char cell = candidateCount == N ? '.' : candidateCount != 1 ? '?' : (char)('1' + Integer.numberOfTrailingZeros(grid[s]));
|
||||||
row.append(cell);
|
row.append(cell);
|
||||||
row.append(s % 9 == 2 || s % 9 == 5 ? " | " : " ");
|
row.append(s % N == BOX_SIZE - 1 || s % N == 2 * BOX_SIZE - 1 ? " | " : " ");
|
||||||
}
|
}
|
||||||
return row.toString();
|
return row.toString();
|
||||||
}
|
}
|
||||||
|
|||||||
+45
-50
@@ -15,12 +15,12 @@
|
|||||||
"|||\n",
|
"|||\n",
|
||||||
"\n",
|
"\n",
|
||||||
"\n",
|
"\n",
|
||||||
"In 2006, I wrote a [Python program](http://norvig.com/sudoku.html) to solve Sudoku. Soon after that, [Peter Seibel](https://gigamonkeys.com/) invited me to publish an article in [*Code Quarterly*](https://gigamonkeys.com/code-quarterly/) magazine which would go into more detail about backtracking search and constraint propagation in general, and would feature a more efficient program. Unfortunately, the magazine [folded](https://gigamonkeys.wordpress.com/2011/10/17/end-of-the-line-for-code-quarterly/) before I could finish the article. In 2021 I [updated the Python Sudoku program](Sudoku.ipynb) to Jupyter notebook form, with modern coding idioms, and now I do the same for the [more efficient Java Sudoku program](Sudoku.java). \n",
|
"In 2006, I wrote a [Python program](http://norvig.com/sudoku.html) to solve Sudoku. Soon after that, [Peter Seibel](https://gigamonkeys.com/) invited me to publish an article in [*Code Quarterly*](https://gigamonkeys.com/code-quarterly/) magazine which would go into more detail about backtracking search and constraint propagation in general, and would feature a more efficient program. Unfortunately, the magazine [folded](https://gigamonkeys.wordpress.com/2011/10/17/end-of-the-line-for-code-quarterly/) before I could finish the article. In 2021 I [updated the Python Sudoku program](Sudoku.ipynb) to Jupyter notebook form with modern coding idioms, and now I do the same for the [more efficient Java Sudoku program](Sudoku.java). \n",
|
||||||
"\n",
|
"\n",
|
||||||
"(I asked Claude and Codex to help me modernize my Java, and I apologize for any remaining legacy Java idioms from decades ago. It turns out the AI coding tools didn't have any significant suggestions for how to make the code faster.)\n",
|
"I asked Claude and Codex to help me modernize my Java, and they were very good at that. I apologize if there are remaining legacy Java idioms from decades ago. The tools did not have any suggestions for how to make the code significantly faster (although the switch to a more modern threading API did make the code a few percent faster). I guess I did a good job of writing efficient Java way back in 2007.\n",
|
||||||
"\n",
|
"\n",
|
||||||
"- [**Sudoku.java**](Sudoku.java) is the Java program discussed in this notebook.\n",
|
"- [**Sudoku.java**](Sudoku.java) is the Java program discussed in this notebook.\n",
|
||||||
"- [**Sudoku.ipynb**](Sudoku.ipynb) is the Python notebook."
|
"- [**Sudoku.ipynb**](Sudoku.ipynb) is the original Python notebook."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -134,7 +134,7 @@
|
|||||||
"\n",
|
"\n",
|
||||||
"I gathered some Sudoku puzzles from various sources:\n",
|
"I gathered some Sudoku puzzles from various sources:\n",
|
||||||
"- [sudokus_hard.txt](sudokus_hard.txt): Allegedly hardest problems, form the [New Sudoku Players Forum](http://forum.enjoysudoku.com/the-hardest-sudokus-new-thread-t6539.html#p65791).\n",
|
"- [sudokus_hard.txt](sudokus_hard.txt): Allegedly hardest problems, form the [New Sudoku Players Forum](http://forum.enjoysudoku.com/the-hardest-sudokus-new-thread-t6539.html#p65791).\n",
|
||||||
"- [sudokus_17.txt](sudokus_17.txt): Puzzles with exactly 17 filled-in squares, from [Gordon Royle at the University of Western Australia](https://web.archive.org/web/20131019184812if_/http://school.maths.uwa.edu.au/~gordon/sudokumin.php).\n",
|
"- [sudokus_17.txt](sudokus_17.txt): Puzzles with exactly 17 filled-in squares (the minimum), from [Gordon Royle at the University of Western Australia](https://web.archive.org/web/20131019184812if_/http://school.maths.uwa.edu.au/~gordon/sudokumin.php).\n",
|
||||||
"- [sudokus.txt](sudokus.txt): My collection, from various sources, and some automatically generated (by permuting rows, or swapping digits).\n",
|
"- [sudokus.txt](sudokus.txt): My collection, from various sources, and some automatically generated (by permuting rows, or swapping digits).\n",
|
||||||
"\n",
|
"\n",
|
||||||
"Here are the number of puzzles in each file:"
|
"Here are the number of puzzles in each file:"
|
||||||
@@ -178,9 +178,9 @@
|
|||||||
"text": [
|
"text": [
|
||||||
"Puzzles μsec KHz Threads Backtracks Name\n",
|
"Puzzles μsec KHz Threads Backtracks Name\n",
|
||||||
"======= ====== ======= ======= ========== ====\n",
|
"======= ====== ======= ======= ========== ====\n",
|
||||||
" 30000 28.3 35.305 28 330.7 sudokus_hard.txt\n",
|
" 30000 27.5 36.392 28 330.7 sudokus_hard.txt\n",
|
||||||
" 983020 2.0 493.460 28 41.7 sudokus_17.txt\n",
|
" 983020 2.0 500.670 28 41.7 sudokus_17.txt\n",
|
||||||
"1000000 2.5 397.963 28 32.5 sudokus.txt\n"
|
"1000000 2.5 400.960 28 32.5 sudokus.txt\n"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -192,7 +192,9 @@
|
|||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"**Conclusion: The program solves about 35,000 puzzles per second with the hardest puzzles, and about 400,000 per second with easier puzzles.** This is a pretty good performance for such a simple program! One of the fastest known Sudoku solvers, [**tdoku**](https://github.com/t-dillon/tdoku), reports similar speed (but that was in 2020, so they probably used a slower computer than my 28-core Mac Studio M3 Ultra).\n",
|
"**Conclusion: The program solves about 35,000 puzzles per second with the hardest puzzles, and about 400,000 per second (or more) with easier puzzles.** \n",
|
||||||
|
"\n",
|
||||||
|
"This is a pretty good performance for such a simple program! One of the fastest known Sudoku solvers, [**tdoku**](https://github.com/t-dillon/tdoku), reports similar speed (but that was in 2020, so they probably used a slower computer than my 28-core 96 GB Mac Studio M3 Ultra).\n",
|
||||||
"\n",
|
"\n",
|
||||||
"The columns of the output are:\n",
|
"The columns of the output are:\n",
|
||||||
"\n",
|
"\n",
|
||||||
@@ -220,9 +222,9 @@
|
|||||||
"text": [
|
"text": [
|
||||||
"Puzzles μsec KHz Threads Backtracks Name\n",
|
"Puzzles μsec KHz Threads Backtracks Name\n",
|
||||||
"======= ====== ======= ======= ========== ====\n",
|
"======= ====== ======= ======= ========== ====\n",
|
||||||
" 3000 284.3 3.518 1 292.2 sudokus_hard.txt\n",
|
" 3000 286.2 3.495 1 292.2 sudokus_hard.txt\n",
|
||||||
" 49151 69.4 14.417 1 40.2 sudokus_17.txt\n",
|
" 49151 70.7 14.138 1 40.2 sudokus_17.txt\n",
|
||||||
" 250000 66.5 15.032 1 34.2 sudokus.txt\n"
|
" 250000 67.0 14.932 1 34.2 sudokus.txt\n"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -243,7 +245,7 @@
|
|||||||
"source": [
|
"source": [
|
||||||
"# Effect of Naked Pairs Strategy\n",
|
"# Effect of Naked Pairs Strategy\n",
|
||||||
"\n",
|
"\n",
|
||||||
"This Java program uses the so-called [naked pairs strategy](https://www.learn-sudoku.com/naked-pairs.html) to eliminate some possible digits, whereas my earlier Python program did not. The following invocation yields two lines of output, the first using the naked pairs strategy, the second without:"
|
"This Java program uses the so-called [naked pairs strategy](https://www.learn-sudoku.com/naked-pairs.html): If two squares in a unit share exactly the same two possible values, eliminate those values from every other square in that unit. My previous Python program did not use this strategy. The following invocation yields two lines of output, the first using the naked pairs strategy, the second without:"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -257,20 +259,20 @@
|
|||||||
"text": [
|
"text": [
|
||||||
"Puzzles μsec KHz Threads Backtracks Name\n",
|
"Puzzles μsec KHz Threads Backtracks Name\n",
|
||||||
"======= ====== ======= ======= ========== ====\n",
|
"======= ====== ======= ======= ========== ====\n",
|
||||||
" 250000 3.1 325.734 28 34.2 sudokus.txt\n",
|
"1000000 2.6 378.661 28 32.5 sudokus.txt\n",
|
||||||
" 250000 4.8 207.398 28 109.1 sudokus.txt\n"
|
"1000000 4.2 240.693 28 107.4 sudokus.txt\n"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"!java Sudoku -naked sudokus.txt -nonaked sudokus.txt"
|
"!java Sudoku -r -R2 -naked sudokus.txt -nonaked sudokus.txt"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"The program is three times as fast with naked pairs compared to without. Perhaps implementing [other strategies](https://bestofsudoku.com/sudoku-strategy) could give similar speedups. However, my feeling is that the program is already fast enough, and there would probably be diminishing returns from further strategies."
|
"The program is 50% faster with naked pairs compared to without. Perhaps implementing [other strategies](https://bestofsudoku.com/sudoku-strategy) could give similar speedups. However, my feeling is that the program is already fast enough, and there would probably be diminishing returns from further strategies."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -340,44 +342,44 @@
|
|||||||
" </tr>\n",
|
" </tr>\n",
|
||||||
" <tr>\n",
|
" <tr>\n",
|
||||||
" <th>mean</th>\n",
|
" <th>mean</th>\n",
|
||||||
" <td>73.332460</td>\n",
|
" <td>74.375662</td>\n",
|
||||||
" <td>23.580335</td>\n",
|
" <td>23.166825</td>\n",
|
||||||
" <td>42.145663</td>\n",
|
" <td>42.145663</td>\n",
|
||||||
" </tr>\n",
|
" </tr>\n",
|
||||||
" <tr>\n",
|
" <tr>\n",
|
||||||
" <th>std</th>\n",
|
" <th>std</th>\n",
|
||||||
" <td>148.850484</td>\n",
|
" <td>151.413051</td>\n",
|
||||||
" <td>11.314832</td>\n",
|
" <td>11.061670</td>\n",
|
||||||
" <td>187.343129</td>\n",
|
" <td>187.343129</td>\n",
|
||||||
" </tr>\n",
|
" </tr>\n",
|
||||||
" <tr>\n",
|
" <tr>\n",
|
||||||
" <th>min</th>\n",
|
" <th>min</th>\n",
|
||||||
" <td>11.700000</td>\n",
|
" <td>11.800000</td>\n",
|
||||||
" <td>0.099000</td>\n",
|
" <td>0.102000</td>\n",
|
||||||
" <td>0.000000</td>\n",
|
" <td>0.000000</td>\n",
|
||||||
" </tr>\n",
|
" </tr>\n",
|
||||||
" <tr>\n",
|
" <tr>\n",
|
||||||
" <th>25%</th>\n",
|
" <th>25%</th>\n",
|
||||||
" <td>32.300000</td>\n",
|
" <td>33.000000</td>\n",
|
||||||
" <td>15.605000</td>\n",
|
" <td>15.405000</td>\n",
|
||||||
" <td>1.000000</td>\n",
|
" <td>1.000000</td>\n",
|
||||||
" </tr>\n",
|
" </tr>\n",
|
||||||
" <tr>\n",
|
" <tr>\n",
|
||||||
" <th>50%</th>\n",
|
" <th>50%</th>\n",
|
||||||
" <td>41.800000</td>\n",
|
" <td>42.500000</td>\n",
|
||||||
" <td>23.952000</td>\n",
|
" <td>23.553000</td>\n",
|
||||||
" <td>5.000000</td>\n",
|
" <td>5.000000</td>\n",
|
||||||
" </tr>\n",
|
" </tr>\n",
|
||||||
" <tr>\n",
|
" <tr>\n",
|
||||||
" <th>75%</th>\n",
|
" <th>75%</th>\n",
|
||||||
" <td>64.100000</td>\n",
|
" <td>64.900000</td>\n",
|
||||||
" <td>30.928000</td>\n",
|
" <td>30.303000</td>\n",
|
||||||
" <td>24.000000</td>\n",
|
" <td>24.000000</td>\n",
|
||||||
" </tr>\n",
|
" </tr>\n",
|
||||||
" <tr>\n",
|
" <tr>\n",
|
||||||
" <th>max</th>\n",
|
" <th>max</th>\n",
|
||||||
" <td>10142.500000</td>\n",
|
" <td>9770.700000</td>\n",
|
||||||
" <td>85.412000</td>\n",
|
" <td>85.106000</td>\n",
|
||||||
" <td>13438.000000</td>\n",
|
" <td>13438.000000</td>\n",
|
||||||
" </tr>\n",
|
" </tr>\n",
|
||||||
" </tbody>\n",
|
" </tbody>\n",
|
||||||
@@ -387,13 +389,13 @@
|
|||||||
"text/plain": [
|
"text/plain": [
|
||||||
" μsec KHz Backtracks\n",
|
" μsec KHz Backtracks\n",
|
||||||
"count 49528.000000 49528.000000 49528.000000\n",
|
"count 49528.000000 49528.000000 49528.000000\n",
|
||||||
"mean 73.332460 23.580335 42.145663\n",
|
"mean 74.375662 23.166825 42.145663\n",
|
||||||
"std 148.850484 11.314832 187.343129\n",
|
"std 151.413051 11.061670 187.343129\n",
|
||||||
"min 11.700000 0.099000 0.000000\n",
|
"min 11.800000 0.102000 0.000000\n",
|
||||||
"25% 32.300000 15.605000 1.000000\n",
|
"25% 33.000000 15.405000 1.000000\n",
|
||||||
"50% 41.800000 23.952000 5.000000\n",
|
"50% 42.500000 23.553000 5.000000\n",
|
||||||
"75% 64.100000 30.928000 24.000000\n",
|
"75% 64.900000 30.303000 24.000000\n",
|
||||||
"max 10142.500000 85.412000 13438.000000"
|
"max 9770.700000 85.106000 13438.000000"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"execution_count": 9,
|
"execution_count": 9,
|
||||||
@@ -413,7 +415,7 @@
|
|||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"The mean latency is 73 microseconds, the median is about 42 microseconds, and the maximum is about 10 milliseconds. For number of backtracks, the mean is 42, the median is only 5, and the maximum is over 13,000. \n",
|
"The mean latency is 74 microseconds, the median is about 42 microseconds, and the maximum is about 10 milliseconds. For number of backtracks, the mean is 42, the median is only 5, and the maximum is over 13,000. \n",
|
||||||
"\n",
|
"\n",
|
||||||
"Here is a scatterplot:"
|
"Here is a scatterplot:"
|
||||||
]
|
]
|
||||||
@@ -425,7 +427,7 @@
|
|||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
"image/png": "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 truncated
|
"image/png": "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 truncated
|
||||||
"text/plain": [
|
"text/plain": [
|
||||||
"<Figure size 640x480 with 1 Axes>"
|
"<Figure size 640x480 with 1 Axes>"
|
||||||
]
|
]
|
||||||
@@ -464,7 +466,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
"image/png": "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 truncated
|
"image/png": "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 truncated
|
||||||
"text/plain": [
|
"text/plain": [
|
||||||
"<Figure size 640x480 with 1 Axes>"
|
"<Figure size 640x480 with 1 Axes>"
|
||||||
]
|
]
|
||||||
@@ -717,23 +719,16 @@
|
|||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"One final word: I got around to writing this up after a friend mentioned to me \"*Hey, a while back didn't you do a Sudoku program that could solve like **a dozen puzzles a second** or something?*\" I felt like Robert Wagner when he had to explain to Dr. Evil that \"*a million dollars isn't exactly a lot of money these days.*\" \n",
|
"One final word: I got around to writing this up after a friend mentioned to me \"*Hey, a while back didn't you do a Sudoku program that could solve like **a hundred puzzles a second** or something?*\" I felt like Robert Wagner when he had to explain to Dr. Evil that \"*a million dollars isn't exactly a lot of money these days.*\" \n",
|
||||||
"\n",
|
"\n",
|
||||||
"<img src=\"https://upload.wikimedia.org/wikipedia/en/1/16/Drevil_million_dollars.jpg\" height=\"249\" width=\"201\">\n",
|
"<img src=\"https://upload.wikimedia.org/wikipedia/en/1/16/Drevil_million_dollars.jpg\" height=\"249\" width=\"201\">\n",
|
||||||
"\n",
|
"\n",
|
||||||
"*One **dozen** puzzles per second!*\n",
|
"*One **hundred** puzzles per second!*\n",
|
||||||
"\n",
|
"\n",
|
||||||
"<img src=\"Number_2.png\" height=\"249\" width=\"201\">\n",
|
"<img src=\"Number_2.png\" height=\"249\" width=\"201\">\n",
|
||||||
"\n",
|
"\n",
|
||||||
"*A dozen isn't exactly a lot of throughput these days...*"
|
"*A hundred isn't exactly a lot of throughput these days...*"
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": null,
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": []
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
|
|||||||
+49359
-49359
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user