项目Euler 10:简单的代码没有给出理想的结果,需要帮助理解原因

时间:2013-05-28 10:22:16

标签: c# primes

我正在解决项目欧拉问题,我目前排在第10位。

首先:我知道还有其他解决方案,我目前正在使用Eratosthenes的筛子编写另一种方法我希望您的帮助是理解为什么此代码不起作用

这是我的代码(问题涉及查找每个素数低于200万的总和)。素数检查方法似乎工作正常,但结果比应有的少。

class Euler10
{
    public static void Main()
    {
        long sum = 0; // Was originally an int. Thanks Soner Gönül!
        for(int i = 1; i < 2000000; i++) 
        {
            if (CheckIfPrime(i) == true)
                sum += i;
        }
        System.Console.WriteLine(sum);
        System.Console.Read();
    }

    static bool CheckIfPrime(int number)
    {
        if (number <= 1)
            return false;
        if (number == 2)
            return true;
        if (number % 2 == 0)
            return false;

        for (int i = 3; i*i < number; i += 2)
        {
            if ((number % i) == 0)
                return false;
        }
        return true;
    }
}

我得到的数字是1,308,111,344,比它应该低两个数量级。代码很简单,我对此错误感到困惑。

编辑:总结很长时间解决了数字问题,谢谢大家!现在,我得到了143042032112作为答案:CheckIfPrime()中的i * i并不总是正确的。 使用sqrt()函数并添加一个(以补偿int cast)会得到正确的结果。这是正确的CheckIfPrime()函数:

 bool CheckIfPrime(int number)
    {
        if (number <= 1)
            return false;
        if (number == 2)
            return true;
        if (number % 2 == 0)
            return false;
        int max = 1 + (int)System.Math.Sqrt(number);
        for (int i = 3; i < max; i += 2)
        {
            if ((number % i) == 0)
                return false;
        }
        return true;
    }

编辑2: Ness会帮助我进一步优化代码(计算数字的平方根并将其与i进行比较比提升i ^ 2慢,然后将其与数字进行比较):问题与原始方法是它没有考虑到数字是i的确切平方的边缘情况,因此有时返回true而不是false。然后,CheckIfPrime()的正确代码是:

bool CheckIfPrime(int number)
    {
        if (number <= 1)
            return false;
        if (number == 2)
            return true;
        if (number % 2 == 0)
            return false;

        for (int i = 3; i*i <= number; i += 2)
        {
            if ((number % i) == 0)
                return false;
        }
        return true;
    }

再次感谢大家!

5 个答案:

答案 0 :(得分:3)

您的代码无效,因为它尝试使用32位int来保存超过32位变量中最高值的数字。问题的答案是142,913,828,922,需要38位。

sum的数据类型更改为long可以解决此问题。

答案 1 :(得分:2)

您正在使用sum 32-bit变量2,147,483,647,但您尝试将其分配给143,042,032,112以上32

但是你的结果是64,它需要比long sum = 0; 更多的位来存储它。

将其类型设置为int,其中存储{{1}}位。

{{1}}

这是一个有效的Int32.Maximum

答案 2 :(得分:2)

使用long应该有所帮助。http://msdn.microsoft.com/en-us/library/ctetwysk.aspx 给你一个64位整数,其中int只有32位。

答案 3 :(得分:2)

for (... i=3 ; i*i < number; i+=2 )错了。它应该是

for (... i=3 ; i*i <= number; i+=2 )
 ...

inumber必须属于同一类型的课程;这几乎永远不会给你一个溢出,因为你从3 开始(除非number非常大,接近类型范围的上限)

比较应具有包容性,以捕捉number是素数平方的情况。

答案 4 :(得分:1)

你的算法不是 Eratosthenes的筛子;模运算符将它放弃。您的算法是由奇数进行的试验除法。这是一个使用Eratosthenes筛选的函数:

function sumPrimes(n)
    sum := 0
    sieve := makeArray(2..n, True)
    for p from 2 to n step 1
        if sieve[p]
            sum := sum + p
            for i from p * p to n step p
                sieve[i] := False
    return sum

致电sumPrimes(2000000)给出了答案,我不会因为尊敬Project Euler而写下这些答案。该函数在时间O( n 日志日志 n )中运行,这比原始程序的O( n ^ 2)要好得多。有更好的筛选方法,但这很简单,容易正确,并且对于大多数目的而言都足够好,包括你的。你应该在不到一秒的时间内得到答案。

我会留给你用适当的数据类型翻译成C#。

如果您对此问题的某个较大版本感兴趣,我在我的博客上计算了the sum of the first billion primes