【问题标题】:How can I count the recursive calls of a function in Python?如何计算 Python 中函数的递归调用?
【发布时间】:2017-10-22 09:32:12
【问题描述】:

我在玩递归阿克曼函数。对于某些值,我的提示不会显示每个计算的输出,因为 Python 会如此快地超过其递归限制,以至于在“简单”部分赶上它之前冻结提示。

所以我想我可以在函数完全执行后添加一个递归计数器和一个快速暂停。我得到了预期的输出,直到它达到值 (1,0)。之后我得到了TypeError: can only concatenate tuple (not "int") to tuple

我的代码如下:

import time
import sys
sys.setrecursionlimit(3000)

def ackermann(i,j,rec):
    output = None
    if i==0:
        output = j+1
    elif j==0:
        output = ackermann(i-1,1,rec)
        rec=rec+1
    else:
        output = ackermann(i-1,ackermann(i,j-1,rec),rec)
        rec=rec+1
    return output,rec

rec=0
for i in range(5):
    for j in range(5):
        print("(",i,",",j,")= ",ackermann(i,j,rec))
        time.sleep(2)

请注意,删除 rec(我的递归计数器)的所有实例后,程序运行良好。 (您可以看到值 i,j = 3 的所有输出)

有人可以指出如何更正我的代码或提出一种不同的方法来查找 Ackermann 函数调用自身的次数吗?

另外,我注意到将限制设置为 5000 会使我的 python 内核崩溃得非常快。有上限吗?

我使用最新的 Anaconda。

编辑

