简体   繁体   English

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

[英]How much time should it take to find the sum of all prime numbers less than 2 million?

I was trying to solve this Project Euler Question . 我试图解决这个项目欧拉问题 I implemented the sieve of euler as a helper class in java. 我实现了euler的筛子作为java中的帮助类。 It works pretty well for the small numbers. 它适用于小数字。 But when I input 2 million as the limit it doesn't return the answer. 但是,当我输入200万作为限制时,它不会返回答案。 I use Netbeans IDE. 我使用Netbeans IDE。 I waited for a lot many hours once, but it still didn't print the answer. 我等了很多个小时一次,但仍然没有打印答案。 When I stopped running the code, it gave the following result 当我停止运行代码时,它给出了以下结果

Java Result: 2147483647 Java结果:2147483647
BUILD SUCCESSFUL (total time: 2,097 minutes 43 seconds) 建立成功(总时间:2,097分43秒)

This answer is incorrect. 这个答案是不正确的。 Even after waiting for so much time, this isn't correct. 即使等了这么多时间,这也是不正确的。 While the same code returns correct answers for smaller limits. 虽然相同的代码返回较小限制的正确答案。

Sieve of euler has a very simple algo given at the botton of this page . 在这个页面的底部给出了一个非常简单的算法。

My implementation is this: 我的实现是这样的:

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);
    }
}

I am perplexed! 我很困惑! My questions is 我的问题是

1) Why is it taking so much time? 1)为什么要花这么多时间? Is there something wrong in what I am doing? 我在做什么有什么不对吗?

Please suggest ways for improving my coding style, if you find something wrong. 如果您发现错误,请建议改进​​我的编码风格的方法。

Question Updated: 问题更新:

Here is the code, where I calculate the sum of the the calculated prime numbers: 这是代码,我计算计算的素数之和:

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);
    } 
}

The reason that your Sieve is so slow is that you have made a fundamental mistake. 你的Sieve速度太慢的原因是你犯了一个根本性的错误。 The primeNumbers should be an array of booleans, not a List . primeNumbers应该是一个布尔数组,而不是List When you are finished, the value of primeMumbers[i] will be true for prime numbers and false for composites. 完成后, primeMumbers[i]的值对于素数将为true ,对于复合而言为false

Here's why it makes such a big difference: 这就是它产生如此巨大差异的原因:

  • Setting or clearing a flag in array is O(1) ; 设置或清除数组中的标志是O(1) ; ie a small constant time per operation. 即每次操作的恒定时间很短。
  • Removing an element from an ArrayList is O(N) where N is the size of the list ... and very large . ArrayList删除元素是O(N) ,其中N是列表的大小......并且非常大
  • Each ArrayList.remove(...) operation has to search the list. 每个ArrayList.remove(...)操作都必须搜索列表。 If the value is no longer there (because you've already removed it), the remove operation has to look at every remaining element in the list ... up to ~2 million of them ... each time it is called. 如果该值不再存在(因为您已经删除了它),则删除操作必须查看列表中的每个剩余元素......每次调用时最多约200万个...每次调用它。
  • When ArrayList.remove(...) finds an element, it removes it by copying all remaining elements after the element one index to the left in the backing array. ArrayList.remove(...)找到一个元素时,它会通过将元素一个索引后面的所有剩余元素复制到后备数组中的左侧来删除它。 Again, you are copying up to ~2 million entries ... each time you remove one. 同样,每次删除一个条目时,您最多可复制约200万个条目。

I'd expect a well implemented Sieve of Erasothenes to be able to calculate all prime numbers less than 2 million in a few seconds. 我希望一个实施良好的Erasothenes筛子能够在几秒钟内计算出不到200万的所有素数。

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

At the first step, this generates an ArrayList of 2 million multiples of 2 (toBeRemoved). 在第一步,这将生成一个200万倍的2的ArrayList(toBeRemoved)。

Then it iterates over toBeRemoved, scanning the entire array of 2 million candidate primes once for each entry in toBeRemoved. 然后它迭代到BeRemoved,为toBeRemoved中的每个条目扫描整个200万个候选素数的数组 Half the values in toBeRemoved can't possibly be in primeNumbers because they're too big. toBeRemoved中的一半值不可能是primeNumbers因为它们太大了。 Each removal results in every value with a greater index than the one removed, being shifted down one place. 每次删除都会导致每个值的索引大于删除的索引,向下移动一个位置。

I think this is the major source of inefficiency. 我认为这是效率低下的主要原因。 The usual way to implement the sieve of Eratosthenes is to create an array of 2 million boolean values, initially all true. 实现Eratosthenes筛子的通常方法是创建一个包含200万个布尔值的数组,最初都是真的。 When i is determined to be non-prime, set possibly_prime[i] to false. i被确定为非素数时,将possibly_prime[i]设置为false。 To find the next prime, scan forward looking for a true value. 要查找下一个素数,请向前扫描以查找true值。 To get a list of all primes at the end, iterate the array recording the index of each true value. 要获得最后所有素数的列表,请迭代记录每个true值的索引的数组。 You should do pretty much the same for the sieve of Euler. 你应该为欧拉的筛子做同样的事情。

You won't need to optimize beyond that for primes up to 2 million. 对于高达200万的素数,您不需要优化。

To give an answer to the question in this topic title: this is what the project Euler web site says: http://projecteuler.net/index.php?section=about 为了回答这个主题标题中的问题:这是项目Euler网站所说的: http//projecteuler.net/index.php?section = about

I've written my program but should it take days to get to the answer? 我已经编写了我的程序但是需要几天才能得到答案吗? Absolutely not! 绝对不! Each problem has been designed according to a "one-minute rule" , which means that although it may take several hours to design a successful algorithm with more difficult problems, an efficient implementation will allow a solution to be obtained on a modestly powered computer in less than one minute. 每个问题都是根据“一分钟规则”设计的 ,这意味着虽然设计一个成功的算法可能需要几个小时才能解决更加困难的问题,但是有效的实施方案可以在一个适度的计算机上获得解决方案。不到一分钟。

;-) ;-)

