【问题标题】:Infinite loop and recursion in PythonPython中的无限循环和递归
【发布时间】:2012-10-27 20:24:35
【问题描述】:

我正在实施迭代深化深度优先搜索,以找到8 puzzle problem 的解决方案。我对自己找到实际的搜索路径不感兴趣,而只是计算程序运行需要多长时间。 (我还没有实现定时功能)。

但是,我在尝试实现实际搜索功能时遇到了一些问题(向下滚动查看)。我粘贴了到目前为止的所有代码,所以如果你复制并粘贴它,你也可以运行它。这可能是描述我遇到的问题的最佳方式......我只是不明白为什么我在递归期间会出现无限循环,例如在拼图 2 (p2) 的测试中,第一次展开应该产生一个解决方案。我认为这可能与未在其中一行代码前面添加“Return”有关(下面对此进行了评论)。当我添加返回时,我可以通过拼图 2 的测试,但是像拼图 3 这样更复杂的东西失败了,因为现在代码似乎只扩展了最左边的分支......

已经在这几个小时,放弃了希望。如果您能指出我的错误,我将非常感谢您对此有另一种看法。谢谢!

#Classic 8 puzzle game
#Data Structure: [0,1,2,3,4,5,6,7,8], which is the goal state. 0 represents the blank
#We also want to ignore "backward" moves (reversing the previous action)

p1 = [0,1,2,3,4,5,6,7,8]
p2 = [3,1,2,0,4,5,6,7,8]
p3 = [3,1,2,4,5,8,6,0,7]

def z(p):   #returns the location of the blank cell, which is represented by 0
    return p.index(0)

def left(p):
    zeroLoc = z(p)
    p[zeroLoc] = p[zeroLoc-1]
    p[zeroLoc-1] = 0
    return p

def up(p):
    zeroLoc = z(p)
    p[zeroLoc] = p[zeroLoc-3]
    p[zeroLoc-3] = 0
    return p

def right(p):
    zeroLoc = z(p)
    p[zeroLoc] = p[zeroLoc+1]
    p[zeroLoc+1] = 0
    return p

def down(p):
    zeroLoc = z(p)
    p[zeroLoc] = p[zeroLoc+3]
    p[zeroLoc+3] = 0
    return p

def expand1(p):   #version 1, which generates all successors at once by copying parent
    x = z(p)
    #p[:] will make a copy of parent puzzle
    s = []  #set s of successors

    if x == 0:
        s.append(right(p[:]))
        s.append(down(p[:]))
    elif x == 1:
        s.append(left(p[:]))
        s.append(right(p[:]))
        s.append(down(p[:]))
    elif x == 2:
        s.append(left(p[:]))
        s.append(down(p[:]))
    elif x == 3:
        s.append(up(p[:]))
        s.append(right(p[:]))
        s.append(down(p[:]))
    elif x == 4:
        s.append(left(p[:]))
        s.append(up(p[:]))
        s.append(right(p[:]))
        s.append(down(p[:]))
    elif x == 5:
        s.append(left(p[:]))
        s.append(up(p[:]))
        s.append(down(p[:]))   
    elif x == 6:
        s.append(up(p[:]))
        s.append(right(p[:]))
    elif x == 7:
        s.append(left(p[:]))
        s.append(up(p[:]))
        s.append(right(p[:]))
    else:   #x == 8
        s.append(left(p[:]))
        s.append(up(p[:]))

    #returns set of all possible successors
    return s

goal = [0,1,2,3,4,5,6,7,8]

def DFS(root, goal):    #iterative deepening DFS
    limit = 0
    while True:
        result = DLS(root, goal, limit)
        if result == goal:
            return result
        limit = limit + 1

visited = []

def DLS(node, goal, limit):    #limited DFS
    if limit == 0 and node == goal:
        print "hi"
        return node
    elif limit > 0:
        visited.append(node)
        children = [x for x in expand1(node) if x not in visited]
        print "\n limit =", limit, "---",children   #for testing purposes only
        for child in children:
            DLS(child, goal, limit - 1)     #if I add "return" in front of this line, p2 passes the test below, but p3 will fail (only the leftmost branch of the tree is getting expanded...)
    else:
        return "No Solution"

#Below are tests

print "\ninput: ",p1
print "output: ",DFS(p1, goal)

