Python中Eratosthenes的高效筛选

时间:2018-04-20 07:22:51

标签: python numpy sieve-of-eratosthenes

#Python中这个非常简短的代码试图模拟Eratosthenes" Sieve of Eratosthenes"对于具有(0)脚本短度约束的前N个自然数; (1)最小化“if”陈述'和' for / while循环&#39 ;; (2)CPU时间效率。

import numpy as np
N = 10**5
a = np.array(range(3,N,2))
for j in range(0, int(round(np.sqrt(N),0))):
    a[(a!=a[j]) & (a%a[j] == 0)] = 0
    a = a[a!=0]
a = [2]+list(a)

在Intel Core I5上,它返回第一个中的素数:

  • N = 100,000,0.03秒;
  • 在0.63秒内N = 1,000,000;
  • 在22.2秒内N = 10,000,000。

有人愿意在上述限制条件下分享更有效的CPU时间代码吗?

4 个答案:

答案 0 :(得分:6)

Eratosthenes的实际 NumPy筛子看起来像这样:

def sieve(n):
    flags = numpy.ones(n, dtype=bool)
    flags[0] = flags[1] = False
    for i in range(2, n):
        # We could use a lower upper bound for this loop, but I don't want to bother with
        # getting the rounding right on the sqrt handling.
        if flags[i]:
            flags[i*i::i] = False
    return numpy.flatnonzero(flags)

它维护一个"可能是素数"标志并直接取消对应于质数倍数的标志,而不需要测试可除性,特别是对于当前正在处理的素数不能被整除的数字。

你正在做的是试验分工,你只需要通过考验是否可以被候选除数整除。即使是试验部门的良好实施也需要做更多的操作,而且比筛子更昂贵的操作。你的实现确实比这更有用,因为它考虑了非素数候选除数,并且因为它不断对数字进行可分性测试,它应该已经知道它是素数。

答案 1 :(得分:1)

1.94秒,持续10.000.000

def sieve_eratosthene(limit):

    primes = [True] * (limit+1)
    iter = 0

    while iter < limit**0.5 :
        if iter < 2:
            primes[iter]= False

        elif primes[iter]:
            for i in range(iter*2, limit+1, iter):
                primes[i] = False

        iter+=1

    return(x for x in range(number+1) if primes[x])

答案 2 :(得分:1)

这是使用NumPy执行此操作的简单方法。它有点类似于OP的索引编制思想,而不是在主循环内再次循环编制,但没有除法检查,只能进行切片。

它也类似于user2357112支持Monica的答案,但是这个仅考虑奇数,这使得它更快。

这是典型的仅奇数筛子:https://stackoverflow.com/a/20248491/8094047

  1. 创建大小为n的布尔数组。
  2. 通过奇数循环直到n的平方根。
    注意:数字可以互换用作索引
  3. 将复合数字(质数)标记为false。

最后,我们有一个布尔数组,可以用来检查n以下的数字是否为质数(偶数除外,您可以使用&1或大约1来检查它们是否不包含数组)

平均n = 20000000的时间:0.1063s

import numpy as np
n = 20000000

isprime = np.ones(n, dtype=np.bool)

# odd only sieve
i = 3
while (i * i < n):
    if isprime[i]:
        isprime[i * i:n:2 * i] = False
    i += 2

# test
print(isprime[97]) # should be True

答案 3 :(得分:1)

我决定玩这个游戏,并创建了一个进一步优化的NumPy版本,该版本由 @ user2357112支持Monica 发布,该版本使用Numba JIT加快了工作进度。

import numba
import numpy
import timeit
import datetime 

@numba.jit(nopython = True, parallel = True, fastmath = True, forceobj = False)
def sieve (n: int) -> numpy.ndarray:
    primes = numpy.full(n, True)
    primes[0], primes[1] = False, False
    for i in numba.prange(2, int(numpy.sqrt(n) + 1)):
        if primes[i]:
            primes[i*i::i] = False
    return numpy.flatnonzero(primes)

if __name__ == "__main__":
    
    timestart = timeit.default_timer()
    print(sieve(1000000000))
    timestop = timeit.default_timer()
    timedelta = (timestop - timestart)
    print(f"Time Elapsed: {datetime.timedelta(seconds = timedelta)}")

else:
    pass

在笔记本电脑上,我在1e9秒内筛选出了前十亿(0:00:10.378686)个自然数中的质数。 JIT在这里至少提供了一个数量级的性能;在撰写本文时,下一个最快的答案花了0:01:27.059963分钟。可悲的是,我在该系统(Mac)上没有Nvidia GPU和Cuda,否则我会使用它。