0

四年前,我写了一个数独解谜程序,现在我试图了解它是如何工作的,以便我可以将其中的一部分重用于 KenKen 解谜程序。我想我最好将循环压缩成列表理解,并为变量选择更多不言自明的名称。

有一个类 Puz 包含作为(81)位数字列表的输入谜题;1 到 9 表示单元格的值已知,0 表示不知道。

Puz 类还包含拼图的工作版本,只是这里列表中的 (81) 个项目都是一个集合;如果一个单元格的答案已知,则该集合包含一个从 1 到 9 的值,例如 set([4]),如果答案未知,则该集合包含剩余的可能性,例如 set([3,5 ,7,9])。当调用 Puz._init__(self, puz) 时,工作列表中的那些“可能”集合被设置为 set([1,2,3,4,5,6,7,8,9]),第一个获得解决方案的步骤是删除在该单元格的行、列和 3x3 块中显示为答案的所有值。

最初,工作列表是使用for循环填充的:对于0到80,如果是答案,则将答案作为集合放入,否则放入集合(范围(1, 10))。我不知道如何将这种条件转换为列表理解,所以我将其分解为一个单独的“填充函数”,下面显示了其中的 3 个版本。fill_funcs 的不同之处在于它们的“非答案分支”:

return set( range( 1,(self.dim+1)))
return set( self.r_dim_1_based)
return self.set_dim_1_based

如您所见,越来越多的处理被移到函数之外,回到初始化小变量的地方。

问题是,前两个变体进入了数独求解器,并且完全按照原始代码的方式工作。但是 --- 第三个变体中断,说工作列表中的第 6 组是(或变为)空的。YET --- 所有三个变体产生的集合列表评估为相等:

p.W1 == p.W2 == p.W3 --> 真

我难住了。

下面是一些用于制作集合列表的代码:

#!/usr/bin/env python
import copy
from math import sqrt

'''
Puzzle #15, from The Guardian: 050624: #41: rated difficult
'''
puz = [
0,0,1, 9,0,0, 3,0,0,
0,0,0, 0,0,0, 2,0,0,
7,6,0, 0,2,0, 0,0,9,

3,0,0, 0,6,0, 0,0,5,
0,0,2, 1,0,3, 4,0,0,
4,0,0, 0,9,0, 0,0,3,

1,0,0, 0,3,0, 0,9,7,
0,0,4, 0,0,0, 0,0,0,
0,0,5, 0,0,8, 6,0,0
]

class GroupInfo: pass

class Puz(GroupInfo):
    def __init__(self, puz):
        self.A =   copy.deepcopy(puz)
        self.ncells =   len( self.A)
        self.r_ncells = range( 0,self.ncells)
        self.dim =      int( sqrt( self.ncells))
        assert (self.dim ** 2) == self.ncells, "puz is not square"
        self.r_dim_0_based = range( 0,self.dim)
        self.r_dim_1_based = range( 1, self.dim + 1)
        self.set_dim_1_based =  set( self.r_dim_1_based) ## <<---- causes to fail!
        ##### with 'empty set at W[5]' !?!?!?

        def W1_fill_func( val):
            if (val == 0):
                return set( range( 1,(self.dim+1)))
            else:
                return set( [val])

        self.W1 = [ W1_fill_func( self.A[cid])  
                   for cid in self.r_ncells ]

        def W2_fill_func( val):
            if (val == 0):
                return set( self.r_dim_1_based)
            else:
                return set( [val])

        self.W2 = [ W2_fill_func( self.A[cid])  
                   for cid in self.r_ncells ]

        def W3_fill_func( val):
            if (val == 0):
                return self.set_dim_1_based
            else:
                return set( [val])

        self.W3 = [ W3_fill_func( self.A[cid])  
                   for cid in self.r_ncells ]

        return
    #def Puz.__init__()
#class Puz

p = Puz(puz)
print p.W1 == p.W2 == p.W3
4

1 回答 1

2

self.W3 在您编写代码时,它包含对同一集合对象的许多引用——只要您在其中一个引用上调用任何变异方法,您就更改了所有其他引用。您需要确保W3_fill_func返回感兴趣集的独立副本,就像所有其他人一样,例如通过将其返回更改为return set(self.set_dim_1_based).

于 2009-11-02T01:37:03.387 回答