print "\ninput: ",p2
print "output: ",DLS(p2, goal, 1)
#print "output: ",DFS(p2, goal)

print "\ninput: ",p3
print "output: ",DLS(p3, goal, 2)
#print "output: ",DFS(p2, goal)

【问题讨论】:

  • 在过去的 2 天里,关于 8-puzzles 的其他问题不少于 3 个。试试the search。尤其是Ashwin's 的问题似乎和你的很相似。
  • 请注意,python 的递归深度非常有限。所以明智地使用递归
  • 有人可以解释为什么(或指出我正确的方向)为什么在 p2 的 DLS 函数中没有返回节点,即使满足 if 语句?但是,节点 IS 在 p1 中返回(其中输入只是解决方案)。

标签: python depth-first-search iterative-deepening sliding-tile-puzzle


【解决方案1】:

您在递归中遇到的直接问题是,当您执行递归步骤时,您没有返回任何内容。但是,无条件地从第一个递归调用返回值也不起作用,因为不能保证第一个孩子是找到解决方案的那个。相反,您需要测试以查看您在子状态上执行的哪些递归搜索(如果有)是成功的。以下是我将如何更改 DLS 函数的结尾:

    for child in children:
        child_result = DLS(child, goal, limit - 1)
        if child_result != "No Solution":
            return child_result

# note, "else" removed here, so you can fall through to the return from above
return "No Solution"

一种更“pythonic”(更快)的方法是使用None 作为标记值而不是“No Solution”字符串。那么您的测试将只是 if child_result: return child_result 并且您可以选择省略失败搜索的返回语句(因为 None 是函数的默认返回值)。

修复此递归问题后,您的代码还会遇到一些其他问题。例如,使用全局 visited 变量是有问题的,除非您在每次重新启动另一个递归搜索时重置它。但我会把这些留给你!

【讨论】:

  • 感谢您花时间详细解释为什么这种方式是正确的!
【解决方案2】:

为您的州使用课程!这应该使事情变得容易得多。让你开始。现在不想发布整个解决方案,但这会让事情变得更容易。

#example usage
cur = initialPuzzle
for k in range(0,5): # for 5 iterations. this will cycle through, so there is some coding to do
    allsucc = cur.succ() # get all successors as puzzle instances
    cur = allsucc[0] # expand first                                                                                           
    print 'expand ',cur 

import copy


class puzzle:

    '''
    orientation
    [0, 1, 2
     3, 4, 5
     6, 7, 8]
    '''

    def __init__(self,p):
        self.p = p

    def z(self):  
        ''' returns the location of the blank cell, which is represented by 0 '''
        return self.p.index(0)

    def swap(self,a,b):
        self.p[a] = self.p[b]
        self.p[b] = 0

    def left(self):
        self.swap(self.z(),self.z()+1) #FIXME: raise exception if not allowed

    def up(self):
        self.swap(self.z(),self.z()+3)

    def right(self):
        self.swap(self.z(),self.z()-1)

    def down(self):
        self.swap(self.z(),self.z()-3)

    def __str__(self):
        return str(self.p)

    def copyApply(self,func):
        cpy = self.copy()
        func(cpy)
        return cpy

    def makeCopies(self,s):
        ''' some bookkeeping '''
        flist = list()
        if 'U' in s:
            flist.append(self.copyApply(puzzle.up))
        if 'L' in s:
            flist.append(self.copyApply(puzzle.left))
        if 'R' in s:
            flist.append(self.copyApply(puzzle.right))
        if 'D' in s:
            flist.append(self.copyApply(puzzle.down))

        return flist

    def succ(self):
        # return all successor states for this puzzle state
        # short hand of allowed success states
        m = ['UL','ULR','UR','UDR','ULRD','UDL','DL','LRD','DR']
        ss= self.makeCopies(m[self.z()]) # map them to copies of puzzles
        return ss


    def copy(self):
        return copy.deepcopy(self)



# some initial state
p1 = [0,1,2,3,4,5,6,7,8]

print '*'*20
pz = puzzle(p1)
print pz

a,b = pz.succ()
print a,b

【讨论】:

    猜你喜欢
    • 2014-10-13
    • 2016-10-06
    • 1970-01-01
    • 1970-01-01
    • 2021-12-06
    • 1970-01-01
    • 2012-10-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多