一個利用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
相關文章
- 一個解數獨的程式
- 另一個數獨求解c++程式C++
- 又一個數獨求解c++程式C++
- 同樣演算法的Python和C求解數獨程式速度比較演算法Python
- python解數獨的簡單優化Python優化
- 一次數獨生成及解題演算法的剖析(Java實現)演算法Java
- 關於一個最簡單的數獨解題實現與疑惑一
- 用vue開發一個所謂的數獨Vue
- java解數獨Java
- 讓我們一起啃演算法----有效的數獨演算法
- 羅塞塔網站上的c++解數獨程式網站C++
- 據說用概率去解數獨的c++程式C++
- 僅50行Python程式碼!數獨求解!4秒!Python
- 最難數獨的快速解法 - pythonPython
- 改進版的python求解數獨Python
- 用Python解決一個等差數列的求和問題Python
- 利用SQL解釋一個魔術SQL
- 我目前找到的最快的數獨求解程式
- 問一個 python 演算法題Python演算法
- uniapp js 數獨小遊戲 寫死的簡單數獨 數獨 3.0APPJS遊戲
- 利用熱鍵控制滑鼠移動的一個程式 (轉)
- Python求一個數的平方根Python
- 第一個Python程式Python
- 演算法41. 缺失的第一個正數演算法
- Python求10個數的平均數實戰案例講解!Python
- 草根學Python(一)第一個Python程式Python
- LeetCode 37. 解數獨LeetCode
- 活下去,一個遊戲人的獨白遊戲
- 一個獨立開發者的失敗自白
- 如何利用Google成為一個更好的程式設計師Go程式設計師
- 利用Hadoop執行第一個程式,計算文章中不同單詞數量Hadoop
- python中一個數的平方怎麼表示Python
- 一個小小的演算法題:求兩數之和演算法
- 演算法學習-第一個缺失的正整數演算法
- 利用THOMAS KATE理論,成功解決一個案例
- 改善 Python 程式的 91 個建議(一)Python
- 一個簡單的python爬蟲程式Python爬蟲
- 我的數學之美(一)——RANSAC演算法詳解演算法