Search code examples
node.jsoptimizationprimes

Creating a large stream of prime numbers fast


I have been working on a coding challenge. The instructions are as follows:

"Create an endless stream of prime numbers - a bit like IntStream.of(2,3,5,7,11,13,17), but infinite ( well, long ). The stream must be able to produce 25 million primes in seconds"

My code generates prime numbers but not fast enough; it keep timing out. I was hoping someone might be able to give me some guidance on how to optimize my solution or find a better one. My code is below. This is my first time posting on here in a while, so if I need to do anything differently or clarification is needed please let me know. Thank you.

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

Solution

  • As rossum suggested in the comments, you can use the 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 raised a valid point, the above method is indeed much too inefficient and can be improved. This led me to look around for the most efficient solution that implements the boolean array method he suggested, which led me to this answer by Matt Gibson:

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