-1

There is implementation of this algorithm of finding prime numbers upto n in O(n*log(log(n)) time complexity. How can we achieve it in O(n) time complexity?

Will Ness
  • 70,110
  • 9
  • 98
  • 181
Rudresh Dixit
  • 320
  • 1
  • 4
  • 15
  • 1
    Why do you tag C++ and Java, as if saying "just give me the code in any of the languages"? If you ask about an algorithm don't tag any languages. If you ask a specific implementation please show us what you have done so far and tag only the appropriate language. – Lukas-T Apr 12 '20 at 08:12

2 Answers2

0

Well, if the algorithm is O(n*log(n)) you generally can’t do better without changing the algorithm.

The complexity is O(n*log(n)). But you can trade between time and resources: By making sure you have O(log(n)) computing nodes running in parallel, it would be possible to do it in O(n).

Hope I didn’t do your homework...

Axel
  • 13,939
  • 5
  • 50
  • 79
  • Apart from the log log, would you care to elaborate? – Axel Apr 12 '20 at 15:09
  • not everything is parallelizable. not every parallelization improves complexity. the number of available CPUs is usually limited and fixed. there's cost to communications between threads, if there is such a need. etc. etc. etc. all this must be addressed specifically for a given algorithm. otherwise it's empty talk. – Will Ness Apr 12 '20 at 19:26
  • Yes, but the sieve is easily parallelizable. Since no code was given, this looked more like a theoretical question in a computer science lecture. When I studied, we could assume a large enough machine. And in practice, even O(n^2) can be much faster than O(n) even for large values of n before it turns, so what gives - it's a theoretical answer to a theoretical question. – Axel Apr 12 '20 at 19:35
  • no *fixed* amount of resources (like available cores) will ever change complexity one iota. – Will Ness Apr 12 '20 at 20:26
  • Ah, you are too practical... ;-) – Axel Apr 12 '20 at 20:28
0

You can perform the Sieve of Eratosthenes to determine which numbers are prime in the range [2, n] in O(n) time as follows:

  • For each number x in the interval [2, n], we compute the minimum prime factor of x. For implementation purposes, this can easily be done by keeping an array --- say MPF[] --- in which MPF[x] represents the minimum prime factor of x. Initially, you should set MPF[x] equal to zero for every integer x. As the algorithm progresses, this table will get filled.

  • Now we use a for-loop and iterate from i = 2 upto i = n (inclusive). If we encounter a number for which MPF[i] equals 0, then we conclude immediately that i is prime since it doesn't have a least prime factor. At this point, we mark i as prime by inserting it into a list, and we set MPF[i] equal to i. Conversely, if MPF[i] does not equal 0, then we know that i is composite with minimum prime factor equal to MPF[i].

  • During each iteration, after we've checked MPF[i], we do the following: compute the number y_j = i * p_j for each prime number p_j less than or equal to MPF[i], and set MPF[y_j] equal to p_j.

This might seem counterintuitive --- why is the runtime O(n) if we have two nested loops? The key idea is that every value is set exactly one, so the runtime is O(n). This website gives a C++ implementation, which I've provided below:

const int N = 10000000;
int lp[N+1];
vector<int> pr;

for (int i=2; i<=N; ++i) {
    if (lp[i] == 0) {
        lp[i] = i;
        pr.push_back (i);
    }
    for (int j=0; j<(int)pr.size() && pr[j]<=lp[i] && i*pr[j]<=N; ++j)
        lp[i * pr[j]] = pr[j];
}

The array lp[] in the implementation above is the same thing as MPF[] that I described in my explanation. Also, pr stores the list of prime numbers.

Ekesh Kumar
  • 495
  • 4
  • 12
  • 1
    I got it. Thanks :) – Rudresh Dixit Apr 12 '20 at 08:23
  • surely it does not scale though. if you don't think it's right, please provide actual run times for e.g. `n1 = 1B` and `n2 = 1.5B` so we can calculate the [Empirical orders of growth](http://en.wikipedia.org/wiki/Analysis_of_algorithms#Empirical_orders_of_growth) as `log(t2/t1) / log(1.5)`. for comparison, simple bit-packed non-segmented (contiguous) sieve of Eratosthenes on odds exhibits ~n^1.06 behavior on that range [on ideone](https://ideone.com/fapob) (that's old timings, current ideone is bound to be faster, but the orders of growth can't change much). – Will Ness Apr 12 '20 at 14:59