繁体   English   中英

计算函数的递归调用

[英]Counting recursive calls of a function

我有用于生成加泰罗尼亚数字的递归代码。

我设法编写了递归调用,但由于某种原因,计数器无法正常工作。

例如,第 7 个加泰罗尼亚号码的呼叫次数应为 1215。

返回值需要是加泰罗尼亚数字和调用次数的元组,例如:(429,1215)。

原始代码:

def catalan_rec(n):
    if n<=1:
        return 1
    res=0
    for i in range(n):
        res+=catalan_rec(i)*catalan_rec(n-i-1)
    return res

计数器代码:

def catalan_rec_count(n,counter=1):
    if n<=1:
        return 1
    res=0
    for i in range(n):
        res+=catalan_rec_count(i,counter+1)*catalan_rec_count(n-i-1,counter+1)        
    return (res,counter)

python 允许您将变量(下面代码段中的catalan.counter )附加到函数对象,因此您不必一直传递计数器,也不需要全局变量:

def catalan(n):

    catalan.counter += 1

    if n <= 1:
        return 1
    res = 0
    for i in range(n):
        res += catalan(i) * catalan(n-i-1)
    return res

catalan.counter = 0

print(catalan(5))
print(catalan.counter)

并看到该函数使用相同的参数多次调用:为了提高效率,您可以使用lru_cache 但这当然违背了计算函数被调用次数的目的; 您只会获得使用唯一n调用函数的编号。

from functools import lru_cache

@lru_cache(maxsize=128)
def catalan(n):

    ...

这可能有点离题......但如果您需要具有单独计数器的函数的单独实例,则闭包可能正是您所需要的:

def make_catalan():

    counter = 0

    def catalan(n):

        nonlocal counter
        counter += 1
        catalan.counter = counter

        if n <= 1:
            return 1
        res = 0
        for i in range(n):
            res += catalan(i) * catalan(n-i-1)
        return res

    return catalan

catalan_1 = make_catalan()
print(catalan_1(2))
print(catalan_1.counter)

catalan_2 = make_catalan()
print(catalan_2(3))
print(catalan_2.counter)

您需要分开行res+=catalan_rec_count(i,counter+1)*catalan_rec_count(ni-1,counter+1)以便它可以分别对递归结果和计数器进行操作,所以只需将其拆分为几行额外的行,在这种情况下,您也不会将counter+1传递给递归调用,以便它跟踪与当前帧无关的调用。

def catalan_rec_count(n,counter=1):
    if n<=1:
        return (1, counter) #remember to return the counter in this case too!
    res=0
    for i in range(n):
        #get the recursive results and counters for both calls
        #don't pass counter+1 to it, it should count how many times it is called on it's own
        partial1, inner_c1 = catalan_rec_count(i)
        partial2, inner_c2 = catalan_rec_count(n-i-1)
        #apply the logic with the actual result and add to the counter
        res+=partial1*partial2
        counter+= inner_c1 + inner_c2
    return (res,counter)

您可以为要跟踪调用的函数编写装饰器。 当装饰器函数被调用时,它会增加其内部调用计数,将参数传递给被跟踪的函数,并将装饰函数的结果返回给它的调用者。

这样,无需对函数的内部结构、参数或返回类型进行任何调整或使用全局变量——只需装饰和滚动即可。

import functools


def count_calls(fn):
    @functools.wraps(fn)
    def wrapper(*args, **kwargs):
        wrapper.calls += 1
        return fn(*args, **kwargs)

    wrapper.calls = 0
    return wrapper


if __name__ == "__main__":

    @count_calls
    def catalan_rec(n):
        if n <= 1:
            return 1
        res = 0
        for i in range(n):
            res += catalan_rec(i) * catalan_rec(n - i - 1)
        return res

    print(catalan_rec(n=7))  # => 429
    print(catalan_rec.calls)  # => 1215

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM