Python递归深度超出限制超出,并且不知道如何删除递归

时间:2011-12-11 15:53:07

标签: python recursion limit

也许问题可以通过删除所有这些功能来解决,不是吗? 但是,我真的不知道如何让源工作。 顺便说一句,它只是模拟一匹马在一个chesstable,周围和周围,随机,试图访问每个广场一次;并且我的递归深度超出错误。

import random

def main():
    global tries,moves
    tries,moves=0,0
    restart()

def restart():
    global a,indexes,x,y
    a=[[0 for y in range(8)] for x in range(8)] #Costrutto chic
    indexes=[x for x in range(8)]
    #Random part
    x=random.randint(0,7)
    y=random.randint(0,7)
    a[x][y]=1
    start()

def start():
    global i,indexes,moves,tries
    i=0
    random.shuffle(indexes) #List filled with random numbers that i'll use as indexes
    while i<=7:
        if indexes[i]==0:
            move(-2,-1)
        elif indexes[i]==1:
            move(-2,1)
        elif indexes[i]==2:
            move(-1,-2)
        elif indexes[i]==3:
            move(-1,2)
        elif indexes[i]==4:
            move(1,-2)
        elif indexes[i]==5:
            move(1,2)
        elif indexes[i]==6:
            move(2,-1)
        elif indexes[i]==7:
            move(2,1)
        i+=1
    for _ in a:
        if 0 in _:
            print "Wasted moves: %d"%(moves)
            tries+=1
            moves=0
            restart()
    print "Success obtained in %d tries"%(tries)

def move(column,row):
    global x,y,a,moves
    try: b=a[x+row][y+column]
    except IndexError: return 0
    if b==0 and 0<=x+row<=7 and 0<=y+column<=7:
        x=x+row
        y=y+column
        a[x][y]=1
        moves+=1
        start()
    else: return 0

try :main()
#except: print "I couldn't handle it" <-Row added to prevent python from returning a huge amount of errors

编辑:这是修改后的版本,但仍然不起作用,但至少它是一个改进:

import random

def move((column,row),x,y):
    try: cell=squares_visited[x+row][y+column]
    except IndexError: return x,y ## NONE TYPE OBJECT
    if cell==0 and 0<=x+row<=7 and 0<=y+column<=7:
        x+=row
        y+=column
        squares_visited[x][y]=1
        return x,y ## NONE TYPE OBJECT

squares_visited=[[0] * 8 for _ in range(8)]
x=random.randint(0,7)
y=random.randint(0,7)
squares_visited[x][y]=1
moves=[(-2,-1),(-2,1),(-1,-2),(-1,2),(1,-2),(1,2),(2,-1),(2,1)]
indexes=list(range(8))
tries=0
print "The horse starts in position %d,%d"%(x,y)

while True:
    random.shuffle(indexes)
    for _ in indexes:
        cells=move(moves[indexes[_]],x,y) ##Passing as arguments x and y looks weird
        x=cells[0]
        y=cells[1]
    #If you out this for cicle, there are no legal moves anymore(due to full completion with 1, or to lack of legit moves)
    for _ in squares_visited:
        if 0 in _:
            squares_visited=[[0] * 8 for _ in range(8)]
            tries+=1
    else:
        print "You managed to do it in %d tries."%(tries)

2 个答案:

答案 0 :(得分:8)

这段代码存在很多问题 - 足以让它完全重复:

import random

def main():
    global tries,moves

过度使用全局变量的许多例子中的第一个。如果可能,传递参数;或创建一个类。这是一个通用策略,可以帮助您构建更易于理解(因此更可调试)的算法;从一般意义上讲,这是您的代码失败的部分原因 - 不是因为任何特定的错误,而是因为代码的复杂性使得很难找到错误。

    tries,moves=0,0
    restart()

def restart():
    global a,indexes,x,y

为什么要为电路板a命名?这是一个可怕的名字!使用像squares_visited这样的描述性内容。

    a=[[0 for y in range(8)] for x in range(8)] #Costrutto chic
    indexes=[x for x in range(8)]

Minor:在python 2中,[x for x in range(8)] == range(8) - 它们完全相同,所以列表理解是不必要的。在3中,它的工作方式略有不同,但如果您想要列表(而不是range对象),只需将其传递给list,如(list(range(8)) )。

    #Random part
    x=random.randint(0,7)
    y=random.randint(0,7)
    a[x][y]=1
    start()

因此,到目前为止,我对代码的理解是a是主板,xy是起始坐标,并且您已使用{标记了第一个访问过的地点{1}}。到现在为止还挺好。但事情开始变得毛茸茸,因为你在1的末尾调用start而不是从顶级控制函数调用它。这在理论上是可以的,但它使递归比必要的更复杂;这是你问题的另一部分。

