计算一百万个素数

时间:2012-11-15 19:06:30

标签: algorithm math numbers primes

我有一个问题要打印一百万个素数。我已经为此编写了一个java程序。它目前需要1.5分钟来计算它。我认为我的解决方案效率不高。我使用了以下算法:

  • 最初将1 2 3添加到主要列表
  • 计算要检查的号码的最后一位数
  • 检查数字是0,2或4还是6或8,然后跳过数字
  • 否则计算数字的平方根..
  • 尝试将从2开始的数字除以数字的平方根
  • 如果数字可以整除,则跳过该数字,否则将其添加到主要列表

我也读过其他几个解决方案,但我没有找到一个好的答案。请在理想情况下建议计算此值的最短时间应该是什么,以及需要进行哪些更改才能使算法更有效。

10 个答案:

答案 0 :(得分:12)

如果您在列表中添加了1,那么您的答案已经错误了:)

无论如何,Sieve of Erathosthenes是您应该开始的地方,它非常简单且非常有效。

一旦你熟悉了筛子的概念及其工作方式,你就可以继续Sieve of Atkin,这有点复杂,但显然效率更高。

答案 1 :(得分:4)

关键词:

  1. 跳过所有偶数。从5开始,一次只添加两个。
  2. 1不是素数......
  3. 通过查找所有素数的mod直到数字的平方根来测试数字。除了素数之外,不需要测试任何东西。

答案 2 :(得分:3)

您可能希望实施Sieve of Eratosthenes算法以查找从1到n的素数,并在需要时迭代地增加范围。 (即没有找到1,000,000个素数)

答案 3 :(得分:2)

一个简单的Eratosthenes筛子就像拍板一样。这会在不到一秒的时间内计算出第1,000,000个素数:

class PrimeSieve
{
    public List<int> Primes;

    private BitArray Sieve;

    public PrimeSieve(int max)
    {
        Primes = new List<int> { 2, 3 }; // Must include at least 2, 3.
        Sieve = new BitArray(max + 1);
        foreach (var p in Primes)
            for (var i = p * p; i < Sieve.Length; i += p) Sieve[i] = true;
    }

    public int Extend()
    {
        var p = Primes.Last() + 2; // Skip the even numbers.
        while (Sieve[p]) p += 2;
        for (var i = p * p; i < Sieve.Length; i += p) Sieve[i] = true;
        Primes.Add(p);
        return p;
    }
}

编辑:最佳筛选从p ^ 2开始,而不是2p,正如Will Ness正确指出的那样(低于p ^ 2的所有化合物编号都会在早期迭代中标记出来。)

答案 4 :(得分:2)

首先,1不是素数。

其次,第一百万个素数是15,485,863,所以你需要为一些大数据处理做好准备。

第三,你可能想要使用Eratosthenes的Sieve;这是一个简单的版本:

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

这可能不适用于计算第一百万个素数所需的数组大小。在这种情况下,您需要实现Eratosthenes的分段筛。

我在我的博客上与prime numbers做了很多工作,其中包括一个essay,它提供了一个优化的Eratosthenes Sieve,并使用五种编程语言实现。

无论你做什么,使用任何编程语言,你都应该能够在不超过几秒的时间内计算出第一百万个素数。

答案 5 :(得分:1)

这是一个实现Trial division sieve的Ocaml程序(正如Will正确指出的那样,是Eratosthenes的反转):

(* Creates a function for streaming integers from x onward *)
let stream x =
  let counter = ref (x) in
  fun () ->
    let _ = counter := !counter + 1 in
    !counter;;

(* Filter the given stream of any multiples of x *)
let filter s x = fun () ->
  let rec filter' () = match s () with
    n when n mod x = 0 ->
      filter' ()|
    n ->
      n in
  filter' ();;

(* Get next prime, apply a new filter by that prime to the remainder of the stream *)
let primes count =
  let rec primes' count' s = match count' with
    0 ->
      []|
    _ -> 
      let n = s () in
      n :: primes' (count' - 1) (filter s n) in
  primes' count (stream 1);;

它适用于整数流。每次发现新的素数时,都会向流中添加一个过滤器,以便对该流的其余部分过滤掉该素数的任何倍数。可以更改此程序以生成素数按需

在Java中采用相同的方法应该相当容易。

希望这有帮助!

答案 6 :(得分:1)

<块引用>
  • 最初将 1 2 3 添加到素数列表

实际上,只需 2 个就足够了。硬编码 3 最多可以节省一毫秒。没有必要在 1 上竖琴。我确信包含它是一个诚实的错误。您已经知道了,参与该计划将有助于证实这一点。

