【问题标题】:Optimize Sieve of Eratosthenes Further进一步优化埃拉托色尼筛
【发布时间】:2015-09-16 05:30:36
【问题描述】:

我已经写了一个埃拉托色尼筛法——我想——但它似乎并没有尽可能地优化。它有效,它使所有素数达到 N,但没有我希望的那么快。我仍然在学习 Python——来自两年的 Java——所以如果有些东西不是特别 Pythonic,那么我道歉:

def sieve(self):
        is_prime = [False, False, True, True] + [False, True] * ((self.lim - 4) // 2)
        for i in range(3, self.lim, 2):
            if i**2 > self.lim: break
            if is_prime[i]:
                for j in range(i * i, self.lim, i * 2):
                    is_prime[j] = False
        return is_prime

我查看了与此类似的其他问题,但我无法弄清楚一些更复杂的优化如何适合我的代码。有什么建议吗?

编辑:根据要求,我看到的其他一些优化是在限制之前停止第一个 for 循环的迭代,并跳过不同的数字——我认为这是轮子优化?

编辑 2:这是使用该方法的代码,用于 Padraic:

primes = sieve.sieve()
for i in range(0, len(primes)):
    if primes[i]:
        print("{:d} ".format(i), end = '')
print() # print a newline

【问题讨论】:

  • 您能否简要介绍一下您所引用的其他优化?
  • 第二行if i**2 > self.lim: break是多余的。
  • @BlackVegetable 问题已编辑。
  • 切片分配是一种标准技术,可用于加速将倍数设置为 false 的内部循环。我打算输入一个答案,但一个简单的谷歌搜索产生的结果比我能得到的要好 --- 例如,codereview.stackexchange.com/questions/42420/…
  • 你可以使用range(i * i, self.lim, 2*i)

标签: python-3.x optimization primes


【解决方案1】:

稍微不同的方法:使用位数组来表示奇数 3,5,7,... 与布尔值列表相比节省了一些空间。

这可能只会节省一些空间,而无助于加速...

from bitarray import bitarray

def index_to_number(i): return 2*i+3
def number_to_index(n): return (n-3)//2

LIMIT_NUMBER = 50
LIMIT_INDEX = number_to_index(LIMIT_NUMBER)+1

odd_primes = bitarray(LIMIT_INDEX)
# index  0 1 2 3
# number 3 5 7 9

odd_primes.setall(True)

for i in range(LIMIT_INDEX):
    if odd_primes[i] is False:
        continue
    n = index_to_number(i)
    for m in range(n**2, LIMIT_NUMBER, 2*n):
        odd_primes[number_to_index(m)] = False

primes = [index_to_number(i) for i in range(LIMIT_INDEX)
          if odd_primes[i] is True]
primes.insert(0,2)

print('primes: ', primes)

又是同样的想法;但这次让 bitarray 使用切片分配处理内部循环。这可能会更快。

for i in range(LIMIT_INDEX):
    if odd_primes[i] is False:
        continue
    odd_primes[2*i**2 + 6*i + 3:LIMIT_INDEX:2*i+3] = False

(此代码均未经过认真检查!谨慎使用)


如果您正在寻找基于不同方法 (wheel factorizaition) 的素数生成器,请查看 this excellent answer

【讨论】:

  • 这实际上是一种非常有趣的方法。肯定会节省空间。
  • 我继续尝试将您的解决方案与其他解决方案集成,并提出了一个似乎运作良好的组合。我的原始算法花费了大约 800 毫秒来处理 1,000,000。我的新的取决于你怎么做。如果您使用位数组(未优化为只做赔率),则需要约 200 毫秒。可以加快速度,但真正的好处在于它的内存占用基本上不存在。 (使用 sys.getsizeof())。如果您改用布尔数组,则大约需要 60-80 毫秒。
  • 我个人更喜欢 n=2i+1 寻址方案(以浪费一个内存单元为代价),因为它允许很好的封闭索引形式:对于第 i 个条目,它的值为 (2i +1),它的平方是4i^2+4i+1,平方的索引是(4i^2+4i+1-1)/2 = 2i^2+2i = 2i(i+1);从这里我们增加 2(2i+1) 的值,即 (2i+1) 的索引增量。参看。 c++ code.
  • @WillNess 这看起来是一个很大的改进!将仔细研究它。多谢!你真的似乎是这类事情的专家之一!
  • @hiroprotagonist 谢谢。我不是真的,我只是曾经回答过很多关于它的简单问题。我从不追求复杂的东西。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多