3

I'm currently having this method which works fine:

 private static List<long> GetPrimeNumbers(long number)
        {
            var result = new List<long>();
            for (var i = 0; i <= number; i++)
            {
                var isPrime = true;
                for (var j = 2; j < i; j++) 
                {
                    if (i % j == 0) 
                    {
                        isPrime = false;
                        break;
                    }
                }
                if (isPrime)
                {
                    result.Add(i);
                }
            }
            return result;
        }

Is the above the best algorithm possible?

It's really slow when the number is above 100000.

I mean, what'd be the best, most performant algorithm to find the prime numbers less than or equal to a given number?

Bart Kiers
  • 166,582
  • 36
  • 299
  • 288
The Light
  • 26,341
  • 62
  • 176
  • 258
  • 8
    Keep a dictionary of known prime numbers in a given range: http://primes.utm.edu/lists/small/1000.txt No calculation needed. Only memory to store. – Darin Dimitrov Nov 03 '11 at 07:38
  • 2
    *prime numbers of a given number* does not mean anything. You mean *prime numbers less then or equal to a given number*? – Miserable Variable Nov 03 '11 at 07:43
  • @hemal he means the primes to multiply to get the number – GvS Nov 03 '11 at 07:50
  • 3
    @GvS do you mean prime factors? His algorithm goes through all numbers less then or equal to the passed parameter, checking by brute force if each is prime and adding to the result list the primes. It does not seem to be doing what you wrote. – Miserable Variable Nov 03 '11 at 07:55
  • @Hemal, the original question title (and still in the question) is about prime factors. Could be his algorithm is wrong, or the question is wrong. – GvS Nov 03 '11 at 08:30
  • 1
    @GvS - the method in the question lists all primes up to `number` (plus the two non-primes 0 and 1), and "works fine", so apparently this is, what he wants. – Henrik Nov 03 '11 at 08:36
  • possible duplicate of [Which is the fastest algorithm to find prime numbers?](http://stackoverflow.com/questions/453793/which-is-the-fastest-algorithm-to-find-prime-numbers) – Saeed Amiri Nov 03 '11 at 23:13

7 Answers7

8
  1. Sieve of Eratosthenes. This algorithm can generate all prime numbers up to n. Time complexity - O(nlog(n)), memory complexity - O(n)

  2. BPSW primality test. This algorithm can check if n is pseudoprime. It was tested on first 10^15 numbers. Time complexity - O(log(n)).

UPDATE: I did some research and wrote simple implementation of generating prime numbers in c#. Main idea when we check number N for primality - we just need to check if it divisible by any prime number that less than sqrt(N).

First implementation:

public static List<int> GeneratePrimes(int n)
{
  var primes = new List<int>();
  for(var i = 2; i <= n; i++)
  {
    var ok = true;
    foreach(var prime in primes)
    {
      if (prime * prime > i)
        break;
      if (i % prime == 0)
      {
        ok = false;
        break;
      }
    }
    if(ok)
      primes.Add(i);
  }
  return primes;
}

Test results:

10^6 - 0.297s
10^7 - 6.202s
10^8 - 141.860s

Second implementation using parallel computing: 1. Generate all primes up to sqrt(N) 2. Generate all primes from sqrt(N) + 1 to N using primes up to sqrt(N) using parallel computing.

public static List<int> GeneratePrimesParallel(int n)
    {
      var sqrt = (int) Math.Sqrt(n);
      var lowestPrimes = GeneratePrimes(sqrt);
      var highestPrimes =  (Enumerable.Range(sqrt + 1, n - sqrt)
                                .AsParallel()
                                .Where(i => lowestPrimes.All(prime => i % prime != 0)));
      return lowestPrimes.Concat(highestPrimes).ToList();
    }

Test results:

10^6 - 0.276s
10^7 - 4.082s
10^8 - 78.624
Ivan Bianko
  • 1,749
  • 15
  • 22
  • I assume complicity -> complexity. – Liz Nov 03 '11 at 10:56
  • 1
    Those times aren't too bad; they are, respectively, about 11, 20, and 42 times as slow as Atkin, which on my machine takes about .025, 0.2, and 1.9 seconds to deliver primes up to 10^6, 10^7, and 10^8. – James Waldby - jwpat7 Nov 03 '11 at 18:58
  • Isn't the complexity for the Sieve of Eratosthenes O (nloglog(n))? Or am I missing something? Source : http://stackoverflow.com/questions/2582732/time-complexity-of-sieve-of-eratosthenes-algorithm, Wikipedia. – gaurav jain Feb 05 '15 at 04:38
  • @jwpat7, yes not bad considering that this is Ivan uses Trial Division. – GordonBGood Apr 03 '15 at 05:32
  • 1
    @gauravjain, if you check the new Wikipedia article, you will see that the the basic SoE does indeed have the performance you state (which article includes the constant offsets and factors). This answer is not the SoE but rather a Trial Division algorithm, and I doubt that the attempt to make it calculate in parallel is all that effective. – GordonBGood Apr 03 '15 at 05:39
  • Isn't the sieve of Atkin faster than the sieve of Eratosthenes ? – THC Jul 31 '16 at 09:10
4

Probably the Sieve of Atkin is most performant, although for all I know somebody found a better once since.

Erathosthenes and Sundaram also have sieves of their own, which are considerably simpler to implement. Any of them kicks the stuffing out of doing it by separately looking for a factor in each number up to the limit.

All sieves use more working memory than factorizing one value at a time, but generally still less memory than the resulting list of primes.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • Although for smaller limits, a good variant of an Eratosthenes sieve tends to be faster than an Atkin sieve due to lower overhead. "Smaller" depends on the implementations, usually the break even point is somewhere between 10^7 and 10^9. – Daniel Fischer Nov 03 '11 at 16:22
  • 1
    For the D. J. Bernstein implementation of Atkin as found at the computing-time break-even point on my machine is between 10^6 and 10^7. For programmer time (getting familiar enough with DJB's code to adapt it to specific purposes) I'd put the break-even somewhere above 10^9. :) – James Waldby - jwpat7 Nov 03 '11 at 19:06
  • 1
    @jwpat7, the break over point depends on your implementation of the SoE: Bernstein's hand tuned C implementation of the SoA never catches up to the maximally wheel factorized and optimized C implementation of the SoE [primesieve](http://primesieve.org/) even when it is limited to only one CPU core. – GordonBGood Apr 03 '15 at 05:44
  • 2
    @DanielFischer, although I realize this is an old post, I think that it can be shown that any reasonable implementation of the SoE that uses maximum wheel factorization (which the Bernstein comparison study limited to just the 2/3/5 wheel and no pre-culling of the buffer arrays) will beat Bernstein's hand tuned C implementation of SoA. I am doing this in many languages, and have beat Bernstein's primegen to one billion even in JIT languages such as Scala and F# in spite of their built-in array bounds checks (at a cost of about two CPU clock cycles per operation). – GordonBGood Apr 03 '15 at 05:50
  • 2
    @DanielFischer, continued: Of course, we know that for truly large ranges Bernstein's primegen falls apart in efficiency as it gets bogged down by the prime squares free computations as the spans per cull get huge and much larger than the segmented page buffer size - a set of operations that gets almost no space in the Atkin and Bernstein paper as being trivial ends up taking more time than the quadratic computations! – GordonBGood Apr 03 '15 at 05:53
2

You can improve substantially your algorithm testing whether n is a multiple of any integer between 2 and sqrt(n).

    private static List<int> GetPrimeNumbers2(long number)
    {
        var result = new List<int>();

        for (var i = 0; i <= number; i++)
        {
            var isPrime = true;
            var n = Math.Floor(Math.Sqrt(i));

            for (var j = 2; j <= n; j++)
            {
                if (i % j == 0)
                {
                    isPrime = false;
                    break;
                }
            }

            if (isPrime)
            {
                result.Add(i);
            }
        }

        return result;
    }

This change the complexity from O(NN) to O(Nsqrt(N)).

The fastest known algorithm for testing the primality of general numbers is the Elliptic Curve Primality Proving (ECPP): http://en.wikipedia.org/wiki/Elliptic_curve_primality_proving I guess that implementing it will be difficult so do it only if you really need it. There are probably library that could help you here.

Charles Okwuagwu
  • 10,538
  • 16
  • 87
  • 157
Andrea Angella
  • 341
  • 2
  • 7
1

This will give you reasonable performance for the initial execution and then near to O(1) (it will be O(N) but very, very, small) performance for any repeated requests, and reasonable performance for values larger than the current max number seen.

private static List<ulong> KnownPrimes = new List<ulong>();
private static ulong LargestValue = 1UL;


private static List<ulong> GetFastestPrimeNumbers(ulong number)
{
    var result = new List<ulong>();
    lock (KnownPrimes)
    {
        result.AddRange(KnownPrimes.Where(c => c < number).ToList());
        if (number <= LargestValue)
        {
            return result;
        }
        result = KnownPrimes;

        for (var i = LargestValue + 1; i <= number; i++)
        {
            var isPrime = true;
            var n = Math.Floor(Math.Sqrt(i));

            for (var j = 0; j < KnownPrimes.Count; j++)
            {
                var jVal = KnownPrimes[j];
                if (jVal * jVal > i)
                {
                    //isPrime = false;
                    break;
                }
                else if (i % jVal == 0)
                {
                    isPrime = false;
                    break;
                }
            }
            if (isPrime)
            {
                result.Add(i);
            }
        }
        LargestValue = number;
    }
    return result;

}

Edit: Considerably faster using Sieve of Atkin, which I addapted to konw about the:

private static List<ulong> KnownPrimes = new List<long>();
private static ulong LargestValue = 1UL;

private unsafe static List<ulong> FindPrimes(ulong number)
{
    var result = new List<ulong>();
    var isPrime = new bool[number + 1];
    var sqrt = Math.Sqrt(number);
    lock (KnownPrimes)
    {

        fixed (bool* pp = isPrime)
        {
            bool* pp1 = pp;
            result.AddRange(KnownPrimes.Where(c => c < number).ToList());
            if (number <= LargestValue)
            {
                return result;
            }
            result = KnownPrimes;

            for (ulong x = 1; x <= sqrt; x++)
                for (ulong y = 1; y <= sqrt; y++)
                {
                    var n = 4 * x * x + y * y;
                    if (n <= number && (n % 12 == 1 || n % 12 == 5))
                        pp1[n] ^= true;

                    n = 3 * x * x + y * y;
                    if (n <= number && n % 12 == 7)
                        pp1[n] ^= true;

                    n = 3 * x * x - y * y;
                    if (x > y && n <= number && n % 12 == 11)
                        pp1[n] ^= true;
                }

            for (ulong n = 5; n <= sqrt; n++)
                if (pp1[n])
                {
                    var s = n * n;
                    for (ulong k = s; k <= number; k += s)
                        pp1[k] = false;
                }

            if (LargestValue < 3)
            {
                KnownPrimes.Add(2);
                KnownPrimes.Add(3);
            }
            for (ulong n = 5; n <= number; n += 2)
                if (pp1[n])
                    KnownPrimes.Add(n);
            LargestValue = number;
        }
    }

    return result;
}

Adapted from Source

This can easily be improved to get better performance when adding items, but I would suggest you save the previous KnownPrimes list to disk between executions, and load a pre-existing list of values such as the list from http://primes.utm.edu/lists/small/millions – Credit goes to CodingBarfield

Seph
  • 8,472
  • 10
  • 63
  • 94
0

I found this link:

http://www.troubleshooters.com/codecorn/primenumbers/primenumbers.htm

according to your question, it seems that what you are interested in is not in proving that a certain given number is probably (or certainly) a prime, and neither you are interested in factoring large numbers. To find all prime numbers up to a given N, one can use Eratosthenes Sieve, but it seems that in the link above further optimizations were considered.

John Donn
  • 1,718
  • 2
  • 19
  • 45
0

I think a pertinent question is "how big will the upper limit ever be". If the number is in a relatively small range [lets say 2^16] you could probably just precompute and save all the primes (below some limit) to file, and then load into memory where appropriate (and then potentially continue using one of the Sieves listed below.

Ivan Benko and Steve Jessop above do state the two more well known fast methods [Eratosthenes, Atkin] although Ivan, the complexity of the Sieve is O(n*log(log(n))).

The Sieve is relatively easy to implement and is very fast compared to your method.

Noxville
  • 544
  • 3
  • 15
-1

The absolute most performant:

(Minimize the work to get the result).

Store the primes of all numbers in the domain in a hashtable with the number as key.

GvS
  • 52,015
  • 16
  • 101
  • 139
  • 1
    That still requires an algorithm to generate the numbers you'll store. ;) – Aberrant Nov 03 '11 at 08:04
  • @Aberrant, But only once. His question is about the most "performant algorithm to find". How to generate is another question (sorry I'm a programmer, tend to take things literally). – GvS Nov 03 '11 at 08:32
  • 3
    "tend to take things literally" == "tend to deliberately resolve any possible ambiguity in the opposite direction to what the questioner intended" ;-) – Steve Jessop Nov 03 '11 at 10:03