我尝试使用列表作为参数来实现相同的功能,并带有以下数据[i,j,output,#recursion]

import time
import sys
sys.setrecursionlimit(3000)

def ackermann(*rec):
    rec=list(rec)
    print(rec) # see the data as they initialize the function
    if rec[0][0]==0:
        rec[0][1]=rec[0][1]+1
        rec[0][2] = rec[0][1]+1
    elif rec[0][1]==0:
        rec[0][0]=rec[0][0]-1
        rec[0][1]=1
        rec = ackermann()
        rec[0][3]=rec[0][3]+1
    else:
        rec[0][0]=rec[0][0]-1
        rec[0][1] = ackermann()
        rec = ackermann()
        rec[0][3]=rec[0][3]+1
    return rec

for i in range(5):
    for j in range(5):
        rec=[i,j,0,0]
        print(ackermann(rec))
        time.sleep(1)

但这次我收到了IndexError: list index out of range,因为不知什么原因,我的列表被清空了

输出:

[[0, 0, 0, 0]]
[[0, 1, 2, 0]]
[[0, 1, 0, 0]]
[[0, 2, 3, 0]]
[[0, 2, 0, 0]]
[[0, 3, 4, 0]]
[[0, 3, 0, 0]]
[[0, 4, 5, 0]]
[[0, 4, 0, 0]]
[[0, 5, 6, 0]]
[[1, 0, 0, 0]]
[]

【问题讨论】:

    标签: python-3.x recursion ackermann


    【解决方案1】:

    原始实现的问题在于 返回输出,rec 当 output 和 rec 都是数字时,会很高兴地创建一个元组,只要 i=0 就成立。但是一旦达到 i=1, j=0,该函数就会在 (0,1,rec) 上调用 Ackerman,它会返回一个元组,然后它不能将整数 rec 添加到该元组中,因此会出现错误消息。我相信我已经采用了这个想法,但几乎没有改变,除了尝试传递和返回rec,我把它变成了全局的(丑陋,我知道)。我还重新格式化了输出,以便更好地阅读它。因此:

    import time
    import sys
    sys.setrecursionlimit(3000)
    def ackermann(i,j):
        global rec
        output = None
        if i==0:
            output = j+1
        elif j==0:
            output = ackermann(i-1,1)
            rec=rec+1
        else:
            output = ackermann(i-1,ackermann(i,j-1))
            rec=rec+1
        return output
    
    
    for i in range(5):
        for j in range(5):
            rec = 0
            print
            print("ack("+str(i)+","+str(j)+") = "+str(ackermann(i,j)))
            print("rec = "+str(rec))
            print
            time.sleep(1)
    

    在出错之前的输出是,

    ack(0,0) = 1
    rec = 0
    
    
    ack(0,1) = 2
    rec = 0
    
    
    ack(0,2) = 3
    rec = 0
    
    
    ack(0,3) = 4
    rec = 0
    
    
    ack(0,4) = 5
    rec = 0
    
    
    ack(1,0) = 2
    rec = 1
    
    
    ack(1,1) = 3
    rec = 2
    
    
    ack(1,2) = 4
    rec = 3
    
    
    ack(1,3) = 5
    rec = 4
    
    
    ack(1,4) = 6
    rec = 5
    
    
    ack(2,0) = 3
    rec = 3
    
    
    ack(2,1) = 5
    rec = 8
    
    
    ack(2,2) = 7
    rec = 15
    
    
    ack(2,3) = 9
    rec = 24
    
    
    ack(2,4) = 11
    rec = 35
    
    
    ack(3,0) = 5
    rec = 9
    
    
    ack(3,1) = 13
    rec = 58
    
    
    ack(3,2) = 29
    rec = 283
    
    
    ack(3,3) = 61
    rec = 1244
    
    
    ack(3,4) = 125
    rec = 5213
    
    
    ack(4,0) = 13
    rec = 59
    

    在我看来,只有一两个其他值(我相信无论如何都会阻塞 4,2,所以你需要先获得 5、0)你可能希望以这种方式退出,不管你怎么修补。

    我有点担心 rec 似乎超出了递归限制,但我认为 Python 必须以某种方式进行解释,以便它比人们想象的更深入,或者我不完全理解 sys.recursionlimit (我看了几次 rec ,至少我按照你的方法计算它;另外,作为健全性检查,我切换了递增它的顺序和函数调用,得到了相同的结果)。

    编辑:我添加了另一个参数来跟踪任何特定调用的递归深度。事实证明,这通常小于(并且最多大于)“rec”。 rec 表示(实际上小于 1)调用函数进行特定计算的次数,但并非所有这些都需要同时在 Python 解释器堆栈上。

    修改后的代码:

    import time
    import sys
    sys.setrecursionlimit(3000)
    def ackermann(i,j,d):
        global rec
        global maxDepth
        if ( d  > maxDepth ) : maxDepth = d
        output = None
        if i==0:
            output = j+1
        elif j==0:
            rec=rec+1
            output = ackermann(i-1,1, d+1)
        else:
            rec=rec+1
            output = ackermann(i-1,ackermann(i,j-1, d+1),d+1)
        return output
    
    
    for i in range(5):
        for j in range(5):
            rec = 0
            maxDepth=0
            print
            print("ack("+str(i)+","+str(j)+") = "+str(ackermann(i,j,1)))
            print("rec = "+str(rec))
            print("maxDepth = "+str(maxDepth))
            print
            time.sleep(1)
    

    修改后的输出(在它放弃之前)

    ack(0,0) = 1
    rec = 0
    maxDepth = 1
    
    
    ack(0,1) = 2
    rec = 0
    maxDepth = 1
    
    
    ack(0,2) = 3
    rec = 0
    maxDepth = 1
    
    
    ack(0,3) = 4
    rec = 0
    maxDepth = 1
    
    
    ack(0,4) = 5
    rec = 0
    maxDepth = 1
    
    
    ack(1,0) = 2
    rec = 1
    maxDepth = 2
    
    
    ack(1,1) = 3
    rec = 2
    maxDepth = 3
    
    
    ack(1,2) = 4
    rec = 3
    maxDepth = 4
    
    
    ack(1,3) = 5
    rec = 4
    maxDepth = 5
    
    
    ack(1,4) = 6
    rec = 5
    maxDepth = 6
    
    
    ack(2,0) = 3
    rec = 3
    maxDepth = 4
    
    
    ack(2,1) = 5
    rec = 8
    maxDepth = 6
    
    
    ack(2,2) = 7
    rec = 15
    maxDepth = 8
    
    
    ack(2,3) = 9
    rec = 24
    maxDepth = 10
    
    
    ack(2,4) = 11
    rec = 35
    maxDepth = 12
    
    
    ack(3,0) = 5
    rec = 9
    maxDepth = 7
    
    
    ack(3,1) = 13
    rec = 58
    maxDepth = 15
    
    
    ack(3,2) = 29
    rec = 283
    maxDepth = 31
    
    
    ack(3,3) = 61
    rec = 1244
    maxDepth = 63
    
    
    ack(3,4) = 125
    rec = 5213
    maxDepth = 127
    
    
    ack(4,0) = 13
    rec = 59
    maxDepth = 16
    

    【讨论】:

    • 谢谢,我没想过为了简单起见放一个全局变量。您是否尝试过增加最大重复限制?在我的代码中放置 5000 会在任何计算之前立即杀死 Python 核心。
    • 所以奇怪的是,即使我把它留在 3000 时,我用 5213 得到了上述结果。5000 并没有破坏我的,但它也没有让我得到 4,1。我想看看你是否得到相同的结果。
    • 我相信 5213 代表函数在计算 ack(3,4) 时被调用的次数,但并非所有这些都需要同时位于 Python 解释器堆栈上。该计算的递归深度最多为 127。
    【解决方案2】:

    在您编辑过的代码版本中,您在 ackerman 的 def 中使用了 *arg 并将其显式设为一个列表,您将获得 11 个输出列表,其中每个列表都包含一个四元素列表,直到第 12 次递归您得到一个空列表。那么,前 11 个列表是否包含根据阿克曼约束的预期元素?此外,在第 12 次递归中,您说列表已“清空”。出于分析目的,我想知道是否可以说它一开始就没有填写。也就是说,不是有什么东西清空了它,而是第十二次没有按预期填满它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-15
      • 2015-01-30
      相关资源
      最近更新 更多