Some obvious mistakes: 一些明显的错误:

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

Square root is calculated in every step (unless optimized by the compiler). 在每个步骤中计算平方根(除非由编译器优化)。 You should calculate it once and store the result. 您应该计算一次并存储结果。

currentPrimeIndex++;

You should at least make the obvious optimization and only test odd numbers. 你应该至少做出明显的优化,只测试奇数。 You already know that even numbers aren't primes. 你已经知道偶数不是素数。 This should cut your time in half. 这应该把你的时间缩短一半。

Also, you are using a brute force method to find the prime numbers. 此外,您正在使用强力方法来查找素数。 This will be slow for large upper limits. 对于大的上限,这将是缓慢的。 You should use a better algorithm for your search - you will be able to find more info in the web, However I don't know if this is allowed in the spirit of Project Euler. 您应该为搜索使用更好的算法 - 您将能够在网络中找到更多信息,但我不知道是否允许这是Project Euler的精神。

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);
    }
}

Here is a solution using a simple Sieve of Eratosthenes: 这是一个使用简单的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

I code this solution in Python here , where it takes one-and-a-quarter seconds. 我在这里用Python编写这个解决方案,需要一秒钟的时间。 If you're interested in programming with prime numbers, I modestly recommend this essay at my blog. 如果你对使用素数编程感兴趣,我谦虚地在我的博客上推荐这篇文章

while(currentPrime <= Math.sqrt(upperLimit)) // It reduces the complexity and one more point the sum is not int . while(currentPrime <= Math.sqrt(upperLimit))//它降低了复杂性,并且总和不是int的另一点。 overflow occurs 发生溢出

If it helps you you look at my solution. 如果它可以帮助您查看我的解决方案。 Here is my solution 这是我的解决方案

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));
 }
  1. Is list the correct type for this? 列表是正确的类型吗? A Set would perform much better during remove(obj) . remove(obj)期间,Set会表现得更好。 In your case, try a BitSet . 在您的情况下,尝试一个BitSet

  2. You first create a (long) list of elements to remove and then remove them individually. 首先创建一个(长)要删除的元素列表,然后单独删除它们。 Why not simply remove them? 为什么不简单地删除它们?

  3. The result doesn't fit in an int . 结果不适合int

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

Perform this operation for all numbers and sum them. 对所有数字执行此操作并将它们相加。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 所有素数小于200万的总和 - Sum of all prime numbers below 2 million 小于200万的所有质数之和 - Sum of all primes less than 2 million 如何在java中找到1到50之间的所有素数的总和? - how to find the sum of all the prime numbers between 1 and 50 in java? 为什么我的程序为所有小于100而不是小于100万的质数的总和生成正确的输出? - Why is my program generating the correct output for sum of all prime numbers under 100, but not under 1 million? 计算小于或等于N的两个数字的对数,以使对数的数字总和为质数 - Count number of pairs of two numbers less than or equal to N such that Sum of the digits of numbers of pair is Prime 如何找到最大的质数,除1外的最小因数 - How to find Largest Prime Number, Smallest Factor except 1, Sum of Numbers 试图找到偶数斐波那契数的总和为 400 万 - trying to find the sum of even fibonacci numbers to 4 million 找出200万以下的所有素数之和。我的程序不适用于很大的数 - Find the sum of all the primes below two million.My program doesn't work for very big numbers 在小于 N 的数字中找到每个数字的总和为 d 的数字的数量 - Find the number of numbers where the sum of each number is d in numbers less than N Java程序以查找2,000,000以下的所有素数之和(欧拉计画) - Java Program to find sum of all prime numbers below 2,000,000 (Project Euler)
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM