完成数独的算法 python_一个利用DLX算法的Python解数独程序

本文介绍了一个Python程序,它使用 Dancing Links(DLX)算法来解决数独谜题。程序首先定义了节点类和数独求解器类,然后通过构建矩阵并进行DLX搜索找到解决方案。如果找到解决方案,程序会输出答案,否则提示无解。通过运行测试程序,可以将给定的数独输入转化为数字列表,并使用数独求解器得出解答。
摘要由CSDN通过智能技术生成

源程序sudokusolver.py

#!/usr/bin/env python

# Copyright (C) 2012 Daogan Ao

# 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 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值