素数发生器优化(项目Euler#7)

时间:2017-07-09 03:29:44

标签: python optimization

我想优化这段代码,以便缩短完成此任务的时间(我确实希望继续打印所有素数):

(获得第10001个素数)

counter = 10001
target_num = 1          #because if we add 1 to it the first time, it will become 2
input('This may take a while, press Enter to continue; you can stop the program any time by pressing CTRL + C.')
while counter <= 10001 and counter > 0:
    target_num = target_num + 1
    for x in range(1, target_num + 1):
        if target_num % x == 0:
            if x == 1:
                pass
            elif x != target_num:
                break
            elif x == target_num:
                counter = counter - 1
                print (target_num)  #prints prime number so user knows if program is actually crunching numbers
print ('10001st prime number is: ' + str(target_num))

2 个答案:

答案 0 :(得分:3)

我计算了你的代码。这需要多长时间:

Over 72 seconds (%timeit died, sorry!)

明显的问题是你将循环从1运行到target_num以找到素数。那个不是是必要的。

可以看到here的证据。

这是一个运行到平方根的版本。您也可以删除那些多余的if

def foo():
    counter = 10001
    target_num = 1          #because if we add 1 to it the first time, it will become 2
    while counter <= 10001 and counter > 0:
        target_num = target_num + 1
        for x in range(2, int(target_num ** 0.5) + 1 ):
            if target_num % x == 0:
                if x != target_num:
                    break
        else:
            counter -= 1

    print ('10001st prime number is: ' + str(target_num))

定时:

1 loop, best of 3: 442 ms per loop

进一步加速可以通过步进范围2来实现,因此您可以跳过对偶数的检查:

def foo():
    counter = 10000
    target_num = 1          
    while counter <= 10001 and counter > 0:
        target_num = target_num + 2
        ...

定时:

1 loop, best of 3: 378 ms per loop

脚注: 这个答案解释了您当前方法的不足之处。但是,对于一种全新的方法,请查看Sieve of Eratosthenes - Finding Primes Python或@ AChampion的答案。

答案 1 :(得分:1)

快速素数生成器有很多资源 例如,您可以安装提供的primesieve

In []:
import primesieve
primesieve.nth_prime(10001)

Out[]:
104743

In []:
%timeit primesieve.nth_prime(10001)

Out[]:
28.3 µs ± 549 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

简单筛子的原生python实现:

In []:
import itertools as it

def primes():
    yield 2
    sieve = {}
    for n in it.count(3, 2):
        if n not in sieve:
            sieve[n*n] = 2*n
            yield n
            continue
        a = sieve.pop(n)
        x = n + a
        while x in sieve:
            x += a
        sieve[x] = a

def nth_prime(n):
    return next(it.islice(primes(), n-1, None))

nth_prime(10001)

Out[]:
104743

In []:
%timeit nth_prime(10001)

Out[]:
26.3 ms ± 629 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)