Python 如何计算值在递归函数中递归的次数?

Python 如何计算值在递归函数中递归的次数?,python,recursion,combinatorics,Python,Recursion,Combinatorics,如果您有递归函数(例如斐波那契序列): 例如,当调用fib(20)时,如何计算fib(2)发生的次数 您可以使用字典统计所有调用的fib。在第一次调用fib之前,必须清除字典 calls = defaultdict(int) 在函数中,在执行任何其他操作之前更新字典中的相应条目: def fib(n): global calls """Assumes n an int >= 0 Returns Fibonacci of n""" calls[n] += 1 if

如果您有递归函数(例如斐波那契序列):


例如,当调用fib(20)时,如何计算fib(2)发生的次数

您可以使用字典统计所有调用的
fib
。在第一次调用
fib
之前,必须清除字典

calls = defaultdict(int)
在函数中,在执行任何其他操作之前更新字典中的相应条目:

def fib(n):
  global calls
  """Assumes n an int >= 0
     Returns Fibonacci of n"""
  calls[n] += 1
  if n == 0 or n == 1:
    return 1
  else:
    return fib(n-1) + fib(n-2)
那么:

def fib(n, counts_dict):
    counts_dict[n] += 1
    if n == 0 or n == 1:
        return 1
    else:
        return fib(n-1, counts_dict) + fib(n-2, counts_dict
其中
counts\u dict=collections.defaultdict(int)

如果我没有弄乱逻辑,这个函数接受
n
x
,并返回一个元组
(y,c)
s.t.
fib(n,u)=y
fib(x,u)
在计算期间被调用
c


这是一个更纯粹的替代方案,以取代其他涉及更新字典的解决方案。它基于这样一种假设,即OP只需要为一个特定的
k
调用
f(k,)
的次数,从而避免填充只需要一个值的字典。

您可以使用装饰器:

import functools

def count(f):
    """Count function calls."""
    @functools.wraps(f)
    def counted(*args, **kwargs):
        counted.count_calls += 1
        return f(*args, **kwargs)
    counted.count_calls = 0
    return counted

fib = count(fib)
fib(5)
fib.count_calls
# 15
或者,您现在可以使用此装饰符和
@
符号预先添加任何函数定义:

@count
def fib(n):
    ...

fib(5)
fib.count_calls
# 15
注意,此装饰器累积函数调用:

fib(5)
fib(5)
fib.count_calls
# 30

这是一个巧妙的实现,利用了鲜为人知的优势。注意,原来的decorator是从John DiNero的
count
函数修改而来的,他在他的函数中讨论了这个特定问题。

没有全局变量:

from collections import defaultdict
def fib(n, count=None):
    if count is None:
        count = defaultdict(int)
    count[n] += 1
    if n in (0, 1):
        return 1, count
    return fib(n - 1)[0] + fib(n - 2)[0], count

fib
函数现在返回一个元组:第一个元素是所需的值,第二个元素是一个字典,其中包含调用
fib
函数的每个值的次数信息。

这就是我尝试的。。。我觉得工作不错

def fib(n):
    global counter
    if (n == 0 or n == 1):
        counter=counter+1
        return 1
    else:
        counter=counter+1
        return fib(n-1) + fib(n-2)
def countfib(n):
    global counter
    counter = 0
    fib(5);
    global count
    count=counter
    counter = 0
    return count
counter=0
count=0
print fib(5)
count=countfib(5)
print count
输出:

8
十五


使用函数属性怎么样。就像一个静态变量

def fib(n): 
    """Return Fibonacci of n; assumes n is an int >= 0."""
    If hasattr(fib, 'cnt'):
        fib.cnt += 1
    else:
        fib.cnt = 1
    if n == 0 or n == 1: 
        return 1 
    else: 
        return fib(n-1) + fib(n-2)

fib.cnt =0

我不清楚要计算的循环值到底是多少,所以我猜是用同一个参数(或一组参数,如果有多个参数)调用(递归)函数的次数

在下面的代码中,使用名为
tally\u recurtive\u args()
的修饰符将函数包装在一些代码中以完成此操作。为了简化工作并避免重复使用,使用
collections.Counter
来统计函数的每个唯一参数组合的调用次数。这是函数的一个属性,因此它可以在
包装器中轻松地引用到对修饰函数的每次调用中

import collections
import functools

def tally_recurring_args(func):
    func._args_counter = collections.Counter()  @ add attribute to func

    @functools.wraps(func)
    def wrapper(*args):
        key = ', '.join(str(arg) for arg in args)
        func._args_counter[key] += 1
        return func(*args)

    return wrapper

@tally_recurring_args
def fib(n):
    """Return Fibonacci of n; assumes n is an int >= 0."""
    if n == 0 or n == 1:
        return 1
    else:
        return fib(n-1) + fib(n-2)

print('fib(5) -> {}'.format(fib(5)))
for args, count in sorted(fib._args_counter.items()):
    print('fib({}): called {} time{}'.format(args, count,
                                             's' if count > 1 else ''))
输出:

fib(5)->8
fib(0):调用3次
fib(1):调用5次
fib(2):调用3次
fib(3):调用2次
fib(4):调用1次
fib(5):调用1次
使用全局:

c = 0
def fib(n):
    global c
    c += 1
    if n == 1:
        return 0
    elif n == 2:
        return 1
    else:
        return fib(n-1) + fib(n-2)

这是另一个没有全局变量的解决方案,但通过值传递完成:

def fibo(n, c):
if n <= 2 and n > 0:
    return n-1, c
else:
    n1, c1 = fibo(n-1,c+1)
    n2, c2 = fibo(n-2,1)
    return n1+n2, c1+c2
def fibo(n,c):
如果n 0:
返回n-1,c
其他:
n1,c1=fibo(n-1,c+1)
n2,c2=fibo(n-2,1)
返回n1+n2、c1+c2

您可以在函数中实现一个作为变量传递的计数器,或者每次都修改一个全局变量…似乎我误读了您的问题…我将递归理解为递归…很抱歉,在计算
fib(n)
时,您想计算每个
i
fib(n)
的调用次数,对吗?
global num\u of twos;如果n==2:num\u of twos+=1
@Natecat正好。。。如果您想动态地执行此操作,我想您应该在“fib”函数中添加第二个参数?除非它已更改,否则这不是使用
defaultdict
的方式。它应该是
calls=defaultdict(int)
。还可能需要提到的是,您必须从集合导入defaultdict
@Natecat有相同的区别:0是
int()
的一个实例。除了0是不可调用的,而函数int是。您还可以使用
Counter
@Natecat似乎是个好主意,但是调用者将没有对它的引用,也将无法访问计数。虽然我同意该实现是聪明的,但应该注意的是,它的副作用是至少将原来的函数调用开销增加一倍。谢谢@martineau的评论。为了澄清,您是说对
fib
的每次调用也会对
count
进行总体调用,这会增加对堆栈的总调用,而不是对
count\u调用的调用,这并不准确。我的意思是这个函数的修饰版本调用原始函数调用修饰函数调用原始函数,等等,这是原始函数调用自身时函数调用数量的两倍。使用一个全局变量或传递一个额外的参数可能会更快。@martineau是的,这就是我所解释的,也是关于性能的一个很好的观点。谢谢你指出这一点。
c = 0
def fib(n):
    global c
    c += 1
    if n == 1:
        return 0
    elif n == 2:
        return 1
    else:
        return fib(n-1) + fib(n-2)
def fibo(n, c):
if n <= 2 and n > 0:
    return n-1, c
else:
    n1, c1 = fibo(n-1,c+1)
    n2, c2 = fibo(n-2,1)
    return n1+n2, c1+c2