[英]Creating a Pascal triangle in a dictionary
我正在嘗試創建一個返回描述pascal三角形的字典的函數。
例如,
pascal(3)
會給我的
{1: [1], 2: [1,1], 3: [1,2,1]}
我目前知道如何創建一個函數,該函數返回某行中的元素列表,其中n等於或大於2
def pascal(n):
if n == 0:
return {}
elif n == 1:
return {1:[1]}
else:
row = [1] + [list(pascal(n-1))[i] + list(pascal(n-1))[i+1] for i in range(n-2)] + [1]
return row
有了這個功能,
pascal(3)
給我
[1,2,1]
是否有可能以這樣的方式改變我的功能
pascal(3)
返回所需的結果
{1: [1], 2: [1,1], 3: [1,2,1]}
任何幫助,將不勝感激。
您可以使用zip
將遞歸調用中的返回列表與相同的列表配對,但在一個索引處分開,填充0:
def pascal(n):
if n == 1:
return {1: [1]}
p = pascal(n - 1)
p[n] = list(map(sum, zip([0] + p[n - 1], p[n - 1] + [0])))
return p
以便:
for n in range(1, 6):
print(pascal(n))
輸出:
{1: [1]}
{1: [1], 2: [1, 1]}
{1: [1], 2: [1, 1], 3: [1, 2, 1]}
{1: [1], 2: [1, 1], 3: [1, 2, 1], 4: [1, 3, 3, 1]}
{1: [1], 2: [1, 1], 3: [1, 2, 1], 4: [1, 3, 3, 1], 5: [1, 4, 6, 4, 1]}
如果您對迭代解決方案持開放態度,我會對以下內容進行研究。
from itertools import chain
def pascal(n):
pad = (0,)
result = {1: [1]}
for i in range(2, n + 1):
previous = list(chain(pad, result[i - 1], pad))
result[i] = [sum(pair) for pair in zip(previous, previous[1:])]
return result
演示:
>>> for n in range(1, 6):
...: print(pascal(n))
...:
...:
{1: [1]}
{1: [1], 2: [1, 1]}
{1: [1], 2: [1, 1], 3: [1, 2, 1]}
{1: [1], 2: [1, 1], 3: [1, 2, 1], 4: [1, 3, 3, 1]}
{1: [1], 2: [1, 1], 3: [1, 2, 1], 4: [1, 3, 3, 1], 5: [1, 4, 6, 4, 1]}
有了更多的線條,還有更好的內存效率:
from itertools import chain, tee
def pascal(n):
pad = (0,)
result = {1: [1]}
for i in range(2, n + 1):
previous = chain(pad, result[i - 1], pad)
c1, c2 = tee(previous)
next(c2)
result[i] = [sum(pair) for pair in zip(c1, c2)]
return result
最后,使用帶有連續整數鍵的dict
並不是很有用,您可以使用從0
開始索引的列表。 最終解決方案
def pascal(n):
pad = (0,)
result = [[1]]
for i in range(1, n):
previous = chain(pad, result[i - 1], pad)
c1, c2 = tee(previous)
next(c2)
result.append([sum(pair) for pair in zip(c1, c2)])
return result
演示:
>>> for n in range(1, 6):
...: print(pascal(n))
...:
[[1]]
[[1], [1, 1]]
[[1], [1, 1], [1, 2, 1]]
[[1], [1, 1], [1, 2, 1], [1, 3, 3, 1]]
[[1], [1, 1], [1, 2, 1], [1, 3, 3, 1], [1, 4, 6, 4, 1]]
編輯:通過不每次迭代創建兩個元組來提高效率,實例化一次pad
就足夠了。
我會小心使用這樣的遞歸 - 這是非常低效的。 您在函數體的循環中調用該函數兩次。 重要的是要考慮調用函數多少次來評估n的某些值。
很明顯,當n = 1時,函數被調用一次。
當n = 2時,函數被調用一次,然后函數調用自己兩次,總共3次調用。
對於n = 3,函數被調用一次,然后函數自己調用兩次,然后這兩個調用每次調用函數四次......這就是11次調用。
所以通話次數為numCalls = 1 + 2 + 2*4 + 2*4*6 + ... + 2*4*6*...*2n)
這個序列變得非常快......當n是20時,那就是1308293051285742128434781
遞歸並不總是邪惡的,你只需要小心,這個解決方案自己調用n次:
def genPascalDict(nMax):
if nMax < 2:
return {1: [1]}
else:
pascalDict = genPascalDict(nMax - 1)
lastRow = pascalDict[nMax - 1]
pascalDict[nMax] = [1] + [lastRow[n + 1] + lastRow[nMax - n - 2] for n in range(nMax - 2)] + [1]
return pascalDict
你可以在制作dict時快速制作副作用:
_cache = {}
def pascal(n):
try:
result = _cache[n]
except KeyError:
if n == 0:
result = []
elif n == 1:
result = [1]
else:
previous = pascal(n - 1)
result = [1] + [previous[i] + previous[i + 1] for i in range(n - 2)] + [1]
_cache[n] = result
return result
pascal(500)
print(_cache)
您不需要多次計算pascal(n)
:它不會像它一樣改變。 所以記住你的最終答案是將它存儲在一個緩存字典中,說字典就是你真正想要的東西。
在我的筆記本電腦上構建字典需要大約0.08秒。
你可以使用帶遞歸的閉包:
def pascal(n:int) -> dict:
def _pascal(_s, _e, _last, _base={1:[1], 2:[1, 1]}):
return _last if not _e-_s else _pascal(_s+1, _e, {**_last, **{_s:_base.get(_s, [1, *[_last[_s-1][i]+_last[_s-1][i+1] for i in range(len(_last)-1)], 1])}})
return _pascal(1, n+1, {})
print(pascal(3))
輸出:
{1: [1], 2: [1, 1], 3: [1, 2, 1]}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.