【问题标题】:Creating a Pascal triangle in a dictionary在字典中创建帕斯卡三角形
【发布时间】:2018-11-07 17:54:58
【问题描述】:

我正在尝试创建一个函数,该函数返回一个描述帕斯卡三角形的字典。

例如,

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]} 

任何帮助将不胜感激。

【问题讨论】:

  • 您愿意使用列表列表而不是字典吗?无论如何,您的 dict 的键只是行索引

标签: python recursion pascals-triangle


【解决方案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]}

【讨论】:

  • OP 的列表理解似乎还不错。因此,除了使用zip,他们还可以将其替换为以前的:p[n] = [1] + [p[n - 1][i] + p[n - 1][i + 1] for i in range(n - 2)] + [1]
  • 是的,但我更喜欢zip,因为通常认为使用迭代器而不是列表索引更符合 Pythonic。
【解决方案2】:

如果您对迭代解决方案持开放态度,我准备了以下内容。

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-1th 而不是 nth
【解决方案3】:

我会小心使用这样的递归 - 它非常低效。您在函数体的循环中调用该函数两次。重要的是要考虑调用函数多少次来评估 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

【讨论】:

    【解决方案4】:

    作为副作用,您可以在构建 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 秒。

    【讨论】:

      【解决方案5】:

      您可以使用带有递归的闭包:

      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]}
      

      【讨论】:

        猜你喜欢
        • 2012-10-24
        • 1970-01-01
        • 2013-11-19
        • 1970-01-01
        • 2014-11-12
        • 2015-01-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多