项目欧拉#10(Python)

时间:2013-07-08 10:46:09

标签: python python-2.7

为什么我的算法用于查找低于200万的所有素数之和如此之慢? 我是一个相当初学的程序员,这就是我想出的解决方案:

import time

sum = 2
start = time.time()

for number in range(3, 2000000):
    prime = True
    for x in range(2, number):
        if number % x == 0:
            prime = False
    if prime:
        sum += number

print "Sum =", sum
end = time.time() - start
print "Runtime =", end

有人可以帮帮我吗? 谢谢!

9 个答案:

答案 0 :(得分:3)

您可以做很多优化(并且应该这样做,因为您需要对项目Euler中的许多问题进行素数生成,因此快速实现会在以后简化事情)。

看看阿特金(和相关的筛子)(http://en.wikipedia.org/wiki/Sieve_of_Atkin)的筛子,以了解如何通过蛮力加速素数生成(算法即可)。

然后看看这个S.O.-帖子(Fastest way to list all primes below N)的精彩答案,它为许多素数生成算法/实现提供时钟。

答案 1 :(得分:3)

没有人指出这一点,但在Python 2.x中使用range非常慢。使用xrange instaed,在这种情况下,这应该会给您带来巨大的性能优势 见this question.

此外,您不必循环直到您检查的号码,检查直到round(sqrt(n)) + 1足够。 (如果大于其平方的数字除以它,则有一个小于您必须注意到的方形的数字。)

答案 2 :(得分:3)

您的算法使用试验分割,这非常慢。一个更好的算法使用了Eratosthenes的Sieve:

def sumPrimes(n):
    sum, sieve = 0, [True] * n
    for p in range(2, n):
        if sieve[p]:
            sum += p
            for i in range(p*p, n, p):
                sieve[i] = False
    return sum

print sumPrimes(2000000)

那应该在不到一秒的时间内完成。如果您对使用素数进行编程感兴趣,我会在我的博客上谦虚地推荐这个essay

答案 3 :(得分:1)

首先,你要循环过多的数字。你不需要检查是否每个小于给定数字的数字都是一个除数来检查一个数字是否为素数(我会让你弄清楚为什么这是你自己)。您正在运行数千亿个循环,其中有数亿个循环。

这样的东西工作得更快,但绝不是最佳的:

    value=2
    for i in range(3, 2000000):
        prime=True 
        if i%2 != 0:
            for j in range(3, int(round(sqrt(i)+1)),2):
                if i % j==0:
                    prime=False
        else:
            prime=False
        if prime==True:
            value+=i
    print value

答案 4 :(得分:1)

你需要使用素筛检查eratostheneses筛,并尝试在代码中实现它。

试验分割对于寻找素数是非常低效的,因为它具有复杂度n平方,运行时间变得非常快。这项任务旨在教你如何更好地找到更好的东西。

答案 5 :(得分:1)

首先,我认为您可以通过定义函数来拆分代码。但是,在这种情况下使用常规函数存在一个缺点,因为每次正常函数return一个值时,对该函数的下一次调用将再次执行函数内的完整代码。由于你要迭代200万次,最好是:

  • 有一个函数可以为您提供下一个素数,并暂时将控件返回给调用者。这些功能称为GENERATORS
  • 要定义生成器函数,只需使用yield命令而不是return
  • 当您使用生成器时,就像知道将再次调用该函数一样,当它发生时,函数内部的执行会在yield指令之后继续执行,而不是再次遍历整个函数。
  • 这种方法的优点是,从长远来看,你可以避免占用系统的所有内存。

我建议你看一下this article about generators in python。它为此示例提供了更广泛的解释。

解决方案是这样的:

import math

# Check if a number is prime
def is_prime(number):
    if number > 1:
        if number == 2:
            return True
        if number % 2 == 0:
            return False
        for current in range(3, int(math.sqrt(number) + 1), 2):
            if number % current == 0: 
                return False
        return True
    return False

# Get the next after a given number
def get_primes(number):
    while True:
        if is_prime(number):
            yield number
        # Next call to the function will continue here!   
        number += 1 

# Get the sum of all prime numbers under a number
def sum_primes_under(limit):
    total = 2
    for next_prime in get_primes(3):
        if next_prime < limit:
            total += next_prime
        else:
            print(total)
            return

# Call the function
sum_primes_under(2000000)

答案 6 :(得分:0)

当您使用eratosthenes Link to it筛时,此问题可以非常快速地输出。你可以通过一些修改使它变得更快,例如只考虑奇数就可以将整个200万个数字迭代一半。这样可以节省大量时间。

n = 2000000
ar = [False for x in range(n)]
sum = 2
def mul(a):
    i = 2;p = i*a
    while (p < n):
        ar[p] = 1
        ++i
        p = i*a
while (x < n):
    if(ar[x] == 0):
        sum += x;mul(x)
    x += 2
print (sum)

在这里你可以看到c ++中的相同算法: -

#include<bits/stdc++.h>
using namespace std;
const int n = 2000000;
bool ar[n];
void mul(int a)
{
    int i = 2;int p = i*a;
    while(p < n)
    {
        ar[p] = 1;
        ++i;p = i*a;
    }
}
long long sieve()
{
    long long sum = 2;
    for(int i = 3;i < n;i += 2)
    {
        if(ar[i] == 0)
            sum += i,mul(i);
    }
    return sum;
}
int main()
{
    cout<<sieve();
    return 0;
}

C ++的工作速度大约是python的10倍,对于这个算法也是如此。

答案 7 :(得分:0)

sum = 2

def isPrime(n):
    if n % 2 == 0: return False
    for i in range(3, int(n**0.5)+1, 2):
        if n % i == 0: return False
    return True
if __name__ == "__main__":
    n = 1
    while n < 2000000:
        n += 2
        if isPrime(n):sum += n
print sum

答案 8 :(得分:0)

import time
start = time.time()

def is_prime(num):
    prime = True
    for i in range(2,int(num**0.5)+1):
        if num % i == 0:
            prime = False
            break
    return prime
sum_prime = 0
for i in range(2,2000000):
    if is_prime(i):
        sum_prime += i
print("sum: ",sum_prime)

elapsed = (time.time() - start)
print("This code took: " + str(elapsed) + " seconds")