【问题标题】:Find the sum of all the primes below two million.My program doesn't work for very big numbers找出所有低于 200 万的素数之和。我的程序不适用于非常大的数字
【发布时间】:2015-07-01 04:12:07
【问题描述】:

这是我查找素数和的代码。它适用于一些小数字,但如果它是 2000000(200 万),它永远不会结束。有人可以帮助我吗?

import java.math.BigInteger;
public class Problem010{
    public static void main(String[] args) {

        BigInteger sum = new BigInteger("2");

        //for (int i=3; i<2000000; i++) {
        for(int i=3; i<10; i++){
            for (int j=2; j<i; j++){
                if (i % j == 0) 
                    break;
                else if (i == j+1){
                    sum = sum.add(BigInteger.valueOf(i));
                }
            }
        }
        System.out.println("Sum  = "+sum); 
    }
}

【问题讨论】:

  • 可以建议这些 2, 1),而不是通过 2 到 i 运行内循环,而是通过 2 到 sqrt(i)。 2) 跳过偶数
  • 我敢肯定,您的程序需要很长时间才能完成。所以,改变你的算法。
  • 永远不会结束大约多少分钟或几小时?
  • 使用“Eratosthenes sieve”方法计算素数,然后求和。我希望它可以在“普通”PC 上在一秒钟内完成评估。
  • 这是来自 Project Euler 的问题。我们的想法是找到可以自己“快速”计算答案的算法。

标签: java algorithm primes


【解决方案1】:

你的答案是142913828922,但如何?

我只是稍微改变了你的算法:

public static void main(String[] args) {

    BigInteger sum = new BigInteger("2");
    boolean isPrime = true;
    for (int i=3; i<2000000; i++) {
    double aa = Math.sqrt((double)i);
        for (int j=2; j<=aa; j++){
            if (i % j == 0){ 
                isPrime = false;
                break;
            }
        }
        if(isPrime){
            sum = sum.add(BigInteger.valueOf(i));
        }
        isPrime = true;
    }
    System.out.println("Sum  = "+sum); 
}

我没有遍历从 2 到 i 的所有数字,而是从 2 到 sqrt(i),这大大提高了您的代码运行时间:)

【讨论】:

    【解决方案2】:

    @Lrrr,答案是正确的。但算法可以进一步优化。看看我的isPrime 算法。对于 200 万,您不需要 BigInteger

        long sum = 2;// new BigInteger("2");
        for (int i=3; i<2000000; i++) {
            if(isPrime(i)) {
                sum = sum + i;//.add(BigInteger.valueOf(i));
            }    
        }
        System.out.println("Sum  = "+sum);
    

    这里是 isPrime 方法。

     static boolean isPrime(int n) {
        if (n < 2) {
            return false;
        }
        if (n == 2 || n == 3) {
            return true;
        }
        if ((n & 1) == 0 || n % 3 == 0) {
            return false;
        }
        int sqrtN = (int) Math.sqrt(n) + 1;
        for (int i = 6; i <= sqrtN; i += 6) {// loop 6 step
            if (n % (i - 1) == 0 || n % (i + 1) == 0) {
                return false;
            }
        }
        return true;
    }
    

    【讨论】:

    • 你是绝对正确的 :) 不需要像 BigInteger 这样的课程 :) +1 为你的改进 :)
    【解决方案3】:

    你可以使用埃拉托色尼筛法,它比你的效率更高。

    1) 将 2 到 N 之间的所有数字存储在数组中,并将它们都标记为素数。

    2) 从 X = 2 开始,标记其所有 i*X (2X, 3X..),其中 i 是小于或等于 N 的自然数,乘数为非质数。不要标记 X。

    3) 找到下一个大于 X 且未标记的数字并重复该过程。如果没有这样的号码,请停止。

    4) 数组中剩余的数字是素数

    类似这样的:

    public static boolean[] findPrimes (int N) {

        boolean[] primes = new boolean[N + 1];
    
        // assume that all numbers are prime within given range
        for (int i = 2; i <= N; i++) {
            primes[i] = true;
        }
    
        // for all numbers in range, starting from 2
        for (int i = 2; i*i <= N; i++) {
    
            // mark natural multiples of i as nonprime
            if (primes[i]) {
                for (int j = i; i*j <= N; j++) {
                    primes[i*j] = false;
                }
           }
    
     return primes;
    }
    

    5) 迭代返回的素数和 TRUE 值的总和索引

    【讨论】:

    • 我认为这将非常低效,因为我们需要一次存储 200 万个数字
    • 该算法的位复杂度为 O(n (\log n) (\log \log n)) 位运算,内存需求为 O(n)。给定足够的内存,它的效率相当高。
    【解决方案4】:

    一个有效的解决方案是使用Sieve of Eratosthenes 找出低于 2,000,000(或任何其他数字)的素数,然后进行后处理并将它们全部相加:

        int n = 2000000;
        boolean[] isPrime = new boolean[n];
        //preprocess - set up the array
        for (int i = 2; i<n;i++) isPrime[i] = true;
        //run sieve:
        for (int i = 2; i < (int) Math.sqrt(n) + 1; i++) { 
            if (isPrime[i]) {
                for (int j = 2; j*i < n; j++) isPrime[i*j] = false;
            }
        }
        //sum primes:
        long sum = 0;
        for (int i = 2; i < n; i++) { 
            if (isPrime[i]) sum+=i;
        }
        System.out.println(sum);
    

    与一次检查每个数字是否为素数不同(这需要O(sqrt(n)) - 并且通过对所有数字执行此操作,您会得到O(nsqrt(n)),在这里您可以汇总以前迭代的知识,有效地降低O(nloglog(n)) 的复杂度,对于足够大的 n 值,它的速度要快得多。
    这是以O(n) 额外空间为代价的。

    【讨论】:

    • 其实最后一个for循环是没有必要的:)
    • @PhamTrung 可以在 siece 方法中就地完成,但在性能方面没有什么意义,而在可读性方面有很多意义。
    【解决方案5】:

    到目前为止,还没有人真正正确地实施了 Sieve。仔细检查维基百科页面,注意你是如何循环遍历这些数字的。如果不使用 int(或布尔值)数组进行任何优化,在 Java 中应该只需要几秒钟。

    【讨论】:

    • 如果您认为这两个筛子的答案不正确,请更具体。
    【解决方案6】:

    我开发了自己的解决方案,它在 700 毫秒内完成,找到所有低于 200 万的人。

    我使用迭代方法,但我只是停止寻找大于 (n/i)+1 的数字,其中 n 是被检查的数字,如果它是素数而i是迭代循环中的数,看是否为除数。

    public void run () {
        long sumOfPrimes = 2;
        int maxNumber = 2000000;
        int counter = 0;
    
        for (int i = 3; i <= maxNumber; i = i+2) {
            if(isPrimeOptimized(i)){
                sumOfPrimes = sumOfPrimes + i;
                counter ++;
            }
        }
        System.out.println("num of primes is " + counter);
        System.out.println("sum of primes is " + sumOfPrimes);
    }
    
    private boolean isPrimeOptimized(int n){
        int limitToDivide = n;
        for(int i=2;i<=limitToDivide && i<n;i++){
            if(n%i == 0)
                return false;
            else
                limitToDivide = (n/i) + 1;
        }
        return true;
    }
    

    【讨论】:

      猜你喜欢
      • 2013-05-28
      • 2014-12-31
      • 1970-01-01
      • 1970-01-01
      • 2011-05-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多