Sudoku

题目

Sudoku puzzles go like this:

Problem

. . 4 8 . . . 1 7
6 7 . 9 . . . . .
5 . 8 . 3 . . . 4
3 . . 7 4 . 1 . .
. 6 9 . . . 7 8 .
. . 1 . 6 9 . . 5
1 . . . 8 . 3 . 6
. . . . . 6 . 9 1
2 4 . . . 1 5 . .

Solution

9 3 4 8 2 5 6 1 7
6 7 2 9 1 4 8 5 3
5 1 8 6 3 7 9 2 4
3 2 5 7 4 8 1 6 9
4 6 9 1 5 3 7 8 2
7 8 1 2 6 9 4 3 5
1 9 7 5 8 2 3 4 6
8 5 3 4 7 6 2 9 1
2 4 6 3 9 1 5 7 8

Every spot in the puzzle belongs to a (horizontal) row and a (vertical) column, as well as to one single 3x3 square (which we call "square" for short). At the beginning, some of the spots carry a single-digit number between 1 and 9. The problem is to fill the missing spots with digits in such a way that every number between 1 and 9 appears exactly once in each row, in each column, and in each square.

解题

首先,使用回溯法求出每个3x3方格的可行解(方格内的数字不重复)。然后,使用回溯法组合九个3x3方格的可行解,求出在全局有效的组合(任意行或列上的数字不重复)。

代码实现

def resolveSquare(square): if isCompleted(square): return [square] nums = availableNums(square) return reduce(concat, [resolveSquare(fill(square, num)) for num in nums], []) def isCompleted(square): return reduce(and_, [(lambda x:x is not None)(e) for e in reduce(concat, square, [])], True) def availableNums(square): all = [num for num in range(1, 10)] used = [num for num in reduce(concat, square, []) if num is not None] return [num for num in all if num not in used] def fill(square, num): newSquare = copy.deepcopy(square) for v in range(0, len(newSquare)): for h in range(0, len(newSquare[v])): if newSquare[v][h] is None: newSquare[v][h] = num return newSquare return newSquare

使用回溯法求出单个方格内所有的填字方式。

from functools import reduce from operator import concat, and_ import copy import numpy as np def sudoku(puzzle): squares = puzzleToSquares(puzzle) solutions = resolveSudoku(squares, []) return [squaresToPuzzle(solution) for solution in solutions] def puzzleToSquares(puzzle): squares = [] d = 3 array = np.array(puzzle) for v in range(0, d): for h in range(0, d): squares.append((array[v*d:v*d+d,h*d:h*d+d]).tolist()) return squares def resolveSudoku(squares, solution): if len(squares) == 0: return [[]] square = squares[0] remainSquares = squares[1:] return [[squareSolution] + remain for squareSolution in resolveSquare(square) for remain in resolveSudoku(remainSquares, solution+[squareSolution]) if isValid(solution + [squareSolution])] def isValid(solutions): puzzleSolution = squaresToPuzzle(solutions) for row in puzzleSolution: nums = [num for num in row if num is not None] if len(nums) != len(set(nums)): return False for y in range(0, 9): nums = [puzzleSolution[x][y] for x in range(0, 9) if puzzleSolution[x][y] is not None] if len(nums) != len(set(nums)): return False return True def squaresToPuzzle(squares): d = 3 puzzle = [[None for h in range(0, 9)] for v in range(0, 9)] for i in range(0, len(squares)): hOffset = (i % d)*d vOffset = (i // d)*d square = squares[i] for v in range(0, len(square)): for h in range(0, len(square[v])): puzzle[vOffset+v][hOffset+h] = square[v][h] return puzzle

同样使用回溯法纙列出九个方格可行解的所有组合,再在这些组合中选出符合全局要的组合为解(任意行或列上的数字不重复)。

results matching ""

    No results matching ""