找到不到200万的所有素数之和需要多长时间?

时间:2010-12-22 11:30:37

标签: java algorithm logic primes

我试图解决这个Project Euler Question。我实现了euler的筛子作为java中的帮助类。它适用于小数字。但是,当我输入200万作为限制时,它不会返回答案。我使用Netbeans IDE。我等了很多个小时一次,但仍然没有打印答案。当我停止运行代码时,它给出了以下结果

  

Java结果:2147483647
  建立   成功(总时间:2,097分钟   43秒)

这个答案是不正确的。即使等了这么多时间,这也是不正确的。虽然相同的代码会返回较小限制的正确答案。

在这个page的底部给出了一个非常简单的算法。

我的实现是:

package support;

import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author admin
 */
public class SieveOfEuler {
    int upperLimit;
    List<Integer> primeNumbers;

    public SieveOfEuler(int upperLimit){
        this.upperLimit = upperLimit;
        primeNumbers = new ArrayList<Integer>();
        for(int i = 2 ; i <= upperLimit ; i++)
            primeNumbers.add(i);
        generatePrimes();
    }

    private void generatePrimes(){
        int currentPrimeIndex = 0;
        int currentPrime = 2;
        while(currentPrime <= Math.sqrt(upperLimit)){
            ArrayList<Integer> toBeRemoved = new ArrayList<Integer>();
            for(int i = currentPrimeIndex ; i < primeNumbers.size() ; i++){
                int multiplier = primeNumbers.get(i);
                toBeRemoved.add(currentPrime * multiplier);
            }

            for(Integer i : toBeRemoved){
                primeNumbers.remove(i);
            }

            currentPrimeIndex++;
            currentPrime = primeNumbers.get(currentPrimeIndex);
        }
    }

    public List getPrimes(){
        return primeNumbers;
    }

    public void displayPrimes(){
        for(double i : primeNumbers)
            System.out.println(i);
    }
}

我很困惑!我的问题是

1)为什么要花这么多时间?我正在做的事情有什么问题吗?

如果您发现错误,请提出改进​​编码风格的方法。

问题已更新:

这是代码,我计算计算的素数之和:

package projecteuler;

import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import support.SieveOfEuler;

/**
 *
 * @author admin
 */
