【问题标题】:bottom up fibonacci in python using O(1) space使用 O(1) 空间在 python 中自下而上斐波那契
【发布时间】:2016-08-25 04:33:46
【问题描述】:

我想用 O(1) 空间写一个自下而上的斐波那契。我的问题是 python 的递归堆栈限制了我测试大量数据。有人可以提供替代或优化我所拥有的吗?这是我的代码:

def fib_in_place(n):
    def fibo(f2, f1, i):
        if i < 1:
            return f2
        else:
            return fibo(f1, f2+f1, i -1)
    return fibo(0, 1, n)

【问题讨论】:

  • 是否需要递归编写它?
  • 您的代码实际上看起来很合理(臃肿,但不是每次调用两次递归调用的幼稚版本)。我们在这里谈论的数字有多大?您是否遇到了 1000 的默认递归限制?
  • 您在这里使用了 O(n) 堆栈空间。如果 Python 有尾调用消除,这将只是 O(1) 空间。 (实际上,无论如何它都不会是 O(1) 空间,因为整数占用恒定空间的假设几乎立即失效,但该问题已包含在问题定义中。您无法真正解决这个问题。)跨度>
  • 如果你想要 O(1) 空间,我会选择使用生成器
  • fib_in_place(100) 在我的机器上立即生成 354224848179261915075 的结果。你的机器上发生了什么?

标签: python recursion fibonacci


【解决方案1】:

以这种方式使用递归意味着您使用的是 O(N) 空间,而不是 O(1) - O(N) 在堆栈中。

为什么要使用递归?

def fib(n):
    a, b = 0, 1
    for i in range(n):
        a, b = b, a + b

    return a

【讨论】:

  • 这很整洁。无法真正想到迭代版本,但这与给出的其他答案之一相似。我希望我能给你们两个“接受的答案”!
【解决方案2】:

你可以记住斐波那契函数来提高效率,但如果你需要一个递归函数,它仍然需要至少 O(n):

def mem_fib(n, _cache={}):
    '''efficiently memoized recursive function, returns a Fibonacci number'''
    if n in _cache:
        return _cache[n]
    elif n > 1:
        return _cache.setdefault(n, mem_fib(n-1) + mem_fib(n-2))
    return n

这是我对 Python 中主要斐波那契问题的回答:How to write the Fibonacci Sequence in Python

如果你被允许使用迭代而不是递归,你应该这样做:

def fib():
    a, b = 0, 1
    while True:            # First iteration:
        yield a            # yield 0 to start with and then
        a, b = b, a + b    # a will now be 1, and b will also be 1, (0 + 1)

用法:

>>> list(zip(range(10), fib()))
[(0, 0), (1, 1), (2, 1), (3, 2), (4, 3), (5, 5), (6, 8), (7, 13), (8, 21), (9, 34)]

如果你只想得到第n个数字:

def get_fib(n):
    fib_gen = fib()
    for _ in range(n):
        next(fib_gen)
    return next(fib_gen)

和用法

>>> get_fib(10)
55

【讨论】:

  • 我熟悉这个实现,我想以自下而上的方式编写它,但不将我的答案累积在一个列表中。有没有办法做到这一点?
  • 趣味提示:当您访问official Python home page 时,您首先看到的是迭代斐波那契代码。
  • 在尝试生成器版本时,它会返回“”。我需要进口吗?
  • 有趣的。 @donutjuice无论是使用递归将数据存储在堆栈帧中还是使用缓存,您都在以空间换取时间-只有缓存不像堆栈那样有限,也不像堆栈那样昂贵。我真的不知道你说的自下而上是什么意思。我将演示另一个函数的用法。
【解决方案3】:

为什么要使用迭代?

def fib(n):
    phi_1 = (math.sqrt(5) + 1) / 2
    phi_2 = (math.sqrt(5) - 1) / 2
    f = (phi_1**n - phi_2**n) / math.sqrt(5)
    return round(f)

代数结果是精确的; round 操作仅允许数字表示不准确。

【讨论】:

  • 实际上应该是f = (phi_1**n - phi_2**n) / math.sqrt(5)
【解决方案4】:

尾递归定义很容易变成迭代定义。如有必要,翻转条件,使尾递归调用位于“if”分支中。

def fibo(f2, f1, i):
    if i > 0:
        return fibo(f1, f2+f1, i -1)
    else:
        return f2

然后将 'if' 转换为 'while',将 return 替换为新参数的解包赋值,并(可选)删除 'else'。

def fibo(f2, f1, i):
    while i > 0:
        f2, f1, i = f1, f2+f1, i -1
    return f2

使用迭代,您不需要嵌套定义。

def fib_efficient(n):
    if n < 0:
        raise ValueError('fib argument n cannot be negative')
    new, old = 0, 1
    while n:
        new, old = old, old+new
        n -= 1
    return new

本地名称“新”和“旧”指的是斐波那契利用生物繁殖来激发序列。然而,这个故事更适用于酵母细胞而不是兔子。旧的、成熟的酵母细胞通过萌芽新的、未成熟的细胞进行繁殖。 (该功能在印度的原始来源似乎是 Virahanka 数数 a 的方法,以从 1 和 2 节音节的有序序列中创建一个具有 n 节拍的梵文诗行。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-08-17
    • 2014-05-23
    • 2013-08-03
    • 2013-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-19
    相关资源
    最近更新 更多