Python - 从函数修改字典

时间:2016-10-23 23:56:00

标签: python python-2.7 dictionary fibonacci

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

我正在研究一个简单的Fibonacci数字生成器,因为我对编程感兴趣。 所以我把它写出来了:

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

并且运行速度非常慢,需要15秒才能在我的计算机上执行f(30)。 所以我写了这个:

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重置为空字典。为什么会发生这种情况,我该如何解决?

2 个答案:

答案 0 :(得分:3)

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

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

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

答案 1 :(得分:0)

尽管有你的问题,你真正想要的是一种更快速的计算Fibonacci序列的方法,对吧?原始方法的问题在于,尽管非常优雅且编码速度快,但重复发生的速度非常慢。 Fibonacci序列具有紧密的形式解决方案。您应该直接进行数学运算以加快代码速度。 按照惯例,将Fibonacci序列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的大值。