"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"0.210 seconds\n"
]
}
],
"source": [
"def do1(puzzle):\n",
" \"Do one puzzle; showing puzzle and solution and printing elapsed time.\"\n",
" show(puzzle)\n",
" t0 = time.clock()\n",
" solution = solve(Grid(puzzle))\n",
" t1 = time.clock()\n",
" assert is_solution(solution, puzzle)\n",
" show(solution)\n",
" print('{:.3f} seconds'.format(t1 - t0))\n",
"\n",
"use_grid_size(16)\n",
"tiny = Grid('...44.3..4...241')\n",
"do1(tiny)"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": false
},
"source": [
"That works fine for a 4x4 array. But on a 9x9, with say, 64 empty squares, there will be 964 combinations of digits.\n",
"Suppose we have access to a secret new custom 10 GHz\n",
"GPU capable of doing 1024 generate and\n",
"test operations in a single clock cycle, and let's say we can fit a million of these units\n",
"in a data center, and we could afford a hundred data centers,\n",
"and while we're shopping, let's say we also pick up a time\n",
"machine and go back 13 billion years to the early days of the universe,\n",
"and a fleet of starships with which we visit a trillion galaxies and\n",
"in each galaxy convince the inhabitants of a billion planets to each\n",
"buy a similar setup, and we all start our programs\n",
"running, managing to distribute the cases perfectly so that no\n",
"computation is duplicated or wasted. Then we can [estimate](http://www.google.com/search?aq=f&sourceid=chrome&ie=UTF-8&q=10+GHz+*+1024+*+1+million+*+100+*+1+trillion+*+1+billion+*+13+billion+years+%2F+9**64+in+percent)\n",
"that we'd be only 3% of the way through with the first puzzle.\n",
"\n",
"Generate and test is not the algorithm you're looking for. Move along."
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": false
},
"source": [
"# Solver 2: Combinatorial Search\n",
"\n",
"In Simon's safe example, he next supposes a defective safe, in which a\n",
"faint click is heard whenever a dial is set to the right position.\n",
"With this safe it only takes 100 × 10 trials to discover the\n",
"correct combination, not 10010. The moral is that if we have some selective\n",
"knowledge of components of the safe (or puzzle) that tell us if a\n",
"partial solution is on track, our search will be\n",
"much easier.\n",
"\n",
"
Fortunately for us, Sudoku is like the defective safe. To be\n",
"precise, it is like a safe with 81 dials, each with 9 numbers,\n",
"and sometimes if you put one of the dials in the wrong position you hear a sound,\n",
"but sometimes not (depending on the positions of the other dials and\n",
"on how carefully you listen). For\n",
"example, in the lower-left corner of the tiny puzzle above, if we try\n",
"1, 2, or 4 we would immediately get feedback that those digits won't\n",
"work, because they already appear elsewhere in the bottom row.\n",
"Therefore, the lower-left corner must be filled by a 3. There are\n",
"262,144 ways to fill in the 9 empty squares, but right away we can eliminate\n",
"196,608 of them; we need only consider the ones that have a 3 in that position.\n",
"\n",
"
Unfortunately for us, Sudoku does not give up all its secrets so\n",
"easily. Sometimes, when we consider a square, we can't immediately\n",
"tell what digit to put there. For example, in the upper-left of the tiny\n",
"puzzle, only 4 can be eliminated as a possibility. That's ok--we're\n",
"allowing ourselves the use of an eraser, so just guess one of the\n",
"remaining three possibilities; if it doesn't pan out, erase it and try one\n",
"of the others. This gives us:\n",
"\n",
"\n",
"
Combinatorial search algorithm: Start filling squares with digits, one at a time, always making sure\n",
"that the digit we put in a square is not the same as any peer. If there are several possible digits, pick one, but remember the others. If we\n",
"reach a square where every digit conflicts with a peer, back\n",
"up and consider a different digit for the previous square. Stop when\n",
"we successfully fill every square, or give up if we tried all\n",
"possibilities without finding a solution.
\n",
"\n",
"The progress of this algorithm can be described as a search\n",
"tree, where each node represents a partially-filled (incremental) state of the\n",
"puzzle, and a branch corresponds to filling in a square with a\n",
"digit. (We display the new digits in red.)\n",
"Here is a search tree for the tiny puzzle:\n",
"\n",
"\n",
"\n",
"A few things to note about this particular search tree:\n",
"
\n",
" - Each successive level fills in one square, so with nine empty\n",
" squares, the solution must appear on the ninth level of the tree\n",
" (counting the root as the zeroth level).\n",
"
- This is a well-formed puzzle, so there will be exactly one solution,\n",
" and thus exactly one node at the ninth level.\n",
"
- A rectangle with a black X indicates an\n",
" inconsistency: there is no way to continue because the next\n",
" unfilled square\n",
" already has all four digits taken by its peers.\n",
"
- When we hit a X we have to pull out our eraser, or in\n",
" other words we have to backtrack--go up a level in the tree and\n",
" try the next possible digit for the previous square. On the far-left\n",
" branch of the tree we have to back up three levels---erasing first the 2, then the 3, then the\n",
" 1--before we can pencil in another possible digit \n",
" (the 2 in the upper-left corner which we\n",
" see in the middle branch of the tree).\n",
" \n",
"
- The rightmost branch of the tree remains unexplored--the search\n",
" proceeds left-to-right and has not got to that brnach yet. (But since the\n",
" puzzle is well-formed and we have found the solution, we know the\n",
" unexplored branches cannot contain another solution.)\n",
" \n",
"
\n",
"The code to implement combinatorial search is straightforward:"
]
},
{
"cell_type": "code",
"execution_count": 99,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def search(grid) -> Grid:\n",
" \"\"\"Select an unfilled square, try each possible digit there, recursively searching.\n",
" When all squares filled: success; when no more digits to try: return None for failure.\"\"\"\n",
" square = select_empty_square(grid)\n",
" if square is None: # No empty square means the grid is a solution\n",
" return grid\n",
" for digit in possible_digits(grid, square):\n",
" result = search(assign(Grid(grid), square, digit))\n",
" if result: \n",
" return result\n",
"\n",
"solve = search\n",
"\n",
"def select_empty_square(grid) -> Square:\n",
" \"Return the first square that is not filled with a digit; or None if all squares are filled.\"\n",
" return (None if grid is None else first(empty_squares(grid)))\n",
"\n",
"def assign(grid, s, d) -> Grid:\n",
" \"For now, simply assign grid[s] = d.\"\n",
" grid[s] = d\n",
" return grid\n",
"\n",
"def possible_digits(grid, s):\n",
" \"A square can be filled with any digit that is not already taken by a peer.\"\n",
" peer_digits = {grid[s] for s in peers[s]}\n",
" return digits - peer_digits"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"The key function is search. It obeys the convention that if\n",
"it is passed Inconsistent (to indicate an invalid grid) it returns\n",
"Inconsistent, meaning that no solution can be found. Otherwise it\n",
"selects some unfilled square to work on. If select_square\n",
"returns None that is actually good news: it means that all\n",
"the squares are already filled, and we are done: we have a solution,\n",
"so we just return it. Otherwise, for each possible\n",
"digit that could fill square s we try to assign that digit to\n",
"s and search for a solution from there. If some call to\n",
"search succeeds, return it; but if a digit assignment causes\n",
"the search to fail, just keep going with the next digit assignment.\n",
"If all digit assignments fail, then the whole call to search\n",
"fails, and we back up to the previous recursive call. \n",
"\n",
"
We call this type of combinatorial search a constraint\n",
" satisfaction search: think of each square as being a\n",
" variable that can take on a value (a digit), and the\n",
" rules of Sudoku as being constraints on the possible values.\n",
" A state in the search tree is then an assignment of values to some\n",
" subset of variables in a way that does not violate any constraint.\n",
"The constraint satisfaction\n",
" approach has found many fun applications in puzzles and serious\n",
" applications in problem solving.\n",
"\n",
"
Here we see solve (and therefore search) in action:\n",
"\n",
"\n",
"\n",
"That's all there is to it! The only reason we don't stop this article\n",
"now is that the program is still too slow for some purposes. We're\n",
"not talking billions-of-years slow, but it did take 3 minutes and 45\n",
"seconds to solve this puzzle (a rather hard one). On a benchmark of\n",
"50 easy puzzles the program was fast enough, taking a total of 9.5\n",
"seconds (a rate of 5\n",
"puzzles per second, or 5 Hz). \n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 96,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"
| 5 | 3 | . | . | 7 | . | . | . | . |
| 6 | . | . | 1 | 9 | 5 | . | . | . |
| . | 9 | 8 | . | . | . | . | 6 | . |
| 8 | . | . | . | 6 | . | . | . | 3 |
| 4 | . | . | 8 | . | 3 | . | . | 1 |
| 7 | . | . | . | 2 | . | . | . | 6 |
| . | 6 | . | . | . | . | 2 | 8 | . |
| . | . | . | 4 | 1 | 9 | . | . | 5 |
| . | . | . | . | 8 | . | . | 7 | 9 |
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"| 5 | 3 | 4 | 6 | 7 | 8 | 9 | 1 | 2 |
| 6 | 7 | 2 | 1 | 9 | 5 | 3 | 4 | 8 |
| 1 | 9 | 8 | 3 | 4 | 2 | 5 | 6 | 7 |
| 8 | 5 | 9 | 7 | 6 | 1 | 4 | 2 | 3 |
| 4 | 2 | 6 | 8 | 5 | 3 | 7 | 9 | 1 |
| 7 | 1 | 3 | 9 | 2 | 4 | 8 | 5 | 6 |
| 9 | 6 | 1 | 5 | 3 | 7 | 2 | 8 | 4 |
| 2 | 8 | 7 | 4 | 1 | 9 | 6 | 3 | 5 |
| 3 | 4 | 5 | 2 | 8 | 6 | 1 | 7 | 9 |
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"0.024 seconds\n"
]
}
],
"source": [
"do1(puzzle0)"
]
},
{
"cell_type": "code",
"execution_count": 97,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"| 4 | . | 7 | . | 6 | . | 8 | . | 5 |
| . | 3 | . | . | . | . | 9 | . | . |
| . | . | . | 7 | . | . | . | . | . |
| . | 2 | . | . | . | . | . | 6 | . |
| . | . | . | . | 8 | . | 4 | . | . |
| . | . | . | . | 1 | . | . | . | . |
| . | . | . | 6 | . | 3 | . | 7 | . |
| 5 | . | . | 2 | . | . | . | . | . |
| 1 | . | 4 | . | . | . | . | . | . |
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"| 4 | 1 | 7 | 3 | 6 | 9 | 8 | 2 | 5 |
| 6 | 3 | 2 | 1 | 5 | 8 | 9 | 4 | 7 |
| 9 | 5 | 8 | 7 | 2 | 4 | 3 | 1 | 6 |
| 8 | 2 | 5 | 4 | 3 | 7 | 1 | 6 | 9 |
| 7 | 9 | 1 | 5 | 8 | 6 | 4 | 3 | 2 |
| 3 | 4 | 6 | 9 | 1 | 2 | 7 | 5 | 8 |
| 2 | 8 | 9 | 6 | 4 | 3 | 5 | 7 | 1 |
| 5 | 7 | 3 | 2 | 9 | 1 | 6 | 8 | 4 |
| 1 | 6 | 4 | 8 | 7 | 5 | 2 | 9 | 3 |
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"17.216 seconds\n"
]
}
],
"source": [
"do1(puzzle1)"
]
},
{
"cell_type": "code",
"execution_count": 98,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 63904089 function calls (62617098 primitive calls) in 30.046 seconds\n",
"\n",
" Ordered by: standard name\n",
"\n",
" ncalls tottime percall cumtime percall filename:lineno(function)\n",
" 52321162 8.946 0.000 8.946 0.000 :17(is_filled)\n",
" 1286992 0.444 0.000 20.602 0.000 :36(first)\n",
"1286992/1 3.380 0.000 30.046 30.046 :1(search)\n",
" 1286992 1.496 0.000 22.374 0.000 :14(select_empty_square)\n",
" 2573983 11.162 0.000 20.108 0.000 :17()\n",
" 1286991 0.238 0.000 0.238 0.000 :19(assign)\n",
" 1286991 1.272 0.000 4.055 0.000 :24(possible_digits)\n",
" 1286991 2.783 0.000 2.783 0.000 :26()\n",
" 1 0.000 0.000 30.046 30.046 :1()\n",
" 1 0.000 0.000 30.046 30.046 {built-in method builtins.exec}\n",
" 1286992 0.326 0.000 20.159 0.000 {built-in method builtins.next}\n",
" 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}\n",
"\n",
"\n"
]
}
],
"source": [
"import cProfile\n",
"cProfile.run(\"solve(puzzle1)\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Solver 3. Arc Consistency"
]
},
{
"cell_type": "code",
"execution_count": 93,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def solve(puzzle: Grid) -> Grid:\n",
" grid = Grid(digitstr if d is empty else d\n",
" for d in puzzle)\n",
" return search(grid)\n",
"\n",
"def possible_digits(grid, s) -> Digits: return grid[s]\n",
"\n",
"def assign(grid, s, d) -> Grid:\n",
" \"\"\"Assign grid[s] = d and eliminate d from the peers of s.\n",
" Return the updated grid, or Inconsistent if inconsistency detected.\"\"\"\n",
" if d not in grid[s]: return Inconsistent # d is not among the possibilities\n",
" grid[s] = d\n",
" if not all(eliminate(grid, p, d) for p in peers[s]): \n",
" return None\n",
" return grid\n",
"\n",
"def eliminate(grid, s, d) -> bool:\n",
" \"Remove d from possibilities for grid[s]. If checking finds an inconsistency return None.\"\n",
" if d not in grid[s]: \n",
" return True # Already eliminated d\n",
" grid[s] = grid[s].replace(d, '')\n",
" return all(constraint(grid, s, d) \n",
" for constraint in constraints)\n",
"\n",
"def arc_consistent(grid, s, d):\n",
" \"Return true if s has multiple digits left, or one that we can consistently assign.\"\n",
" ndigits = len(grid[s])\n",
" return ndigits >= 2 or (ndigits == 1 and assign(grid, s, grid[s]))\n",
"\n",
"constraints = [arc_consistent]"
]
},
{
"cell_type": "code",
"execution_count": 94,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"| 4 | . | 7 | . | 6 | . | 8 | . | 5 |
| . | 3 | . | . | . | . | 9 | . | . |
| . | . | . | 7 | . | . | . | . | . |
| . | 2 | . | . | . | . | . | 6 | . |
| . | . | . | . | 8 | . | 4 | . | . |
| . | . | . | . | 1 | . | . | . | . |
| . | . | . | 6 | . | 3 | . | 7 | . |
| 5 | . | . | 2 | . | . | . | . | . |
| 1 | . | 4 | . | . | . | . | . | . |
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"ename": "TypeError",
"evalue": "'NoneType' object is not subscriptable",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mdo1\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mpuzzle1\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# Should be 0.2\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;32m\u001b[0m in \u001b[0;36mdo1\u001b[0;34m(puzzle)\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0mshow\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mpuzzle\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0mt0\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtime\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mclock\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0msolution\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msolve\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mGrid\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mpuzzle\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 6\u001b[0m \u001b[0mt1\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtime\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mclock\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0mis_solution\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msolution\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpuzzle\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m\u001b[0m in \u001b[0;36msolve\u001b[0;34m(puzzle)\u001b[0m\n\u001b[1;32m 2\u001b[0m grid = Grid(digitstr if d is empty else d\n\u001b[1;32m 3\u001b[0m for d in puzzle)\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0msearch\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgrid\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 5\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mpossible_digits\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgrid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0ms\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0mDigits\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mgrid\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0ms\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m\u001b[0m in \u001b[0;36msearch\u001b[0;34m(grid)\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mgrid\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mdigit\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mpossible_digits\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgrid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msquare\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 8\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msearch\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0massign\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mGrid\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgrid\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msquare\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdigit\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 9\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m\u001b[0m in \u001b[0;36msearch\u001b[0;34m(grid)\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mgrid\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mdigit\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mpossible_digits\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgrid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msquare\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 8\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msearch\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0massign\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mGrid\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgrid\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msquare\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdigit\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 9\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m\u001b[0m in \u001b[0;36msearch\u001b[0;34m(grid)\u001b[0m\n\u001b[1;32m 2\u001b[0m \"\"\"Select an unfilled square, try each possible digit there, recursively searching.\n\u001b[1;32m 3\u001b[0m When all squares filled: success; when no more digits to try: return None for failure.\"\"\"\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0msquare\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mselect_empty_square\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgrid\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 5\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0msquare\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# No empty square means the grid is a solution\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mgrid\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m\u001b[0m in \u001b[0;36mselect_empty_square\u001b[0;34m(grid)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mselect_empty_square\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgrid\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\"Return an unfilled square with the fewest possible digits; or None if no unfilled squares.\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0munfilled\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0ms\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0ms\u001b[0m \u001b[0;32min\u001b[0m \u001b[0msquares\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgrid\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0ms\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 4\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mmin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0munfilled\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkey\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mlambda\u001b[0m \u001b[0ms\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgrid\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0ms\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0munfilled\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mselect_empty_square\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgrid\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\"Return an unfilled square with the fewest possible digits; or None if no unfilled squares.\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0munfilled\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0ms\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0ms\u001b[0m \u001b[0;32min\u001b[0m \u001b[0msquares\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgrid\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0ms\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 4\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mmin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0munfilled\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkey\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mlambda\u001b[0m \u001b[0ms\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgrid\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0ms\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0munfilled\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;31mTypeError\u001b[0m: 'NoneType' object is not subscriptable"
]
}
],
"source": [
"do1(puzzle1) # Should be 0.2"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Benchmarking"
]
},
{
"cell_type": "code",
"execution_count": 86,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"puzzles = \"\"\"\n",
"4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......\n",
"52...6.........7.13...........4..8..6......5...........418.........3..2...87.....\n",
"6.....8.3.4.7.................5.4.7.3..2.....1.6.......2.....5.....8.6......1....\n",
"48.3............71.2.......7.5....6....2..8.............1.76...3.....4......5....\n",
"....14....3....2...7..........9...3.6.1.............8.2.....1.4....5.6.....7.8...\n",
"......52..8.4......3...9...5.1...6..2..7........3.....6...1..........7.4.......3.\n",
"6.2.5.........3.4..........43...8....1....2........7..5..27...........81...6.....\n",
".524.........7.1..............8.2...3.....6...9.5.....1.6.3...........897........\n",
"6.2.5.........4.3..........43...8....1....2........7..5..27...........81...6.....\n",
".923.........8.1...........1.7.4...........658.........6.5.2...4.....7.....9.....\n",
"6..3.2....5.....1..........7.26............543.........8.15........4.2........7..\n",
".6.5.1.9.1...9..539....7....4.8...7.......5.8.817.5.3.....5.2............76..8...\n",
"..5...987.4..5...1..7......2...48....9.1.....6..2.....3..6..2.......9.7.......5..\n",
"3.6.7...........518.........1.4.5...7.....6.....2......2.....4.....8.3.....5.....\n",
"1.....3.8.7.4..............2.3.1...........958.........5.6...7.....8.2...4.......\n",
"6..3.2....4.....1..........7.26............543.........8.15........4.2........7..\n",
"....3..9....2....1.5.9..............1.2.8.4.6.8.5...2..75......4.1..6..3.....4.6.\n",
"45.....3....8.1....9...........5..9.2..7.....8.........1..4..........7.2...6..8..\n",
".237....68...6.59.9.....7......4.97.3.7.96..2.........5..47.........2....8.......\n",
"..84...3....3.....9....157479...8........7..514.....2...9.6...2.5....4......9..56\n",
"\"\"\".split()[5:6]\n",
"\n",
"benchmarks = {}\n",
"\n",
"def benchmark(label, puzzles=puzzles):\n",
" \"Run `solve` on these puzzles; record and verify results for this label; print all results.\"\n",
" n = len(puzzles)\n",
" t0 = time.clock()\n",
" results = [solve(Grid(p)) for p in puzzles]\n",
" avg = (time.clock() - t0) / len(puzzles)\n",
" for (r, p) in zip(results, puzzles):\n",
" assert is_solution(r, p) \n",
" benchmarks[label] = '{:.3f} sec/puzzle ({:.1f} Hz)'.format(avg, 1/avg)\n",
" for L in sorted(benchmarks):\n",
" print('{:10s} {}'.format(L, benchmarks[L]))"
]
},
{
"cell_type": "code",
"execution_count": 84,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"1"
]
},
"execution_count": 84,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"len(puzzles)"
]
},
{
"cell_type": "code",
"execution_count": 87,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3. AC 11.219 sec/puzzle (0.1 Hz)\n"
]
}
],
"source": [
"benchmark('3. AC')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 4. Dual Consistency"
]
},
{
"cell_type": "code",
"execution_count": 72,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def dual_consistent(grid, s, d):\n",
" \"\"\"After eliminating d from grid[s], check each unit of s and make sure there is some\n",
" position in the unit for d. If only one possible place left for d, assign it.\"\"\"\n",
" for u in units[s]:\n",
" places_for_d = [s2 for s2 in u if d in grid[s2]]\n",
" nplaces = len(places_for_d)\n",
" if nplaces==0 or (nplaces==1 and not assign(grid, places_for_d[0], d)):\n",
" return None\n",
" return True"
]
},
{
"cell_type": "code",
"execution_count": 88,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3. AC 11.219 sec/puzzle (0.1 Hz)\n",
"4. AC+Dual 26.392 sec/puzzle (0.0 Hz)\n"
]
}
],
"source": [
"constraints = [arc_consistent, dual_consistent]\n",
"benchmark('4. AC+Dual')"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"# 5. Naked pairs"
]
},
{
"cell_type": "code",
"execution_count": 89,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def naked_pairs(grid, s, ignore):\n",
" \"\"\"Look for two squares in a unit with the same two possible digits. \n",
" For example, if s and s2 both have the value '35', then we know that 3 and 5\n",
" must go in those two squares. We don't know which is which, but we can eliminate \n",
" 3 and 5 from any other square s3 that is in the unit.\"\"\"\n",
" vals = grid[s]\n",
" if len(vals) != 2: return True\n",
" for u in units[s]:\n",
" for s2 in u:\n",
" if s2 != s and grid[s2] == vals:\n",
" # Found naked pair: s and s2; remove their two vals from others in unit\n",
" for s3 in u:\n",
" if s != s3 != s2:\n",
" if not all(eliminate(grid, s3, v) for v in vals):\n",
" return None\n",
" return True"
]
},
{
"cell_type": "code",
"execution_count": 90,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3. AC 11.219 sec/puzzle (0.1 Hz)\n",
"4. AC+Dual 26.392 sec/puzzle (0.0 Hz)\n",
"5. AC+D+NP 29.821 sec/puzzle (0.0 Hz)\n"
]
}
],
"source": [
"constraints = [arc_consistent, dual_consistent, naked_pairs]\n",
"benchmark('5. AC+D+NP')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 6. Variable Ordering"
]
},
{
"cell_type": "code",
"execution_count": 91,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def select_empty_square(grid):\n",
" \"Return an unfilled square with the fewest possible digits; or None if no unfilled squares.\"\n",
" unfilled = [s for s in squares if len(grid[s]) > 1]\n",
" return min(unfilled, key=lambda s: len(grid[s])) if unfilled else None"
]
},
{
"cell_type": "code",
"execution_count": 92,
"metadata": {
"collapsed": false
},
"outputs": [
{
"ename": "TypeError",
"evalue": "'NoneType' object is not subscriptable",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mbenchmark\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'6. VarOrd'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;32m\u001b[0m in \u001b[0;36mbenchmark\u001b[0;34m(label, puzzles)\u001b[0m\n\u001b[1;32m 28\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mpuzzles\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 29\u001b[0m \u001b[0mt0\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtime\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mclock\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 30\u001b[0;31m \u001b[0mresults\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0msolve\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mGrid\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mp\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mp\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mpuzzles\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 31\u001b[0m \u001b[0mavg\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mtime\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mclock\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0mt0\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mpuzzles\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 32\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mp\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mzip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresults\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpuzzles\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 28\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mpuzzles\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 29\u001b[0m \u001b[0mt0\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtime\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mclock\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 30\u001b[0;31m \u001b[0mresults\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0msolve\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mGrid\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mp\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mp\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mpuzzles\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 31\u001b[0m \u001b[0mavg\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mtime\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mclock\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0mt0\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mpuzzles\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 32\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mp\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mzip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresults\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpuzzles\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m\u001b[0m in \u001b[0;36msolve\u001b[0;34m(puzzle)\u001b[0m\n\u001b[1;32m 2\u001b[0m grid = Grid(digitstr if d is empty else d\n\u001b[1;32m 3\u001b[0m for d in puzzle)\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0msearch\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgrid\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 5\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mpossible_digits\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgrid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0ms\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0mDigits\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mgrid\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0ms\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m\u001b[0m in \u001b[0;36msearch\u001b[0;34m(grid)\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mgrid\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mdigit\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mpossible_digits\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgrid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msquare\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 8\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msearch\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0massign\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mGrid\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgrid\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msquare\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdigit\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 9\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m\u001b[0m in \u001b[0;36msearch\u001b[0;34m(grid)\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mgrid\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mdigit\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mpossible_digits\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgrid\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msquare\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 8\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msearch\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0massign\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mGrid\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgrid\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msquare\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdigit\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 9\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m\u001b[0m in \u001b[0;36msearch\u001b[0;34m(grid)\u001b[0m\n\u001b[1;32m 2\u001b[0m \"\"\"Select an unfilled square, try each possible digit there, recursively searching.\n\u001b[1;32m 3\u001b[0m When all squares filled: success; when no more digits to try: return None for failure.\"\"\"\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0msquare\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mselect_empty_square\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgrid\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 5\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0msquare\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# No empty square means the grid is a solution\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mgrid\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m\u001b[0m in \u001b[0;36mselect_empty_square\u001b[0;34m(grid)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mselect_empty_square\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgrid\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\"Return an unfilled square with the fewest possible digits; or None if no unfilled squares.\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0munfilled\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0ms\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0ms\u001b[0m \u001b[0;32min\u001b[0m \u001b[0msquares\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgrid\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0ms\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 4\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mmin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0munfilled\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkey\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mlambda\u001b[0m \u001b[0ms\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgrid\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0ms\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0munfilled\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mselect_empty_square\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgrid\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\"Return an unfilled square with the fewest possible digits; or None if no unfilled squares.\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0munfilled\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0ms\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0ms\u001b[0m \u001b[0;32min\u001b[0m \u001b[0msquares\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgrid\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0ms\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 4\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mmin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0munfilled\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkey\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mlambda\u001b[0m \u001b[0ms\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgrid\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0ms\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0munfilled\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;31mTypeError\u001b[0m: 'NoneType' object is not subscriptable"
]
}
],
"source": [
"benchmark('6. VarOrd')"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"However, if you want to solve a million hard puzzles, you probably\n",
"don't want to wait a few years, and you would prefer a\n",
"faster algorithm.\n",
"Look again at the search tree above, and consider how we can find the\n",
"solution faster. Here are four general strategies:\n",
"
\n",
"- We could try to move the solution to the left. Since we go top-down,\n",
" left-to-right, this means we would find it sooner. This can\n",
" potentially be done with better value ordering, the order in\n",
" which the function possible_values considers which digit to\n",
" try next. If we were able to choose right every time,\n",
" there would in effect be no search--the solution would be in the\n",
" lower-left corner of the tree and we would arrive at it directly\n",
" with no backtracking.\n",
"
- We could try to move the solution closer to the top. If we only\n",
" fill in one square at a time then the solution will always be at the\n",
" same level (the number of empty squares in the original puzzle).\n",
" But if we could fill in several squares at a time, the solution gets\n",
" closer to the top and we can find it faster. Filling in multiple\n",
" squares at a time can be accomplished with constraint\n",
" propagation--using the constraints imposed by the rules of Sudoku to\n",
" conclude that an unfilled square must take on a certain digit,\n",
" thus sanctioning us to fill in several squares at once.\n",
"
- We could prune branches (mark them with an X and backtrack)\n",
" earlier. This can also be done with\n",
" constraint propagation, but in this case rather than filling in\n",
" squares, we would notice that some\n",
" square has no possible digit, and thus that we can backtrack immediately.\n",
"
- We could consider the squares in a better order, a process called\n",
" variable ordering, which in our program is implemented by the\n",
" function select_square. At each node in\n",
" the tree we pick one square to branch on. The current\n",
" implementation of select_square just considers the squares\n",
" in order from 0 to 80 and chooses the first unfilled one, but it\n",
" could be more clever. A\n",
" different ordering could be better (or worse) for any of the three reasons above.\n",
"
"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.5.1"
}
},
"nbformat": 4,
"nbformat_minor": 0
}