【问题标题】:Python - Modify dictionary from functionPython - 从函数修改字典
【发布时间】:2016-10-23 23:56:00
【问题描述】:

好吧,这个问题有点奇怪,但是我想知道我是否可以这样做。

我正在开发一个简单的斐波那契数生成器,因为我对编程很感兴趣。 所以我写了这个:

def f(n):
    if n == 1: return 1
    if n == 2: return 2
    else:
        return f(n-1) + f(n-2)

而且运行速度非常慢,在我的电脑上运行f(30) 需要 15 秒。 于是我写了这个:

def f(n):
    global a
    if n == 1: return 1
    if n == 2: return 1
    else:
        if "fib(%s)" % n in a:
            return a["fib(%s)" % n]
        else:
            z =  f(n-1) + f(n-2)
            a["fib(%s)" % n] = z
            return z

它基本上将以前的结果存储在字典中,如下所示:

{'f(1)':1,'f(2)':2,'f(3)':3,'f(4)':5} 等等。在函数中,它会检查该结果是否在该字典中,然后只需使用它,而不必重做所有计算。

这使它更快。我可以做f(100),它会立即出现。每隔 500 次,我到达f(4000),它仍然是瞬时的。一个问题是字典变得非常大。

所以我在函数末尾添加了a = {},但没有奏效;它仍然留下了 a 作为一个庞大的字典。

这样做:

def f(n):
    global a
    if n == 1: return 1
    if n == 2: return 1
    else:
        if "fib(%s)" % n in a:
            return a["fib(%s)" % n]
        else:
            z =  f(n-1) + f(n-2)
            a["fib(%s)" % n] = z
            return z
    a = {}

没用。但如果我这样做:

def f(n):
    global a
    if n == 1: return 1
    if n == 2: return 1
    else:
        if "fib(%s)" % n in a:
            return a["fib(%s)" % n]
        else:
            z =  f(n-1) + f(n-2)
            a["fib(%s)" % n] = z
            return z
# now run the function
f(100)
a = {}

a 被重置为空字典。为什么会发生这种情况,我该如何解决?

【问题讨论】:

  • 在 return z 之前添加了 a={} ?
  • 没有@Skycc 不起作用。每次运行时都会重置整个想法无用。我想做的是在调用最后一次递归后重置字典。
  • 很奇怪,它在我身边工作,“if "fib(%s)" % n in a:" 将递归调用,else 部分将是最后一个递归部分,return z 将退出功能已经
  • 请注意,您可以通过将字典的键设为斐波那契索引(n,函数参数)而不是字符串来大幅缩小您的字典。此外,根本不需要递归地执行此操作;类似a, b = 0, 1; for _ in range(n): a, b = b, a+b
  • 谢谢@James 我精简了字典

标签: python python-2.7 dictionary fibonacci


【解决方案1】:

您在函数中的a = {} 语句从未被执行;在此之前,每条可能的执行路径都会达到return。如果它被执行了,你不会喜欢结果——它会在每个对函数的递归调用中执行,这意味着你的字典永远不会包含一个以上的项目!您将不得不以某种方式检测最外层的调用并只清除那里的字典,或者(更简单)在递归之外清除它,如您的第二个示例所示。

请注意,您的字典的大部分大小来自使用长字符串键的奇怪决定。用数字本身键入它(如a[n] = z)会使其更紧凑。

(供将来参考:您在此处提出的保存先前函数调用结果的技术称为“记忆化”。)

【讨论】:

  • 谢谢!我将尝试该列表压缩。我如何检测到最外层的调用?
【解决方案2】:

尽管您提出了问题,但您真正想要的是一种更快的计算斐波那契数列的方法,对吧?您原始方法的问题在于,尽管重复代码非常优雅且编码速度很快,但速度很慢。斐波那契数列有一个接近形式的解决方案。你应该直接做这个数学来加速你的代码。 按照惯例,将斐波那契数列 F(i) 视为: F(0) = 0, F(1) = 1, F(k) = F(k-1) + F(k-2) k = 2, 3 , ... 这个序列的解决方案是(我不会在这里演示,因为不是那个地方)F(k) = (1/sqrt(5))*(a^k - b^k),其中 a = (1 + sqrt(5))/2 和 b = (1 - sqrt(5))/2。 因此,您的代码可以这样实现:

def f(n):

    a = (1 + 5**.5)/2
    b = (1 - 5**.5)/2
    F = (a**n - b**n)/5**.5
    F = int(round(F)) #necessary to get an integer without the decimal part of the approximation. Afterall, you are working with irrational numbers.
    return F

此代码适用于较大的 n 值。

【讨论】:

    猜你喜欢
    • 2022-01-14
    • 1970-01-01
    • 2012-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-01
    • 2021-06-28
    • 1970-01-01
    相关资源
    最近更新 更多