3

I have solved the seventh problem of Euler, it says:

By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we can see that the 6th prime is 13.

What is the 10 001st prime number?

I solved it using, and in the array in which I keep the cousins, when it reaches the length of 10001, I return that number. The algorithm takes 1300 ms, which I tink that is very inefficient, what am I doing particularly in my implementation?

var start = performance.now();

function eratosthenes(n) {
  var arr = [2], acc = 0; 
// matrix to save the found prime numbers and mark their multiples
  for(var i = 3; true; i += 2) { // loop
    if(arr.length === n) return arr[arr.length - 1]; // if the array length is equal to n return the last number
    if(!resolve(arr, i)) { // check if is multiple of the prime numbers, already found.
      arr.push(i); // if isnt multiple, save it
    }
  }
}

function resolve(array, n) {
  return array.some(cur => !(n%cur));
}
console.log(eratosthenes(10001)); // Tooks 1300 ms

var end = performance.now();
var time = end - start;

console.log(time);
J. Uchu
  • 121
  • 6
  • 3
    Your algo is not [Sieve of eratosthenes](https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes), and if implemented correctly, it should help. – Pham Trung Feb 21 '18 at 03:27
  • I did it as it says wikipedia – J. Uchu Feb 21 '18 at 03:29
  • 1
    Nope, the implementation is not correct. Maybe [this](https://www.geeksforgeeks.org/sieve-of-eratosthenes/) can help – Pham Trung Feb 21 '18 at 03:30
  • Yes, I can not know the length of the array – J. Uchu Feb 21 '18 at 03:43
  • Yes Jaromanda, some () should then be equivalent – J. Uchu Feb 21 '18 at 03:46
  • 1
    Why don't you just do the experiment your self? binary search is not a bad idea.10^6 should be a good guess. – Pham Trung Feb 21 '18 at 03:46
  • 1
    @JaromandaX [Prime number theorem](https://en.wikipedia.org/wiki/Prime-counting_function) I doubt how long it take for the above algo to run to find the 100,001st prime – Pham Trung Feb 21 '18 at 03:49
  • How i can know the 10001 prime in the sieve of eratosthenes – J. Uchu Feb 21 '18 at 03:52
  • 1
    @J.Uchu That's question is simple, and you should be able to solve it yourself. Hint: by keeping a counting number? – Pham Trung Feb 21 '18 at 03:53
  • Yes but for that, I must have a predefined number to create the array, and I need that number – J. Uchu Feb 21 '18 at 03:58
  • Chrome, last version – J. Uchu Feb 21 '18 at 04:16
  • How you suppose 10 ^ 6? – J. Uchu Feb 21 '18 at 04:17
  • @JaromandaX I think you misunderstand how that algorithm works. With the seive when a number is visited you already know whether or not it is prime (if it hasn't been marked as composite then it is prime when you visit it). With the OP's algorithm, when a number is visited it's not already determined whether or not it is prime and needs to do some kind of test against the set of smaller of primes (like using .some) which is very inefficient. – Paul Feb 21 '18 at 05:53
  • @J.Uchu I added an answer which uses the seive of eratosthenes but does not rely on knowing how big to make the array in advance. – Paul Feb 21 '18 at 20:02

4 Answers4

3

Euler sieve, Pham knows this one :) 12ms

Uchu, I don't see where your code is marking the multiples. Isn't that what Sieve of Eratosthenes is supposed to do?

JavaScript code (this code is actually an adaptation of code by btilly, optimizing an idea of mine):

var start = performance.now();
n = 115000
a = new Array(n+1)
total = 0
s = []
p = 1
count = 0
while (p < n){
  p = p + 1

  if (!a[p]){
    count = count + 1
    if (count == 10001){
      console.log(p);
      end = performance.now();
      time = end - start;

      console.log(time);
      break;
    }
    a[p] = true
    s.push(p)

    limit = n / p
    new_s = []

    for (i of s){
      j = i
      while (j <= limit){
        new_s.push(j)
        a[j*p] = true;
        j = j * p
      }
    }
    s = new_s
  }
}
גלעד ברקן
  • 23,602
  • 3
  • 25
  • 61
  • Nice answer :) +1 – Pham Trung Feb 21 '18 at 09:54
  • nope - must be browser specific - I'm guessing you're running google chrum – Jaromanda X Feb 21 '18 at 10:55
  • Chrum runs this answer (17ms) 600% faster than Pham's(107ms) - Firefox runs this answer (60ms) 50% slower than Phams (40ms)... – Jaromanda X Feb 21 '18 at 10:56
  • @JaromandaX also Euler is O(n) time, which I thought was part of the goal. – גלעד ברקן Feb 21 '18 at 10:57
  • goes to show "make this code run faster" type questions are sometimes very difficult to answer due to browser differences :p (no, I haven't tested IE or Edge, I wouldn't pee on them if they were on fire :p ) – Jaromanda X Feb 21 '18 at 10:59
  • 1
    The bound you set are better than mine (10^5 vs 10^6), that's why mine is slower, you can edit my answer and see the difference. However, as 10^6 is my original guess, so I purposely set it such :) – Pham Trung Feb 21 '18 at 11:07
  • 1
    @PhamTrung ah good point! It's a good example of O(n) time not being as fast because of extra overhead on each iteration. – גלעד ברקן Feb 21 '18 at 11:50
  • @PhamTrung how do the algorithms compare for finding 600,000th prime? – גלעד ברקן Feb 21 '18 at 12:06
  • @גלעדברקן You seem to be right, I read other sources and did tests. Sorry I take it back. – maraca Feb 21 '18 at 12:13
  • I do not need to mark anything in the array, since the `check()` function checks the multiples – J. Uchu Feb 21 '18 at 14:29
  • I think you would be interested in my answer, it has a similar performance to yours (slightly faster on my machine), but the really interesting thing is that it doesn't rely on any approximation of `max` so it is not targeted to work specifically for the 10001st prime. – Paul Feb 21 '18 at 19:57
  • @JaromandaX I am curious how my new answer performs on your browser compared to this one, but also to Pham's. This answer is about 4x faster than Pham's in my browser and mine is slightly faster than this one, but I wonder if Pham's or mine is faster in your browser. – Paul Feb 21 '18 at 20:00
  • @Paulpro MacOS, Chrome browser: `n = 10001` => mine: 13.89, yours: 16.2. `n = 600,000` (9,000,000 array on mine) => mine: 391.4, yours: 861.2. – גלעד ברקן Feb 21 '18 at 20:13
  • Yes, your answer has a better time complexity so obviously it will get much faster as n gets larger, but also it needs to be updated to target specific inputs without breaking. – Paul Feb 21 '18 at 20:21
  • FYI on my machine for `n = 10001` yours takes 9ms while mine takes 6-7ms – Paul Feb 21 '18 at 20:21
  • (3 year old) Intel computer: Firefox, yours 44 vs Pham 24, Chrome yours 12 vs Pham 76 ... (5 year old) AMD computer: Firefox: yours 60 vs Pham 40, Chrome: yours 20, Phams 100 - so, clearly there is a difference between browser optimisations :p – Jaromanda X Feb 21 '18 at 21:46
2

As requested by JaromandaX, this is the code for Sieve of Eratosthenes. 51 ms on my browser (OP solution is 750 ms)

var max = 1000000;

function eratosthenes(n) {
   var arr = [], count = 0;
   for (var i = 0; i < max; i++){
       arr.push(true);
   }
   for (var i = 2; i < max; i++){
       if(arr[i]){
 
           count++;
           if(count == n){
 
              return i;
           } 
           for (var j = i + i; j < max; j += i ){
                arr[j] = false;
           }
       }
   }
    
}
var start = performance.now(); 
console.log(eratosthenes(10001)); 
var end = performance.now();
var time = end - start;

console.log(time);
Pham Trung
  • 11,204
  • 2
  • 24
  • 43
  • The inner loop can be opimized `for (var j = i * i; j < max; j += i) {...}`. With a tighter bound (e.g. 115000 like the other answer) it then runs in < 10 ms on my computer. – maraca Feb 21 '18 at 11:47
  • I think you would be interested in my answer, it has a slightly better performance than גלעד ברקן's but doesn't rely on any approximation of `max` so it is not targeted to work specifically for the 10001st prime. – Paul Feb 21 '18 at 19:56
1

This has a similar running time to גלעד ברקן's answer (actually about 10% faster on my machine), but doesn't rely on knowing an approximate max before starting. It performs a seive of Eratosthenes up to max (starting at 2) and then doubles max, initialises the new elements in the array per the previously found primes and repeats.

function eratosthenes(n) {
    let prev_max = 1, max = 2, i, j;
    const primes = [], is_prime = new Array(max+1).fill(true);
    while( true ) {
      for ( i = prev_max + 1; i <= max; i++){
        if ( ! is_prime[i] ) continue;

        primes.push( i );

        if ( primes.length === n )
            return i;

        for ( j = i + i; j <= max; j += i )
            is_prime[j] = false;
      }

      const next_max = max*2;
      is_prime.length = next_max + 1;
      is_prime.fill( true, max + 1, next_max );

      for ( i = 0; i < primes.length; i++ ) {
        const prime = primes[i];
        for ( j = max + prime - max%prime; j <= next_max; j += prime )
          is_prime[j] = false;
      }
      prev_max = max;
      max = next_max;
   }
}

var start = performance.now(); 
console.log(eratosthenes(10001)); 
var end = performance.now();
var time = end - start;

console.log(time);
Paul
  • 139,544
  • 27
  • 275
  • 264
0

If it is a code-writing exercise, it is better to explore the earlier answers.

But if you are after a simple and fast solution, here's how you can solve it using prime-lib, which I created:

import {generatePrimes} from 'prime-lib';

const seekIndex = 10_001; // index of the prime being sought
const start = Date.now();
let a, c = 0;
const i = generatePrimes({boost: seekIndex + 1});
while ((a = i.next()) && !a.done && c++ < seekIndex) ;

console.log(`Prime: ${a.value}, took ${Date.now() - start}ms`);

On my PC it spits out:

Prime: 104759, took 5ms

And with the modern RXJS this becomes even simpler still:

import {generatePrimes} from 'prime-lib';
import {from, last} from 'rxjs';

const seekIndex = 10_001; // index of the prime being sought
const i = generatePrimes({boost: seekIndex + 1});
from(i).pipe(last()).subscribe(console.log);
//=> 104759
vitaly-t
  • 24,279
  • 15
  • 116
  • 138
  • There's no problem here opening up an old question to post a new answer. But if you're going to point people to a library you maintain, it's a good idea to mention that once in your post. When I mention Ramda, for instance, I always do something like this: "When using Ramda (disclaimer: I'm one of its main authors) ..." – Scott Sauyet Oct 14 '21 at 00:39
  • 1
    @ScottSauyet Amended ;) – vitaly-t Oct 14 '21 at 00:43
  • This is nice... much faster than my naive wheel-less sieve which takes about 90ms. Some day, I swear I'm going to dig deeper into Will Ness's postponed sieve optimization. I don't really know what he's postponing besides the obvious optimization that we can start with the square of the current prime. It's more than just the 2-3-5-7 (or whatever) wheel, right? – Scott Sauyet Oct 14 '21 at 01:20
  • Will Ness only scratched the surface there, LOL, but [GordonBGood did a lot more digging into it](https://stackoverflow.com/questions/39312107/implementing-the-page-segmented-sieve-of-eratosthenes-in-javascript/55761023), much of which landed in my library also :) On the whole, it can become an overly-consuming subject. – vitaly-t Oct 14 '21 at 01:39
  • I saw that article mentioned in you code, and may dig into it someday. But it's not something that consumes me overmuch. A long time ago when I was doing Euler problems, I wanted a reasonably efficient prime generator, wrote a sieve and was done with it. And yet, I *am* still curious! – Scott Sauyet Oct 14 '21 at 02:07