public class Problem10 {
    private int upperLimit;
    private BigInteger sumOfPrimes;
    public void getInput() {
        try {
            System.out.println("Enter the upper limit");
            upperLimit = Integer.parseInt(br.readLine());
        } catch (IOException ex) {
            Logger.getLogger(Problem10.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void execute() {
        BigInteger sum = new BigInteger("0");
        SieveOfEuler soe = new SieveOfEuler(upperLimit);
        ArrayList<Integer> primeNumbers = (ArrayList<Integer>)soe.getPrimes();
        for(int i : primeNumbers){
            sum = sum.add(new BigInteger(Integer.toString(i))) ;
        }
        System.out.println(sum);
    }

    public void printOutput() {
       //System.out.println(sumOfPrimes);
    } 
}

9 个答案:

答案 0 :(得分:10)

你的Sieve太慢的原因是你犯了一个根本性的错误。 primeNumbers应该是一个布尔数组,而不是List。完成后,对于素数,primeMumbers[i]的值为true,对于复合数,false的值为O(1)

这就是为什么它会产生如此大的不同:

  • 在数组中设置或清除标志为ArrayList;即每次操作的时间很短。
  • O(N)中移除元素N,其中ArrayList.remove(...)是列表的大小... 非常大
  • 每个ArrayList.remove(...)操作都必须搜索列表。如果该值不再存在(因为您已经删除了它),则删除操作必须查看列表中的每个剩余元素...最多约200万个...每次被召唤。
  • 当{{1}}找到一个元素时,它会通过将元素一个索引之后的所有剩余元素复制到支持数组中的左侧来删除它。同样,每次删除一个条目时,您最多可复制约200万个条目。

我希望一个实施良好的Erasothenes筛子能够在几秒钟内计算出不到200万的所有素数。

答案 1 :(得分:4)

for(int i = currentPrimeIndex ; i < primeNumbers.size() ; i++){
    int multiplier = primeNumbers.get(i);
    toBeRemoved.add(currentPrime * multiplier);
}

在第一步,这将生成一个200万倍的2的数组列表(toBeRemoved)。

然后迭代到BeRemoved,为toBeRemoved中的每个条目扫描 200万个候选素数的整个数组。 toBeRemoved中的一半值不可能在primeNumbers中,因为它们太大了。每次删除都会导致每个值的索引都大于删除的索引,并向下移动一个位置。

我认为这是效率低下的主要原因。实现Eratosthenes筛子的通常方法是创建一个包含200万个布尔值的数组,最初都是真的。确定i为非素数​​时,请将possibly_prime[i]设置为false。要查找下一个素数,请向前扫描以查找true值。要获得最后所有素数的列表,请迭代记录每个true值的索引的数组。对于欧拉的筛子,你应该做的几乎一样。

对于高达200万的素数,你不需要进行优化。

答案 2 :(得分:4)

回答本主题标题中的问题:这是项目Euler网站所说的内容:http://projecteuler.net/index.php?section=about

  

我已经编写了我的程序但是需要几天才能得到答案吗?   绝对不!每个问题都是根据“一分钟规则”设计的,这意味着虽然设计一个成功的算法可能需要几个小时才能解决更多难题,但有效的实施方案可以让解决方案成为现实。在不到一分钟的时间内在一台中等电脑上获得。

- )

答案 3 :(得分:2)

一些明显的错误:

while(currentPrime <= Math.sqrt(upperLimit))

每个步骤都计算平方根(除非编译器优化)。您应该计算一次并存储结果。

currentPrimeIndex++;

你应该至少做出明显的优化,只测试奇数。你已经知道偶数不是素数。这应该把你的时间缩短一半。

此外,您正在使用强力方法来查找素数。对于大的上限,这将是缓慢的。您应该为搜索使用更好的算法 - 您将能够在网络中找到更多信息,但我不知道是否允许这是项目Euler的精神。

答案 4 :(得分:1)

import java.util.*;

public class PrimeSum
{
    public static int isPrime(int num)
    {
        int sum = 0;
        int factor = 1;
        while(factor <= num)
        {
            if(num % factor != 0)
            {
                sum += factor;
                factor ++;
            }
            else
            {
                factor ++;
            }
        }
        return sum;
    }

    public static void main(String[] args)
    {
        System.out.println("The program gets the sum of all prime numbers.");

        Scanner scan = new Scanner(System.in);

        System.out.print("Enter a number: ");
        int num = scan.nextInt();

        int sum = isPrime(num);

        System.out.println(sum);
    }
}

答案 5 :(得分:0)

while(currentPrime&lt; = Math.sqrt(upperLimit))//它降低了复杂性并再增加一点 总和不是 int 。发生溢出

如果它可以帮助您查看我的解决方案。这是我的解决方案

public static boolean isPrime(int i) { // general isPrime method
      if (i < 2) {
         return false;
      } else if (i % 2 == 0 && i != 2) {
          return false;
      } else {
           for (int j = 3; j <= Math.sqrt(i); j = j + 2) {
               if (i % j == 0) {
                  return false;
                }
           }
      }

  return true;
 }

    public static boolean isPrimeForOdd(int i){ // only for odds
        for (int j = 3; j <= Math.sqrt(i); j = j + 2) {
             if (i % j == 0) {
                return false;
              }
        }

      return true;
     }

 public static long sumOfPrimes(int n) {
    long sum = 2;

    for (int i = 3; i < n; i += 2) {
          if (isPrimeForOdd(i)) {
              sum += i;
          }
    }

    return sum;
 }

 public static void main(String[] args) throws ParseException {
      System.out.println(sumOfPrimes(2000000));
 }

答案 6 :(得分:0)

  1. 列表是正确的类型吗?在remove(obj)期间,Set会表现得更好。在您的情况下,请尝试BitSet

  2. 首先创建一个(长)要删除的元素列表,然后单独删除它们。为什么不简单地删除它们?

  3. 结果不适合int

答案 7 :(得分:0)

即使没有筛子,这个问题也可以在不到1秒的时间内解决。检查数字是否为素数:http://www.parseexception.com/how-to-check-whether-a-number-is-prime-or-not/

对所有数字执行此操作并将它们相加。

答案 8 :(得分:0)

这是一个使用简单的Eratosthenes筛子的解决方案:

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

我在Python here中对此解决方案进行编码,这需要一到四分之一秒。如果您对使用素数进行编程感兴趣,我会在我的博客上谦虚地推荐这个essay