【问题标题】:Creating a large stream of prime numbers fast快速创建大量素数
【发布时间】:2019-12-20 20:20:26
【问题描述】:

我一直在应对编码挑战。说明如下:

“创建一个无穷无尽的素数流 - 有点像 IntStream.of(2,3,5,7,11,13,17),但是是无限的(嗯,long )。 流必须能够在几秒钟内产生 2500 万个素数"

我的代码生成素数但不够快;它一直超时。我希望有人能给我一些关于如何优化我的解决方案或找到更好的解决方案的指导。我的代码如下。这是我一段时间以来第一次在这里发帖,所以如果我需要做任何不同的事情或需要澄清,请告诉我。谢谢。

class Primes {
  static * stream() {
  yield 2; 
  let n = 3;
   while (n < 15486042) {
     if (isPrime(n)) {yield n}
     n += 2;
   }
  }
}

function isPrime(n) {
  for (let a = 3; a <= ~~Math.sqrt(n); a+=2) {
    if (n%a == 0) return false;
    }
  return true;
}

【问题讨论】:

  • 使用埃拉托色尼筛。如有必要,请分块使用。如果这还不够快,那么试试阿特金筛子,它会稍微快一点。
  • 您通过检查每个可能的除数通过平方根来测试每个数字的素数。如果您可以存储先前的素数,则只需测试直到平方根的素数除数(基本上是埃拉托色尼筛)。
  • @rossum - 谢谢,我已经考虑尝试使用阿特金筛。它似乎更复杂,但也许这是要走的路。
  • @lurker - 感谢您指出这一点。我将此解决方案基于 Eratosthenes 筛,尽管我知道这远未达到理想的优化。因此,为了优化,我应该将素数数组传递给 isPrime(n) 函数并将其用作评估的跳跃点?再次感谢您。
  • @WillNess 我明白了。这绝对不是我的本意。这就是这些简洁的小cmets的问题。 :p

标签: node.js optimization primes


【解决方案1】:

按照 cmets 中的 rossum 建议,您可以使用 Sieve of Eratosthenes

function getPrimes(limit) {

  let primes = [];
  let toCheck = Array.from(Array(limit + 1).keys()).splice(2);

  while (toCheck.length) {
    primes.push(toCheck.shift());
    toCheck = toCheck.filter(
      function(i) {
        return i % primes[primes.length - 1] !== 0;
      }
    );
  }

  console.log(primes);

}

getPrimes(10000);

James Reinstate Monica Polk 提出了一个有效的观点,上述方法确实效率太低,可以改进。这使我四处寻找实现他建议的布尔数组方法的最有效解决方案,这使我找到了 Matt Gibson 的this answer

"use strict";
function findPrimes(n){
  
  function primeSieve(g,o,r){
    var t = (Math.sqrt(4+8*(g+o))-2)/4,
        e = 0,
        s = 0;
    
    ar.fill(true);
    if (o) {
      for(var i = Math.ceil((o-1)/3); i < (g+o-1)/3; i++) ar[1+3*i-o] = false;
      for(var i = 2; i < t; i++){
        s = Math.ceil((o-i)/(1+2*i));
        e = (g+o-i)/(1+2*i);
        if (i%3-1) for(var j = s; j < e; j++) ar[i + j + 2*i*j-o] = false;
      }
    } else {
        for(var i = 1; i < (g-1)/3; i++) ar[1+3*i] = false;
        for(var i = 2; i < t; i++){
          e = (g-i)/(1+2*i);
          if (i%3-1) for(var j = i; j < e; j++) ar[i + j + 2*i*j] = false;
        }
      }
    for(var i = 0; i < g; i++) ar[i] && r.push((i+o)*2+1);
    return r;
  }
  
  var cs = n <= 1e6 ? 7500
                    : n <= 1e7 ? 60000
                               : 100000, // chunk size
      cc = ~~(n/cs),                     // chunk count
      xs = n % cs,                       // excess after last chunk
      ar = Array(cs/2),                  // array used as map
  result = [];
  
  for(var i = 0; i < cc; i++) result = primeSieve(cs/2,i*cs/2,result);
  result = xs ? primeSieve(xs/2,cc*cs/2,result) : result;
  result[0] *=2;
  return result;
}


var primes = [];
console.time("primes");
primes = findPrimes(15486042);
console.timeEnd("primes");
console.log(primes.length);
console.log(primes.splice(0, 1000));

【讨论】:

  • 这是一个埃拉托色尼筛法的例子,但我认为这对于 OP 来说不够有效。一种更常见和更有效的方法是用真值填充数组 S,然后为每个素数 p 标记 S[2*p], S[3*p], ... 为假。您不需要执行任何余数/模 (%) 操作或创建新数组。
  • 好点,其实我从来没想过自己会这样做。
  • 哇太棒了!我不会想到以这种方式分解它。谢谢你们。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-01
  • 1970-01-01
  • 2011-09-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多