【问题标题】:Optimize Speed of Prime Number Sieve (Java)优化素数筛的速度(Java)
【发布时间】:2016-03-31 01:35:46
【问题描述】:

我正在研究一种在 Java 中创建布尔数组 isPrime 的方法:

boolean[] isPrime;

其中素数标记为“真”,其余标记为“假”。
在此过程中,我还想计算找到的 Prime 数量:

int numPrimesFound;

基本思想是使用埃拉托色尼筛。到目前为止,我的方法如下所示:

public Sieve(final int limit) {

    long startTime = System.currentTimeMillis();

    boolean[] isPrime = new boolean[limit];
    this.isPrime = isPrime;

    for (int i=3; i<=limit ;i+=2) {
        isPrime[i] = true;                        //sets all even numbers to true
    }

    isPrime[2] = true;
    numPrimesFound = 1;                           //special case of '2'

    for (int i = 3; i * i <= limit; i += 2) {
        if (isPrime[i] == true) {
            for (int j = i; i * j <= limit; j += 2) {

                isPrime[i * j] = false;           //has a multiple ==> not a prime

                numPrimesFound++;                 //doesn't work yet
            }
        }
    }

    long stopTime = System.currentTimeMillis();   //measuring execution time
    System.out.println("Sieve: " + (stopTime - startTime) + " milliseconds.")

}

所以我的问题是

numPrimesFound++:

不起作用,因为筛子将一些非素数的值设置为“假”不止一次(例如 45 bcs 3*15 = 45 和 9*5 = 45)。
那么有没有人知道我可以如何重写这个程序,以便将所有非质数设置为“假”只有一次

或者一般来说,任何人都可以提出使该方法更快的方法吗?

【问题讨论】:

  • 您对哪个结果感兴趣:素数计数,还是素数本身?如您所见,numPrimesFound 是错误的,但这与算法查找素数的速度无关。另外:您可能应该阅读How to write a correct microbenchmark in Java
  • 去掉内部循环中的乘法,只留下加法:i*(j + 2) == i*j + 2*i

标签: java primes sieve-of-eratosthenes sieve


【解决方案1】:

如果你使用BitSet,你可以要求它是cardinality

public BitSet primes(final int limit) {

    long startTime = System.currentTimeMillis();
    BitSet isPrime = new BitSet(limit);
    // A bitSet starts all zeros but with a sieve - evrything must start prime.
    isPrime.flip(0, limit);

    // 0 and 1 are not prime
    isPrime.clear(0);
    isPrime.clear(1);

    for (int i = 2; i * i <= limit; i += 2) {
        if (isPrime.get(i)) {
            // Remove all multiples of i.
            for (int j = 2; i * j <= limit; j += 1) {
                isPrime.clear(i * j);
            }
        }
    }

    long stopTime = System.currentTimeMillis();   //measuring execution time
    System.out.println("Sieve: " + (stopTime - startTime) + " milliseconds.");
    return isPrime;
}

public void test() {
    BitSet primes = primes(50);
    System.out.println("Primes: " + primes);
    System.out.println("Count: " + primes.cardinality());
}

我还修正了您逻辑中的几个错误。例如。您的内部循环将j 步进2,而您的外部循环并未删除所有偶数(从3 开始)。

您当然可以改进这一点 - 谷歌是您的朋友。 :)

【讨论】:

  • 为了避免初始翻转,使用 set 而不是 clear 然后返回 limit - cardinality
【解决方案2】:

这是我的版本(它只计算包含输入数字的可能素因数的集合):

import java.util.BitSet;
import java.util.Scanner;

public class Main {

public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    System.out.println("Enter the interval limit: ");
    int limit = sc.nextInt();
    int max = (int) Math.sqrt(limit);
    long start = System.currentTimeMillis();
    BitSet intArray = new BitSet(max);

    // 1 is not prime
    intArray.set(0);

    // 2 is the first prime number, so the first step is to filter out all even numbers
    for (int i = 2; i < limit; i+=2) {
        intArray.set(i);
    }

    //all remaining number will be odd
    int currentNumber = 3;
    // i is the multiplicator and will be adjusted with the current number , in order to avoid repetition
    int i = 3;
    int temp;

    while (currentNumber <= max) {
        // flag multiple of the current prime number
        while (true) {
            temp = (currentNumber * i);
            if (temp > max) break;
            intArray.set(temp - 1);
            i += 2;
        }
        //all non-prime numbers until now are already flagged, therefore we can find the next prime number by checking the set-status.
        while (true) {
            currentNumber += 2;
            if (currentNumber > max) break;
            if (!intArray.get(currentNumber - 1)) {
                i = currentNumber;
                break;
            }
        }
    }

    int b = 0;
    for (int n = max -1 ; n > 0; n--){
        if (!intArray.get(n)){
            b = n + 1;
            break;
        }
    }
    System.out.println("There are " + (max - intArray.cardinality()) + " PRIMES and the biggest is: " + b);
    System.out.println("Time in total: " + ((System.currentTimeMillis() - start) / 1000.0) + "s");
}
}

要检查 100 个 mio 号码,它需要 ca。在我的 i7 3770k 台式电脑上为 0.7 秒。

【讨论】:

  • 您可能想删除一些代码的作用
  • 刚刚添加了几个 cmets。我希望现在更容易理解。
【解决方案3】:

你有一个困惑:

numPrimesFound++;没关系,但它必须在循环之外 for (int j = i; i * j

你的主循环必须走得更远(或者你忘记了大于 sqrt(limit) 的素数

 for (int i = 3; i < limit; i += 2) 

在 Eratosthenes Sieve 中标记多次“非质数”是正常的。

and i * j

这样的结果还可以,但只有

final int limit=40000; // 50 000 is too big !

用那个替换你的内部循环,你至少可以去 1000000

 for (int z = i*2; z<limit; z+=i)
    isPrime[z] = false;           //has a multiple ==> not a prime

你可以使用 bitset

【讨论】:

    【解决方案4】:

    好的..这是我想出的

    long startTime = System.currentTimeMillis();
    int limit = 100000;
    boolean[] isPrime = new boolean[limit+1];
    
    Arrays.fill(isPrime, true);
    isPrime[0] = false;
    isPrime[1] = false;
    int numPrimesFound = limit-1;                           
    
    System.out.println("Sqrt is:" + Math.sqrt(limit));
    for (int i=2; i < Math.sqrt(limit);i++) {
        if (isPrime[i] == true) {
            int inc = 0;
            while (true) {
                int j = (i*i) + (inc*i);
                if( j > limit) break;
                if (isPrime[j]) {
                    isPrime[j]= false;
                    numPrimesFound--;     
                }
                inc++;
            }
        }
    }
    
    System.out.println("Number of Primes" + numPrimesFound);
    for (int i = 2; i < limit;i++) {
        if (isPrime[i]) 
            System.out.println("Prime#:" + i);
    }
    long stopTime = System.currentTimeMillis();   //measuring execution time
    System.out.println("Sieve: " + (stopTime - startTime) + " milliseconds.");
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-08-01
      • 2016-07-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-06
      • 2017-02-08
      相关资源
      最近更新 更多