簡體   English   中英

在字典中創建Pascal三角形

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM