简体   繁体   中英

Algorithm “Sieve of Eratosthenes” by javascript

Is the code in the example implementation of the Sieve of Eratosthenes?
What is the complexity of this implementation?

UPD: I build a chart of count operation with an array item. And I think that complexity is O(n) . Am I right? 在此输入图像描述

 console.time('exec'); var arr = fn(Math.pow(10, 2)); console.timeEnd('exec'); function fn(n) { var arr = Array.apply(null, {length: n + 1}).map(function() {return true;}); arr[0] = arr[1] = false; var base = 2; while (Math.pow(base, 2) < n) { var counter = 2; while (counter * base < n) { arr[counter * base] = false; counter++; } base++; } return arr; } // show result arr.forEach(function(item, index) { if (item) { console.log(index); } }); 

The asymptotic time complexity of the algorithm is, I think, O ( n log n ) .

The outer loop runs for 2 ... sqrt(n) . The inner loop runs n / base times, where base is in the outer range of 2 ... sqrt(n) .

Running the loops results in total iteration count that can be expressed as:

(1) (n / 2) + (n / 3) + (n / 4) + ... + (n / sqrt(n))

Parentheses above used to denote iteration count of the inner loop within a single iteration of the outer loop.

We can extract n and get

(2) n * (1/2 + 1/3 + 1/4 + ... + 1 / sqrt(n))

The parenthesized term is the harmonic series which is known to be divergent so we don't get aything nice like O (1) there, although the divergence is extremely slow. This is also proved empirically by your chart which looks linear.

It was shown that harmonic series has a constant relationship with ln(n) ( source ).

Hence, we get n * ln(n) and therefore complexity of O ( n log n ) .

You're not getting the nicer O ( n log log n ) complexity, because your solution does not use prime factorization (and therefore prime harmonic series which is O (log log n ) ( source )).

Practically, this is because your algorithm checks non-primes, eg the same index in arr[counter * base] = false; is assigned for base and counter pairs {2, 6}, {3, 4}, {4, 3}, {6, 2}, yet base 4 and 6 are already known not to be primes at the point they are applied and by definition of the algorithm, all their multiples are also already known not be primes and therefore it is useless to check them again.

EDIT

An O ( n log log n ) JavaScript implementation could look like this:

function sieve(n)
{
    // primes[x] contains a bool whether x is a prime
    const primes = new Array(n + 1).fill(true);
    // 0 and 1 are not primes by definition
    primes[0] = false;
    primes[1] = false;

    function square(i)
    {
        return i * i;
    }

    for (let number = 2; square(number) <= n; number += 1)
    {
        if (!primes[number])
        {
            // we have already determined that the number is not a prime
            // therefore all its multiples are also already determined not to be primes
            // skip it
            continue;
        }

        for (let multiplier = 2; multiplier * number <= n; multiplier += 1)
        {
             // a multiple of prime is not a prime
             primes[multiplier * number] = false;
        }
    }

    return primes;    
}

Such an algorithm still runs the outer loop sqrt(n) times but the inner loop is now not ran for each number but only for primes, so for (2) we now get

(2a) n * (1/2 + 1/3 + 1/5 + 1/7 + ... + 1 / (last_prime_less_or_equal_sqrt(n))

As mentioned before, the parenthesized term is prime harmonic sequence with log log n growth. This gets us to O ( n log log n ) once multiplied by the n .

The runtime complexity is O(n*n), because you iterate base and counter up to the wanted value n (where you missed the last value in the comparisons for the loops).

 console.time('exec'); function fn(n) { var arr = Array.from({ length: n + 1 }, (_, i) => i > 1), base = 2, counter; while (Math.pow(base, 2) <= n) { counter = 2; while (counter * base <= n) { arr[counter * base] = false; counter++; } base++; } return arr; } var arr = fn(Math.pow(10, 2)); console.timeEnd('exec'); // show result arr.forEach(function(item, index) { if (item) { console.log(index); } }); 

@ZdeněkJelínek, @NinaScholz, thanks for your help. This is the code based on your suggestions.
Finally, hope that I was able to implement it with complexity O(n log log n) .

 console.time('exec'); var arr = fn(Math.pow(10, 5)); function fn(n) { var arr = Array.apply(null, { length: n + 1 }).map(function(_, i) { return i > 1; }); var base = 2; while (Math.pow(base, 2) < n) { var counter = 2; while (counter * base <= n) { arr[counter * base] = false; do { counter++; } while (!arr[base]); } do { base++; } while (!arr[base]); } return arr; } console.timeEnd('exec'); console.log('array length: ' + arr.length); // show result /* arr.forEach(function(item, index) { if (item) { console.log(index); } }); */ 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM