表面上相等的集合列表在 Python 2.5 下表现不同的情况(我认为......)

发布于 2024-08-09 20:17:28 字数 2682 浏览 2 评论 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,如果是答案,则将答案作为集合放入,否则放入set(range(1, 10))。我不知道如何将这种条件式转化为列表理解,因此我将其分解为一个单独的“填充函数”,其中有 3 个版本如下所示。 fill_func 的不同之处在于它们的“非答案分支”:

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

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

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

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

Four years ago I wrote a Sudoku puzzle solver, and now I'm trying to understand how it works so that I can reuse parts of it for a KenKen puzzle solver. I thought I'd better compactify loops into list comprehensions and pick more self-explanatory names for variables.

There's a class Puz which contains the input puzzle as a list of (81) digits; 1 through 9 where the cell's value is known, and 0 where it is not.

Class Puz also contains a working version of the puzzle, except that here each of the (81) items in the list is a set; where the answer for a cell is known, the set contains one value from 1 to 9, e.g., set([4]), and where the answer is unknown, the set contains the remaining possibilities, e.g., set([3,5,7,9]).
When Puz._init__(self, puz) is called, those "maybe" sets in the work list are set to set([1,2,3,4,5,6,7,8,9]), and the first step in getting to a solution is to strike out all the values which appear as answers in that cell's row, column, and 3x3 block.

Originally, the work list was filled in using a for loop: for 0 to 80, if it's an answer, put in the answer as a set, else put in set( range( 1, 10)). I couldn't figure out how to get this kind of conditional into a list comprehension, so I broke it out into a separate "filling function", of which 3 versions are shown below. The fill_funcs differ in their "not-an-answer branches":

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

As you see, increasing amounts of processing are moved outside the function back to where the little variables are initialized.

The trouble is, the first two variations slip into the Sudoku solver and work exactly the way the original code did. BUT --- the third variation breaks, saying that the 6th set in the working list is (or becomes) empty. YET --- the lists of sets produced by all three variations evaluate as equal:

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

I'm stumped.

Here's some code for making the lists of sets:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

哑剧 2024-08-16 20:17:28

正如您编码的那样, self.W3 包含对同一集合对象的许多引用 - 一旦您对其中一个引用调用任何变异方法,您就更改了所有其他引用。您需要确保 W3_fill_func 返回感兴趣的集合的独立副本,就像所有其他操作一样,例如将其返回更改为 返回集合(self.set_dim_1_based)

self.W3 as you've coded it contains many references to the same set object -- as soon as you call any mutating method on one of those references, you've changed all the others. You need to ensure W3_fill_func returns independent copies of the set of interest, just like all others do, e.g. by changing its return to return set(self.set_dim_1_based).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文