restart

更多全局......

def start():
    global i,indexes,moves,tries

好的,所以你要做的是按顺序遍历 i=0 random.shuffle(indexes) #List filled with random numbers that i'll use as indexes while i<=7: if indexes[i]==0: move(-2,-1) elif indexes[i]==1: move(-2,1) elif indexes[i]==2: move(-1,-2) elif indexes[i]==3: move(-1,2) elif indexes[i]==4: move(1,-2) elif indexes[i]==5: move(1,2) elif indexes[i]==6: move(2,-1) elif indexes[i]==7: move(2,1) i+=1 中的每个索引。你为什么要使用indexes?为什么while全局?我不认为它在其他任何地方使用过。这样做过于复杂。只需使用i循环直接迭代for,就像在

中一样
indexes

好的,现在针对具体问题......

    for index in indexes: 
        if index==0:
            ...

我不明白你在这里要做什么。每当您在主板上找到 for _ in a: if 0 in _: print "Wasted moves: %d"%(moves) tries+=1 moves=0 restart() print "Success obtained in %d tries"%(tries) (即未访问的位置)时,您似乎正在呼叫restart。但0将所有电路板值重置为restart,因此除非在达到此点之前还有其他方法用0 s填充电路板,否则将导致无限递归。事实上,1move 之间的相互递归可能原则上能够实现这一点,但实际上,它太复杂了!问题是没有明确的递归终止条件。

start

原则上,这个想法似乎是如果你的移动命中def move(column,row): global x,y,a,moves try: b=a[x+row][y+column] except IndexError: return 0 if b==0 and 0<=x+row<=7 and 0<=y+column<=7: x=x+row y=y+column a[x][y]=1 moves+=1 start() else: return 0 或者离开棋盘,那么递归的当前分支就会终止。但由于1iindexes之上是全局的,因此重新调用start时,会重新调整start并重置indexes到0!结果是纯粹的混乱!我甚至无法理解这将如何影响递归;似乎有可能因为i每次都在i开头重置,并且因为start的每次成功通话都会调用move,{{1}开始循环永远不会终止!

我认为有可能最终这个过程将设法访问每个方块,此时事情可能会按预期工作,但实际上,即使预测也太复杂了。

start

不确定你的最后一行是什么意思,但听起来并不是一个好兆头 - 你是在报错而不是找到根本原因。

我将稍微使用这段代码,看看我是否可以通过对其部分状态进行去全球化来使其表现得更好......将很快报告。

<强>更新

好的,如上所述,我将while去全球化了。然后,我使用try :main() #except: print "I couldn't handle it" <-Row added to prevent python from returning a huge amount of errors 中的无限循环替换了indexes / start递归,restart中的restart语句,其中使用了对return的调用在start结尾处有一个restart(以便在成功时突破无限循环)。结果表现得更像预期。这仍然是糟糕的设计,但它现在有效,因为它递归地尝试一堆随机路径,直到每个本地位置都被访问过。

当然它仍然没有成功;它只是保持循环。实际上找到一个解决方案可能需要更多地重新思考这个算法!但遵循我的上述建议至少可以帮助一些人。

答案 1 :(得分:3)

start()move()互相调用,使其成为间接递归调用但move() return启动退出move()而不是递归

当你调用一个函数时,你会看到一个调用一个函数的函数,它会进入一个引用每个调用的堆栈。当你得到你的最终结果时,你应该向后退,然后取消堆叠这些函数调用。

在这里,你没有,你调用move(),调用start(),它会返回一些东西然后你继续深入堆栈。

尝试制作代码的迭代版本。递归很难,从容易开始。

如果你想坚持递归版本,请让move()自己调用,然后一旦达到out条件就从它自己向后移动。它比处理来自两个函数的递归调用更清楚。

顺便说一句:

  • 避免使用全局变量。将数据作为参数传递,或使用类。我会使用参数传递,它会迫使你想出一个更好的算法。
  • while循环不是必需的。将其替换为索引上的for循环
  • 不需要巨大的if/elif语句,将其替换为字典

你最终会得到这样的东西:

for i in indexes:
    move(*MOVES[i])

MOVES,是与i的参数相关联的move()值的词汇。

  • 您可能希望使用生成器而不是列表推导,但这需要一些算法更改。它可能对你的内存占用更好。至少,缩短:

[x for x in range(8)]可以写成range(8) [[0 for y in range(8)] for x in range(8)]可以写成[[0] * 8 for x in range(8)]

range()可以替换为xrange()