【问题标题】:Python: Odd behavior with modulo operatorPython:模运算符的奇怪行为
【发布时间】:2012-03-13 17:27:39
【问题描述】:

解决方案可能相当简单,但我就是想不通。这是代码,它是一个简单的斐波那契数生成器。目标是总结所有低于 4,000,000 的偶数斐波那契数。

我的方法是首先生成所有低于 4,000,000 的斐波那契数,然后: a)用偶数生成一个新列表(“偶数”)(这很好用) b)从列表“all”中删除奇数

但是,在后一种情况下,由于我不明白的原因,输出是: [2、5、8、21、34、89、144、377、610、1597、2584、6765、10946、28657、46368、121393、196418、514229、832040、2178309、3524578]

非常感谢任何帮助。谢谢!

all = []
even = []

def fibonacci():
    a, b = 1, 2
    while a < 4000000:
        all.append(a)
        a, b = b, a + b
    print all

##Putting all the even fibonacci numbers in a different list and summing them up works fine
#    for i in all:
#        if i % 2 == 0:
#            even.append(i)
#    print even                   
#    print sum(even)


# But for some strange reason I can't figure out how to remove the odd numbers from the list
    for i in all:
        if i % 2 != 0:
            all.remove(i)

    print all
    print sum(all)

fibonacci()

【问题讨论】:

  • 我建议你不要使用“all”作为变量名。有一个非常有用的内置函数 all,它真的很方便。
  • DSM,感谢您指出这一点!
  • 另外,如果有人想知道斐波那契数列:是的,它通常以 0, 1, 1, 2, ... 开头,但是 Project Euler 对这个问题的说明让它以 1, 2 开头, ...
  • 感谢下面的精彩回答!我想我从他们身上学到了很多。

标签: python fibonacci


【解决方案1】:

这是一个“陷阱”情况:您在迭代列表时从列表中删除项目,从而更改列表,导致您的迭代出现意外行为。试试这个:

...
# But for some strange reason I can't figure out how to remove the odd numbers from the list
    for i in all[:]:
        if i % 2 != 0:
            all.remove(i)
...

这就是所谓的“切片”表示法,它会导致您迭代列表的一次性副本,这样您的迭代就不会受到 all.remove() 调用的影响。

【讨论】:

  • 另一种更简洁、可以说更清晰且至少同样有效(当您复制整个列表时)的替代解决方案是过滤以生成一个新列表:all = [x for x in all if x % 2 == 0]
  • 是的,列表理解看起来不错,如果我自己编写代码,我可能会这样写。如果您将其写为答案,我会投票赞成!但是,我通常会发现我的答案对提问者越有用,它们越接近问题,所以我的 SO 风格是对问题做出最小的差异以使其行为正确。
  • matt,非常感谢您的解释。另外,我确实发现您对我的原始代码的修改比 delnan 的解决方案更容易理解。虽然 delnan 的代码对于经验丰富的 Python 开发人员来说可能更清晰,但对我来说有点过于简洁。不过我已经记下了。几个月后,我可能也会喜欢它……
【解决方案2】:

您不能从您迭代的列表中删除项目。 Python 使用迭代器,它只知道相对于列表开头的当前索引。当您从列表的前面删除项目时,所有元素的位置都会发生变化,并且您会跳过下一个元素。

您可以通过多种方式避免该问题,例如使用生成器:

def fibonacci():
    a, b = 1, 2
    while a < 4000000:
        yield a
        a, b = b, a + b

def even(seq):
    for item in seq:
        if item % 2 == 0:
            yield item

print sum(even(fibonacci()))

【讨论】:

    【解决方案3】:

    如果我们仔细观察下面代码中的迭代是如何发生的

    for i in all:
            if i % 2 != 0:
                all.remove(i)
    
                # Add these two lines for debugging.. 
                # Or to know how this iteration functions
    
                print "when %d: " %i 
                print all
    
        print "Remaining Evens",
        print all
    

    如果最大数量为 100,则输出如下所示。

    original series [1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
    when 1: 
    [2, 3, 5, 8, 13, 21, 34, 55, 89]
    when 3: 
    [2, 5, 8, 13, 21, 34, 55, 89]
    when 13: 
    [2, 5, 8, 21, 34, 55, 89]
    when 55: 
    [2, 5, 8, 21, 34, 89]
    Remaining Evens [2, 5, 8, 21, 34, 89]
    

    这里当python开始迭代一个列表时,它在技术上只记住它必须迭代的数字的位置..

    如果我们观察输出,

    在第一次迭代中,它删除了 1。

    在下一次迭代中,它会记住它必须计算第二个位置。现在列表从“2”开始。所以第二个位置是“3”。因此它会删除它。

    在下一次迭代中,它会记住它必须从第 3 个位置开始计数。现在在当前列表中,第 3 位是“8”。所以,它从那里开始计算......而不是从“5”开始。因此,由于 8 不满足,所以它转到 13..

    因此,它跳过了所有这些数字..

    如何解决这个问题:

    实际上,您需要复制“全部”列表并进行迭代。 (它不应该引用同一个对象..)。如果是这样,同样的事情也会发生。

    您可以通过简单地使用切片运算符来做到这一点:

    copy_all= all[:] 
    
    #or else, you need to use deepcopy()
    
    import copy
    copy_all = copy.deepcopy(all)
    
    # you iterate copy_all but delete in all. 
    
    However, prefer the first method. Its very simple.
    

    【讨论】:

      【解决方案4】:

      这是因为您删除了索引 i 处的项目,而不是数字“i”。

      【讨论】:

      • 我不相信这是真的。 Python 2.7.2 (default, Nov 14 2011, 19:37:59) [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin Type "help", "copyright", "credits" or "license" for more information. &gt;&gt;&gt; foo = [5, 4, 3, 2, 1] &gt;&gt;&gt; foo.remove(5) &gt;&gt;&gt; foo [4, 3, 2, 1]
      猜你喜欢
      • 2012-12-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-29
      • 1970-01-01
      • 2020-01-20
      • 2013-10-29
      • 2015-02-01
      相关资源
      最近更新 更多