2017-01-07 167 views
2

如果你有一個遞歸函數(例如斐波那契序列):如何計算遞歸函數中值的重複次數?

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) 

如何將一個數,例如,當FIB(20)被調用時次FIB(2)的數量?

+1

你可以實現taht你傳遞一個變量在功能的計數器,或每次修改一個全局變量... – deweyredman

+0

看來我誤解你的問題...我讀recurses遞歸...對不起 – deweyredman

+1

你想在計算'fib(n)'時計算每個'i'調用'fib(i)'的次數,對嗎? – DyZ

回答

1

您可以使用字典來計算所有調用fib。字典必須在第一次撥打fib之前清除。

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) 
+0

除非它已被更改,否則這不是你如何使用'defaultdict'。它應該是'calls = defaultdict(int)'。也可能想提一提,你必須從集合中導入defaultdict' – Natecat

+0

@Natecat同樣的區別:0是'int()'的一個實例。 – DyZ

+2

除了0不可調用,而函數int是。 – Natecat

1

如何:

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_dict = collections.defaultdict(int)

+1

@Natecat似乎是一個好主意,但是調用者將不會引用它,並且將無法訪問計數。 –

0
def fib(n, x): 
    c = 1 if n == x else 0 

    if n == 0 or n == 1: 
     return 1, c 
    else: 
     fA, cA = fib(n - 1, x) 
     fB, cB = fib(n - 2, x) 
     return fA + fB, cA + cB + c 

如果

calls = defaultdict(int) 

在功能上,做其他事情之前更新在字典中的相應條目我有 沒有弄亂邏輯,這個函數接受nx並返回一個元組(y, c) s.t.在計算過程中調用fib(n, _)=yfib(x, _)時間爲c次。

這是一個更純粹的替代方案,涉及更新字典的其他解決方案。它基於OP僅需要對特定的k調用f(k, _)的次數的假設,並且因此避免填充僅需要一個值的字典。

2

您可以使用一個裝飾:

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 

這是一個聰明的實現,它需要adv較少知道的抗體function attributes。請注意,最初的修飾器是從他的lecture on Memoization中討論的John DiNero的count功能修改而來,他在這裏解決了這個特定問題。

+0

雖然我同意實現很聰明,但應該指出的是它有一個副作用 - 至少使原始函數的調用開銷加倍。 – martineau

+0

謝謝@martineau的評論。爲了澄清,你說每次調用'fib'還會調用'count',這會增加調用堆棧的總次數,而不是'count_calls'? – pylang

+0

不完全。我的意思是該函數的裝飾版本調用調用裝飾的原件,調用原件等等 - 這是原始調用自身時函數調用數量的兩倍。使用全局變量或傳遞額外的參數可能會比這更快。 – martineau

0

沒有全局變量:

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功能多少次的每個值是所述信息的字典拜訪。

0

這就是我試過的...這樣想做工精細

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 

輸出:

0

如何使用的功能屬性。就像一個靜態變量。

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 
1

這我不清楚到底是什麼,你要算重複值,所以我猜這是時代的(遞歸)函數被調用具有相同參數的數量(或一羣人,如果有多個)。

在下面的代碼中,名爲tally_recurring_args()的裝飾器用於在某些代碼中包裝該功能來執行此操作。爲了簡化事情並避免重新發明輪子,使用collections.Counter來計算函數的每個唯一參數組合的調用次數。這是該函數的一個屬性,因此每次調用裝飾函數時都可以在wrapper中輕鬆引用該屬性。

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): called 3 times 
fib(1): called 5 times 
fib(2): called 3 times 
fib(3): called 2 times 
fib(4): called 1 time 
fib(5): called 1 time