简体   繁体   English

Python-从函数修改字典

[英]Python - Modify dictionary from function

Okay, this question is a little strange, but I was wondering if I could do it like this. 好的,这个问题有点奇怪,但是我想知道我是否可以这样做。

I'm working on a simple Fibonacci number generator for fun since I was interested in programming it. 我正在研究一个简单的斐波那契数发生器,因为我很喜欢对其进行编程,因此很有趣。 So I wrote this out: 所以我这样写:

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

and that ran very slowly, taking 15 seconds to do f(30) on my computer. 而且运行非常缓慢,需要15秒才能在计算机上执行f(30) So then I wrote this: 所以我这样写:

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

which basically stores previous results in a dictionary like so: 它基本上将以前的结果存储在字典中,如下所示:

{'f(1)':1,'f(2)':2,'f(3)':3,'f(4)':5} and so on. {'f(1)':1,'f(2)':2,'f(3)':3,'f(4)':5}等。 In the function it would check if that result was in that dictionary, then just use that instead of having to redo all the calculations. 在该函数中,它将检查该结果是否在该字典中,然后仅使用该结果,而不必重做所有计算。

This made it tons faster. 这使其速度更快。 I could do f(100) and it instantly appear. 我可以做f(100) ,它立即出现。 Going by intervals of 500, I got to f(4000) and it was still instantaneous. 以500为间隔,我到达了f(4000) ,它仍然是瞬时的。 The one problem was that the dictionary was getting stupidly big. 一个问题是字典变得越来越大。

So I added a = {} to the end of the function, and that didn't work; 所以我在函数的末尾添加a = {} ,但是没有用; it still left a as a massive dict. 它仍然留下a作为一个巨大的字典。

So doing this: 这样做:

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 = {}

didn't work. 没有用 but if I do this: 但是如果我这样做:

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 gets reset to an empty dictionary. a被重置为一个空的字典。 Why does this happen and how can I fix it? 为什么会发生这种情况,我该如何解决?

Your a = {} statement inside the function was never being executed; 您在函数内的a = {}语句从未执行过; every possible path of execution reaches a return before then. 在此之前,所有可能的执行路径都会return If it WAS executed, you wouldn't have liked the results - it would have executed in every single recursive call to the function, meaning that your dictionary would never hold more than one item! 如果它执行了,您将不会喜欢结果-它会在对该函数的每个递归调用中执行,这意味着您的词典永远不会包含多个项目! You would somehow have to detect the outermost call and only clear the dict there, or (much simpler) clear it outside of the recursion as in your second example. 您将不得不以某种方式检测最外面的调用,然后仅清除那里的dict,或者(更简单地)在递归之外清除它,如第二个示例中所示。

Note that much of the size of your dictionary is coming from the strange decision to use a long string key. 请注意,字典的大部分大小来自使用长字符串键的奇怪决定。 Keying it with the number itself (as in a[n] = z ) would make it much more compact. 用数字本身对其进行键控(如a[n] = z )将使其更加紧凑。

(For future reference: the technique you've come up with here, of saving the results from previous function calls, is known as "memoization".) (以备将来参考:您在此处提出的用于保存先前函数调用的结果的技术称为“记忆化”。)

Despite your question, what you really want is a faster way of calculating Fibonacci sequence, right? 尽管有您的问题,但您真正想要的是一种更快的计算斐波那契数列的方法,对吗? The problem with your original approach is that recurrence, despite being very elegant and quick to code, is quite slow. 原始方法的问题在于,尽管非常优雅且可以快速编写代码,但是重复执行速度却很慢。 Fibonacci sequence has a close form solution. 斐波那契数列具有近似形式的解。 You should do this math directly to speed up your code. 您应该直接进行数学运算以加快代码速度。 As convention, consider the Fibonacci sequence F(i) as: F(0) = 0, F(1) = 1, F(k) = F(k-1) + F(k-2) k = 2, 3, ... The solution for this sequence is (I will not demonstrate it here, because is not the place for that) F(k) = (1/sqrt(5))*(a^k - b^k), where a = (1 + sqrt(5))/2 and b = (1 - sqrt(5))/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。 Thus, you code could be implemented like this: 因此,您的代码可以这样实现:

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

This code scale very well for large values of n. 对于较大的n值,此代码可很好地缩放。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM