我尝试用Python编写的这个数独求解器https://freepythontips.wordpress.com/2013/09/01/sudoku-solver-in-python/代码工作正常。这是完整的代码:
import sys
def same_row(i,j): return (i/9 == j/9)
def same_col(i,j): return (i-j) % 9 == 0
def same_block(i,j): return (i/27 == j/27 and i%9/3 == j%9/3)
def r(a):
i = a.find('0')
if i == -1:
sys.exit(a)
excluded_numbers = set()
for j in range(81):
if same_row(i,j) or same_col(i,j) or same_block(i,j):
excluded_numbers.add(a[j])
for m in '123456789':
if m not in excluded_numbers:
r(a[:i]+m+a[i+1:])
r('100070030830600000002900608600004907090000050307500004203009100000002043040080009')
我不理解这个循环:
for m in '123456789':
if m not in excluded_numbers:
r(a[:i]+m+a[i+1:])
请有人解释此代码的算法。感谢
编辑: 我问的是那个循环的逻辑,现在我理解了逻辑,我检查了Peter Novig的数独求解器。
答案 0 :(得分:2)
在代码的开头,您将i
设置为尚未填充的第一个字段的索引。接下来,您将excluded_numbers
设置为此位置上不允许的所有数字,因为它们与i
已存在于同一行,列或块中。
在for
循环中,你问一下,你循环遍历所有可能的数字,猜测位置i
的数字为m
。首先检查m
是否不是禁止的数字之一,如果不是,则再次递归调用函数r
,将空点替换为guess m
。这是通过将所有字段连接到位置i
(a[:i]
),猜测m
以及位置i
之后的所有字段(a[i+1:]
)来完成的。在位置i
之前和之后获取部件由slicing完成到代表整个电路板的长字符串中。
函数r()
作为一个整体通过找到第一个未填充的位置,用有效数字填充它然后再次递归调用自身来工作。如果在某个时刻,没有更多的数字填充在某个位置(因为你做出了错误的猜测),函数就会退出。当函数最终找到完整的解决方案(没有0
)时,它会打印解决方案并退出。请注意,这不是解决数独的有效方法。一个更好的算法是explained by Peter Norvig,他是Google研究的负责人。
答案 1 :(得分:1)
def r(a):
i = a.find('0') # find the next "missing" item
if i == -1: # if there is none, we have a winner - print it on exit
sys.exit(a)
excluded_numbers = set() # build a set of excluded numbers, the range is [0,9] (though '0' is not meaningful)
for j in range(81): # we have 9 mini 3X3 boards which combined to one sudoku board
if same_row(i,j) or same_col(i,j) or same_block(i,j): # if i and j are on the same row/column/block - we'll exclude the item on a[j] because i cannot contain the same number - according to sudoku rules
excluded_numbers.add(a[j])
for m in '123456789': # for each one of the candidates m in [1,2,3,...9] (though it's being read as a string)
if m not in excluded_numbers: # if m was not excluded in the previous step
r(a[:i]+m+a[i+1:]) # replace the i-th item with m and continue (recursively)
在最后一行,我们有一个递归调用,试图使用未被排除的m
中的每一个来“猜测”解决方案。假设该板至少有一个解决方案 - 至少有一个调用应达到条件if i == -1
,并且第一个递归分支将到达那里将打印自己并退出(忽略其他可能的解决方案,“稍后”)所有其他可能性树上的一片叶子。)