一個利用DLX演算法的Python解數獨程式
源程式sudokusolver.py
#!/usr/bin/env python # Copyright (C) 2012 Daogan Ao <https://github.com/daogan> # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. # jul 31, 2012 from __future__ import division import math class Node(object): def __init__(self, left = None, right = None, up = None, down = None, col_header = None, row_header = None): self.left = left or self self.right = right or self self.up = up or self self.down = down or self self.col_header = col_header self.row_header = row_header class SudokuSolver(object): def __init__(self, sudoku): self.sudoku = sudoku self.num_cells = len(sudoku) self.dimension = int(math.sqrt(self.num_cells)) self.box_size = int(math.sqrt(self.dimension)) self.num_columns = self.dimension ** 2 * 4 self.cell_offset = 0 self.row_offset = self.dimension ** 2 + self.cell_offset self.col_offset = self.dimension ** 2 * 2 + self.cell_offset self.box_offset = self.dimension ** 2 * 3 + self.cell_offset self.col_headers = [] self.col_size = [0] * self.num_columns self.partial_answer = [-1] * self.num_columns self.answer = [-1] * self.num_cells self.run() def run(self): self.construct_matrix() if self.dlx_search(0): self.compute_answer() print self.answer else: print 'no solution' def construct_matrix(self): self.root = Node() # construct column headers for i in xrange(self.num_columns): new_col_header = Node(left = self.root.left, right = self.root) self.root.left.right = new_col_header self.root.left = new_col_header new_col_header.col_header = i new_col_header.row_header = 0 self.col_headers.append(new_col_header) for i in xrange(self.num_cells): r = i // self.dimension c = i % self.dimension # negative(ie, -1) represents an empty cell if self.sudoku[i] < 0: # fill this empty cell with every possible value for value in xrange(self.dimension): self.insert_value(r, c, value) # positive indicates the cell is filled with a value else: self.insert_value(r, c, self.sudoku[i]) def insert_value(self, row, col, value): # constraint 1: each cell must have a number filled cell_idx = row * self.dimension + col + self.cell_offset # constraint 2: each row must contain each number exactly once row_idx = row * self.dimension + value + self.row_offset # constraint 3: each column must contain each number exactly once col_idx = col * self.dimension + value + self.col_offset # constraint 4: each box must contain each number exactly once box_idx = ((row // self.box_size) * self.box_size + (col // self.box_size)) * self.dimension + value + self.box_offset # map <row, col, #value> to the vertical index of the matrix vertical_row_idx = ((row * self.dimension + col) * self.dimension + value + self.cell_offset) self.add_row(vertical_row_idx, cell_idx, row_idx, col_idx, box_idx) def add_row(self, row, col1, col2, col3, col4): self.col_size[col1] += 1 col_header_node = self.col_headers[col1] # add new node to [row, col1] row_first_node = Node(up = col_header_node.up, down = col_header_node, col_header = col1, row_header = row) # place at vertical bottom of col col1 col_header_node.up.down = row_first_node col_header_node.up = row_first_node # add new nodes to the rest columns for col in [col2, col3, col4]: self.col_size[col] += 1 col_header_node = self.col_headers[col] new_node = Node(left = row_first_node.left, right = row_first_node, up = col_header_node.up, down = col_header_node, col_header = col, row_header = row) # place at horizontal tail of the row row_first_node.left.right = new_node row_first_node.left = new_node # place at vertical bottom of the column col_header_node.up.down = new_node col_header_node.up = new_node def dlx_search(self, level): # all columns have been removed, success if self.root.right == self.root: return True min_size = 0xfffffff j = self.root.right # choose the best column j with the least nodes in it while j != self.root: if self.col_size[j.col_header] < min_size: min_size = self.col_size[j.col_header] column_header = j j = j.right # cover this column self.cover(column_header) node_down = column_header.down # stop until node goes back to original (vertically) while node_down != column_header: # record the possible answer self.partial_answer[level] = node_down.row_header node_right = node_down.right # remove row, stop until node goes back to original (horizontally) while node_right != node_down: # cover this node's corresponding column self.cover(self.col_headers[node_right.col_header]) node_right = node_right.right # after previous removal, continue to next level if self.dlx_search(level + 1): return True # previous cover() failed, restore states node_left = node_down.left while node_left != node_down: self.uncover(self.col_headers[node_left.col_header]) node_left = node_left.left node_down = node_down.down # previous cover() failed, so restore original self.uncover(column_header) def cover(self, column_header): # remove this column column_header.left.right = column_header.right column_header.right.left = column_header.left node_down = column_header.down # move downwards vertically node by node, while node_down != column_header: node_right = node_down.right # remove this row vertically while node_right != node_down: node_right.down.up = node_right.up node_right.up.down = node_right.down # decreament the corresponding column size counter self.col_size[node_right.col_header] -= 1 # move to next node at the right side node_right = node_right.right # move to next node at the down side node_down = node_down.down def uncover(self, column_header): # restore this column column_header.right.left = column_header column_header.left.right = column_header node_up = column_header.up # move upwards vertically node by node while node_up != column_header: node_left = node_up.left while node_left != node_up: # restore this row vertically node_left.down.up = node_left node_left.up.down = node_left # increament the corresponding column size counter self.col_size[node_left.col_header] += 1 # move on the the left node node_left = node_left.left # move on to the up node node_up = node_up.up def compute_answer(self): # map the value of the choosed row index to cells' value for value_row_index in self.partial_answer: if value_row_index < 0: return t = value_row_index - self.cell_offset row = t // (self.dimension ** 2) col = (t // self.dimension) % self.dimension value = t % self.dimension self.answer[row * self.dimension + col] = value
測試程式test.py
import sudokusolver sudoku = [] solved = [] #s="000070508090008300510020000800000000045060980000000004000090056001400070407050000" s="081600090000000000004037600600400500030000070007002004005210300000000000070004810" for i in range(81): if s[i]!='0': sudoku.append(ord(s[i]) - ord('1')) else: sudoku.append(-1) print sudoku ss = sudokusolver.SudokuSolver(sudoku) for i in xrange(len(ss.answer)): solved.append(chr(ss.answer[i] + ord('1'))) print solved
這麼呼叫
\python27\python test.py
相關文章
- 一次數獨生成及解題演算法的剖析(Java實現)演算法Java
- 關於一個最簡單的數獨解題實現與疑惑一
- 讓我們一起啃演算法----有效的數獨演算法
- 用Python解決一個等差數列的求和問題Python
- 僅50行Python程式碼!數獨求解!4秒!Python
- 最難數獨的快速解法 - pythonPython
- 問一個 python 演算法題Python演算法
- 草根學Python(一)第一個Python程式Python
- Python求一個數的平方根Python
- Python求10個數的平均數實戰案例講解!Python
- LeetCode題解(1639):統計只差一個字元的子串數目(Python)LeetCode字元Python
- LeetCode 37. 解數獨LeetCode
- 演算法41. 缺失的第一個正數演算法
- python中一個數的平方怎麼表示Python
- Python基礎:第一個Python程式(2)Python
- mac下利用pyenv管理多個版本的pythonMacPython
- 一個小小的演算法題:求兩數之和演算法
- 獨一無二的出現次數
- 如何利用Python隨機從list中挑選一個元素Python隨機
- 【Python】用Python實現一個簡單的執行緒池模型效果程式碼分析講解Python執行緒模型
- 利用HashMap統計字元個數HashMap字元
- [work] python list中數字與一個數相乘Python
- python如何判斷一個數是否是整數Python
- uniapp js 數獨小遊戲 寫死的簡單數獨 數獨 3.0APPJS遊戲
- 利用python的KMeans和PCA包實現聚類演算法PythonPCA聚類演算法
- 雙指標演算法的一個簡單題解指標演算法
- JAVA開發環境搭建及變數配置(利用eclipse編寫第一個Java程式HelloWorld)Java開發環境變數Eclipse
- 【演算法題解】485. 最大連續1的個數 - Java演算法Java
- Wpf應用程式作為一個單獨的可執行檔案
- 《Space Engine》:一個人的孤獨宇宙
- python函數語言程式設計詳解Python函數程式設計
- Python產生20個隨機整數的方法詳解!Python隨機
- 一個數number的n次冪 python的pow函式Python函式
- python將輸入的一個正整數分解質因數(map)Python
- python函數語言程式設計一Python函數程式設計
- 利用 Numba 加速你的 Python 程式碼,使其變得像 C++ 一樣快PythonC++
- Python程式設計實現蟻群演算法詳解Python程式設計演算法
- 一個拖拉且錯誤的猜數字程式
- 演算法:利用分治演算法求解N個元素中的第M大元素演算法