<块引用>
  • 计算要检查的号码的最后一位

最后一个数字?在什么基地?基数 10?我想这可能是你的问题。

<块引用>
  • 检查数字是 0、2 或 4 或 6 或 8,然后跳过数字 否则计算数字的平方根

我认为这就是问题所在。您的程序应该简单地跳过偶数,因为除了 -2 和 2,它们都是合数。另一方面,这不会使运行时间减半,因为像 91 和 2209 这样的奇数可能需要更多的努力才能排除不是质数。

<块引用>
  • 尝试除以从 2 开始的数字直到数字的平方根 如果数字是可整除的,则跳过该数字,否则将其添加到素数列表中

“2 直到数字的平方根”是否包括像 4、6 和 9 这样的数字?唯一需要检查的潜在因素是已经被证明为素数的数字。如果 n 不能被 7 整除,它也不能被 49 整除。如果你正在建立一个列表,你不妨用它来检查潜在的素数。

对 Java 进行基准测试有点困难,因为您受运行时系统的支配。尽管如此,一分半钟,虽然被梅森认为是奇迹,但今天太慢了。五、十秒,我认为可以接受。

也许这是您应该避免使用对象以支持原始数组的情况之一。我的初稿比你的还要长。最后我想出了这个:

    static int[] fillWithPrimes(int quantity) {
        int[] primes = new int[quantity];
        primes[0] = 2;
        int currPi = 1;
        int currIndex = 0;
        int currNum = 3;
        int currPrime;
        boolean coPrimeFlag;
        double squareRoot;
        while (currPi < quantity) {
            squareRoot = Math.sqrt(currNum);
            do {
                currPrime = primes[currIndex];
                coPrimeFlag = (currNum % currPrime != 0);
                currIndex++;
            } while (coPrimeFlag && currPrime <= squareRoot);
            if (coPrimeFlag) {
                primes[currPi] = currNum;
                currPi++;
            }
            currNum += 2;
            currIndex = 0;
        }
        return primes;
    }

然后我写了一个 main() 记录调用 fillWithPrimes() 之前的时间,参数为 1,000,000,并报告结果:

<块引用>

运行:

操作耗时 2378 毫秒

第 10 个质数是 29

第 100 个质数是 541

第 1000 个质数是 7919

第 10000 个质数是 104729

第 100000 个质数是 1299709

第 1000000 个质数是 15485863

构建成功(总时间:2 秒)

我相信它可以进一步优化。就我个人而言,我对两秒半感到满意。

答案 7 :(得分:0)

这是一个使用递归和迭代达到百万分之一素数的javascript解决方案。它不像Erathosthenes的筛子那么快,但不需要提前知道百万个素数的值(即所需筛子的大小):

function findPrimes(n, current, primes) {
    if (!n || current < 2) return []
    var isPrime = true
    for (var i = 0; i < primes.length; i++) {
        if (current % primes[i] == 0) {
            isPrime = false
            break
        }
    }
    if (isPrime) primes.push(current)
    if (primes.length < n) return findPrimes(n, current + 1, primes)
    else return primes
}

var primes = [2,3]
for (var i = 1; i <= 1000; i++) {
    primes = findPrimes(i*1000, primes[primes.length - 1]+1, primes)
    console.log(i*1000 + 'th prime: ' + primes[primes.length-1])
}
process.exit()

输出:

...
996000th prime: 15419293
997000th prime: 15435941
998000th prime: 15452873
999000th prime: 15469313
1000000th prime: 15485863

Process finished with exit code 0

答案 8 :(得分:0)

  

作为一个较新的水平,我将尝试该水平,因此,对其进行更有效,更快速的改进表示赞赏。

 public static void main(String ar[]) {
    ArrayList primeNumbers = new ArrayList();

    for(int i = 2; primeNumbers.size() < 1000000; i++) {//first 1 million prime number

       // for(int i = 2; i < 1000000; i++) {//prime numbers from 1 to 1 million

            boolean divisible = false;

        for(int j=2;j<i/2;j++){

            if((i % j) == 0) {
                divisible = true;
                break;
            }
        }

        if(divisible == false) {
            primeNumbers.add(i);
           // System.out.println(i + " ");
        }
    }
    System.out.println(primeNumbers);
}

答案 9 :(得分:-1)

5之后的所有内容都不会被5整除,所以你可以跳过正确的事情(1,麻木)&lt;&gt;&#34; 5&#34;例如987,985。我在Excel中制作了一个将测试一百万个素数的素数并在大约15秒钟内将它们吐在一个列中但它在1500万左右疯狂