[英]Recursion function - Python
這就是問題:
寫一個遞歸函數f,它產生序列0, 1, 0.5, 0.75, 0.625, 0.6875, 0.65625, 0.671875, 0.6640625, 0.66796875
。 前兩個術語是0和1,每個其他術語是前兩個術語的平均值。
>>> f(0)
0
>>> f(1)
1
>>> f(2)
0.5
>>> [(i,f(i)) for i in range(10)]
[(0, 0), (1, 1), (2, 0.5), (3, 0.75), (4, 0.625), (5, 0.6875), (6, 0.65625), (7, 0.671875), (8, 0.6640625), (9, 0.66796875)]
這是我的代碼到目前為止,我似乎無法弄明白。 任何幫助/建議將不勝感激。
def f(n):
if n==0:
return 0
if n==1:
return 1
else:
return f(n-2)//f(n-1)
遞歸的情況是錯誤的:
return f(n-2)//f(n-1) # not the formula for the average of two numbers
兩個數字a和b的平均值是(a + b)/ 2 。 所以你可以將你的函數定義為:
def f(n):
if n==0:
return 0
if n==1:
return 1
else:
return (f(n-1)+f(n-2))/2
或者我們可以使它與Python版本無關,如:
def f(n):
if n==0:
return 0
if n==1:
return 1
else:
return 0.5*(f(n-1)+f(n-2))
然后,您可以使用列表推導生成序列,如:
>>> [f(i) for i in range(10)]
[0, 1, 0.5, 0.75, 0.625, 0.6875, 0.65625, 0.671875, 0.6640625, 0.66796875]
或者使用map
:
>>> list(map(f,range(10)))
[0, 1, 0.5, 0.75, 0.625, 0.6875, 0.65625, 0.671875, 0.6640625, 0.66796875]
因此,對於n > 1
, U0 = 0
, U1 = 1
, Un = (U(n-1) + U(n-2)) / 2
。
你只需將它作為一個函數逐字翻譯:
def f(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return (f(n-1) + f(n-2)) / 2
現在用於生成從0
到n
的Ui
序列:
def generate_sequence(n):
return [f(i) for i in range(n)]
這可以(並且確實應該)通過memoization進行優化。 基本上,您只需將先前計算的結果存儲在字典中(而在這種情況下您可以直接使用列表)。
results = dict()
def memoized_f(n):
if n in results:
return results[n]
else:
results[n] = f(n)
return results[n]
這樣,對於每個n
, f(n)
將僅計算一次。
作為獎勵,當memoized_f(n)
, results
字典將f(i)
的值保持為從0
到至少 n
。
遞歸輔助功能
您可以使用簡單的輔助過程和幾個狀態變量來定義它 - 以下f
實現演變了線性迭代過程
def f (n):
def aux (n, a, b):
if n == 0:
return a
else:
return aux (n - 1, b, 0.5 * (a + b))
return aux(n, 0, 1)
print([f(x) for x in range(10)])
# [0, 1, 0.5, 0.75, 0.625, 0.6875, 0.65625, 0.671875, 0.6640625, 0.66796875]
走向通用
或者你可以在我稱之為fibx
的整個過程中fibx
from functools import reduce
def fibx (op, seed, n):
[x,*xs] = seed
if n == 0:
return x
else:
return fibx(op, xs + [reduce(op, xs, x)], n - 1)
現在我們可以使用fibx
實現(例如) fib
from operator import add
def fib (n):
return fibx (add, [0,1], n)
print([fib(x) for x in range(10)])
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
或者我們可以使用fibx
和自定義運算符來實現您的f
def f (n):
return fibx (lambda a,b: 0.5 * (a + b), [0, 1], n)
print([f(x) for x in range(10)])
# [0, 1, 0.5, 0.75, 0.625, 0.6875, 0.65625, 0.671875, 0.6640625, 0.66796875]
浪費了計算
這里的一些答案是遞歸(例如) 0.5 * (f(n-1) + f(n-2))
,它復制了大量的工作。 大約40的n
值比天文學更長( 幾分鍾與毫秒相比)來計算,而不是我在這里描述的方法。
在這個例子中看一下樹遞歸fib(5)
:看看fib(3)
和fib(2)
是如何重復幾次的? 這歸功於fib
計划的天真實施。 在這種特殊情況下,我們可以使用輔助循環函數(如我的答案所示)或使用memoisation(在另一個答案中描述)輕松避免這種重復工作
像這樣的樹遞歸導致O(n 2 ),而我的答案中的線性迭代遞歸是O(n)
生成n
的序列
@MadPhysicist提供的另一個答案為單個n
輸入值生成一個序列 - 即f(9)
將生成前10個值的列表。 然而,由於相同的f(n-1), and
f(n-2)`調用,實現同時是復雜和幼稚的並且浪費了大量的計算。
我們的初始方法的微小變化可以在很短的時間內生成相同的序列 - 使用我的代碼f(40)
將花費幾分之一秒,而這些錯誤的樹遞歸答案將花費超過2分鍾
( 粗體變化)
def f (n):
def aux (n, acc, a, b):
if n == 0:
return acc + [a]
else:
return aux (n - 1, acc + [a], b, 0.5 * (a + b))
return aux(n, [], 0, 1)
print(f(9))
# [0, 1, 0.5, 0.75, 0.625, 0.6875, 0.65625, 0.671875, 0.6640625, 0.66796875]
如果您想要一個在單個調用中生成序列的函數,而不必為列表的每個元素調用該函數,則可以在展開堆棧時存儲計算的值:
def f(n, _sequence=None):
if _sequence is None:
_sequence = [0] * (n + 1)
if n == 0 or n == 1:
val = n
else:
f(n - 1, _sequence)
f(n - 2, _sequence)
val = 0.5 * (_sequence[n - 1] + _sequence[n - 2])
_sequence[n] = val
return _sequence
如果f
返回單個值,則這樣做的優點是不需要在相同的值上進行多次遞歸,就像最后使用[f(n) for n in range(...)]
。
您可以使用@RightLeg建議的更全局的memoization形式來記錄多個調用之間的知識。
與其他解決方案不同,此功能實際上會生成完整的序列。 例如,您的原始示例是:
>>> f(9)
[0, 1, 0.5, 0.75, 0.625, 0.6875, 0.65625, 0.671875, 0.6640625, 0.66796875]
另一個簡單的解決方案可能如下所示
a=0.0
b=1.0
count = 0
def f(newavg, second, count):
avg = (second+newavg)/2
print avg
count=count+1
if count<8:
newavg = avg
f(second, avg, count)
f(a, b, count)
當然這個代碼只輸出到監視器...如果你想將輸出放到一個列表中,只需在遞歸中添加代碼。
還要注意在需要的地方正確縮進。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.