Eratosthenes的Python Sieve无法正常工作

时间:2013-11-13 04:40:12

标签: python sieve-of-eratosthenes sieve

首先,这是HOMEWORK,我目前正在研究python上的Eratosthenes Sieve。 我的程序看起来像:

x=[]
    for i in range(2,100):
        x.append(i)
primes=[]
i=2

while len(x)!=0:
    k=x[0]
    x.pop(0)
    primes.append(k)
    while k*i in x:
        x.remove(k*i)
        i+=1

print(primes)
print(x)

当我的程序打印'primes'时,我得到:

[2, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39,
41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77,
79, 81, 83, 85, 87, 89, 91, 93, 95, 97, 99]

为什么列表中有复合数字?看起来程序应该可行

编辑程序,现在看起来:

x=[]
for i in range(2,100):
    x.append(i)
primes=[]



while len(x)!=0:
    i=2
    k=x[0]
    x.pop(0)
    primes.append(k)
    while k*i<=max(x):
        x.remove(k*i)
        i+=1
        if k*i not in x:
            i+=1

print('primes','\n',primes,sep='')
print('x','\n',x,sep='')

仍然无法正常工作,收到错误; “ValueError:list.remove(x):x不在列表中”

3 个答案:

答案 0 :(得分:2)

一个问题是您只需添加到i。每次从列表中弹出另一个元素时,您需要将i重置为2.

也就是说,首先你从列表中弹出2。您逐渐增加i以从列表中删除所有2的倍数。最后,i将为50.然后您返回并从列表中弹出3,i仍为50,因此它只在列表中查找50*3

即使你解决了这个问题,它仍然无效,因为一旦找到不在列表中的一个,你就会停止查看i值。但k*i可能不在列表中但k*(i+1)是 - 例如,在你设置2的倍数后,3的第一个倍数(即6)不在列表中,但下一个(即9)是。所以你不能停下来,直到你尝试每个倍数到列表最大值。

答案 1 :(得分:1)

评论:

  • 应使用更多描述性变量名称
  • 每次在x循环中输入k * i时需要重新启动i
  • x.pop(0)对于大x
  • 来说很慢
  • x.remove(k * i)对于大x
  • 来说很慢
  • 应该将内部循环更改为“while k * i&lt; top”并添加“if” k * i in x“。top是100。

这是一个使用位列表的工作和快速筛子: http://stromberg.dnsalias.org/svn/sieve/trunk/

答案 2 :(得分:1)

你应该接受@BrenBarn的答案,因为它涵盖了重要的东西。但是这里有一些提示:

  • 有一种更简单,更快捷的方式来制作初始x列表。

让Python为你做。只需将range()包裹在list()中,就像这样:

MAX_NUM = 100
x = list(range(2, MAX_NUM))

我们稍后会有更多机会使用MAX_NUM;请继续阅读。

  • 在Python中,最佳做法是使用for循环range()而不是while循环添加到索引变量。

而不是:

i = 2
while k*i <= max(x):
    # do stuff with k*i
    i += 1

试试这个:

for i in range(k*2, max(x), k):
    # do stuff with i

Python内置range()将为您创建一系列值,从k*2开始,每次添加k,并以k的最后一个数字停止}小于max(x)。现在你的循环运行得更快,你避免了一堆倍数。

  • 不是将x[0]编入索引以获取k,然后使用x.pop()来放弃k中的x值,而是采用更简单的方式。

list.pop()返回弹出的值。所以你可以在一行中这样做:

k = x.pop()
  • 您多次计算max(x)。但您已经知道x中的最大数字,因为您构建了x。在构建x时,您可以将最高数字保存在变量中,并使用此变量而不是一遍又一遍地查找max(x)。关于max(x)的好处是它不会检查已被拔出的数字;例如,当k为3时,将删除99。但max(x)价格昂贵,所以我认为仅使用保存的价值总体上是一个胜利。

这就是我保存MAX_NUM的原因。所以你可以这样做:

for i in range(k*2, MAX_NUM, k):
    # do stuff with i
  • 如果你刚刚开始,你可能不知道Python的set()类,但它对这个问题有好处。正如dstromberg在他/她的回答中所说,x.remove(some_value)x包含许多值时很慢。但是从set中删除值总是非常快速的操作。

你可以建立一个这样的集合:

x = set(range(2, 100))

此集将包含2到99之间的所有整数值。

然后,关于集合的一个简洁的事情:你可以摆脱成员而不用检查它们是否在集合中,使用.discard()成员函数(另一个便宜的事情与{ {1}})。

set

实际上,# list solution if i in my_list: my_list.remove(i) # set solution my_set.discard(i) (在列表中使用)和in都很昂贵。所以list.remove()用一个廉价的操作取代了两个昂贵的操作!

将原始程序置于工作状态后,请保留原始程序的副本,然后重写该程序以使用set代替set。将最大整数从100增加到,比如说10000和两个程序的时间。你应该注意到差异。 list需要的时间比set长,但您可以在操作(list测试或删除值)上赢得大量时间。

  • 但您可能想知道,“史蒂夫,in无法编入索引,因此set将无法正常工作......如何查找x[0]?”

我建议您只使用带有k语句的for循环来生成所需的range()值。您可以执行以下操作,而不是查看k或使用x[0]

k = x.pop()

使用for k in range(2, MAX_NUM): if k not in x: continue set测试非常快,因此这将很快跳过所有非素数。它不像你的原始程序总是有下一个准备好的方式那样聪明,但总的来说我认为in是这个问题的胜利。

哦,嘿,我们可以再次使用set

  • 您可能也会想“A MAX_NUM没有set所以如何在筛子结束时获取素数列表?”

再好不过了!

.pop()

所以筛选掉非素数,然后在完成后取出素数列表。

  • 您可能希望使用稍微好一点的变量名称。我个人喜欢简洁的单字母变量,如果它们用在紧凑的代码中,那么我会保留result = list(my_set) # get a list of values stored in my_set ,但i可能xsieve应该是k或其他什么。

  • 我很高兴看到您了解如何使用next_prime功能的更高级功能,但我认为您的打印代码可能更简单。

而不是:

print()

试试这个:

print('primes','\n',primes,sep='')

或许这个:

print('primes')
print(primes)

使用上述所有建议来计算Eratosthenes筛选的实际程序比建议的要短得多!我有它的书面和测试,但我不会发布它,除非你希望我这样做。我的解决方案(不包括print('primes:\n{}'.format(primes)) 调用或空行)是8行Python,第一行是:print()(编辑:这是六行,但我没有检查到看看MAX_NUM = 100是否在集合中,所以它很慢。检查又增加了两行。)

完成后,将原件与修改后的原件进行比较。你更倾向哪个?有人比其他人更容易理解吗?

我喜欢Python的一个方面是,当程序有效地使用Python的内置功能时,它会变得更简单,更漂亮,更容易理解。

祝你好运,玩得开心!