Click here to Skip to main content
15,887,135 members
Articles / Programming Languages / C#
Alternative
Article

Finding Prime Numbers

Rate me:
Please Sign up or sign in to vote.
4.67/5 (12 votes)
14 Sep 2012CPOL3 min read 33.6K   19   29
This is an alternative for "Finding prime numbers".

Introduction

I recently answered a question on C# forum about an algorithm for the Sieve of Eratosthenes. The person asking the question had actually done a brute force implementation of finding all primes, and wanted to know how to better implement it since the calculation was taking forever. I actually solved the problem much more efficiently by improving the algorithm, and going to an Array of bool instead of a List of instantiated classes, and then looked up what was published. I had a bit faster algorithm than the one on the paper I found, so published an alternative version. This presents a faster algorithm for finding prime numbers using Sieve of Eratosthenes.

The Code

I used a simple Array of bool for numbers since this should have the best performance (KISS). It had to be initialized to true since the algorithm starts with true instead of false; I could improve the performance slightly by starting with false values. Basically, the algorithm starts from 2, a prime:

  1. Sets all entries of the array of Boolean that have an index of a number that are a multiple of a prime to false.
  2. Finds the next prime by looking for the next lowest index value that is still true.
  3. If the next found prime is less than the square root of the numbers to analyze, then stop, otherwise repeat step 1.

The criteria for stopping is when the square root of the number to be checked if they are prime because we know that any non-prime number that has not been found will have to have factors that are greater than the last processed prime, or the prime that would be next processed times itself. This algorithm is implemented in the Run method below:

C#
class Program
{
  private static bool[] _numbers;
  private const int Count = 1000000000;

  static void Main(string[] args)
  {
    Console.WriteLine("Maximum number checked if prime: " + (Count - 1).ToString("n0"));
    var sw = new Stopwatch();
    sw.Start();
    Run();
    sw.Stop();
    Console.WriteLine("Milliseconds to run algorithm: " + sw.ElapsedMilliseconds);

    sw.Reset();
    sw.Start();
    Console.WriteLine("Primes: " + _numbers.Count(i => i).ToString("n0"));
    sw.Stop();
    Console.WriteLine("Milliseconds using LINQ Count: " + sw.ElapsedMilliseconds);

    sw.Reset();
    sw.Start();
    Console.WriteLine("Primes: " + _numbers.Sum(i => i ? 1 : 0).ToString("n0"));
    sw.Stop();
    Console.WriteLine("Milliseconds using LINQ Sum: " + sw.ElapsedMilliseconds);

    sw.Reset();
    sw.Start();
    Int64 sum = 0;
    for (int i = 2; i < _numbers.Count(); i++)
      if (_numbers[i]) sum += i;
    Console.WriteLine("Sum of Primes: " + sum.ToString("n0"));
    sw.Stop();
    Console.WriteLine("Milliseconds for sum of Primes: " + sw.ElapsedMilliseconds);

    Console.WriteLine("Primes:");
    for (int i = 0; i < 100; i++)
      if (_numbers[i]) Console.WriteLine("   " + i);

    Console.ReadLine();
  }

  private static void Run()
  {
    _numbers = new bool[Count];
    for (int i = 2; i < Count; i++)
      _numbers[i] = true;

    int baseCounter = 1;
    int countLimit = (int)Math.Sqrt(Count);
    while (baseCounter < countLimit)
    {
      do
      {
        baseCounter++;
        if (baseCounter == Count) return;
      } while (!_numbers[baseCounter]);

      int counter = baseCounter << 1;
      while (counter < Count)
      {
        _numbers[counter] = false;
        counter += baseCounter;
      }
    }
  }
}

Notes

  • Originally, I implemented this algorithm so that it processed all primes. Putting in the criteria of ending the processing on the square root of the number of numbers to be analysed the processing time dropped from 40 milliseconds to 24 milliseconds.
  • I also replaced the bool Array with a BitArray (see this link). This increases the processing time by about 50%. However, I ran out of memory with the method using the bool Array at between 1.15 and 1.2 trillion, whereas could get to over 2 trillion with a BitArray (limited by the max size of the BitArray which takes an int argument). In fact, I could process 1 trillion primes with an array of Boolean and a BitArray. Here is the code for the BitArray:
C#
private static void RunBitArray()
{
  const int count = 1000000000;
  var _numbers = new BitArray(count);
  for (int i = 2; i < count; i++)
    _numbers[i] = true;

  int baseCounter = 1;
  int countLimit = (int)Math.Sqrt(count);

  while (baseCounter < countLimit)
  {
    do
    {
      baseCounter++;
      if (baseCounter == count) return;
    } while (!_numbers[baseCounter]);

    int counter = baseCounter << 1;
    while (counter < count)
    {
      _numbers[counter] = false;
      counter += baseCounter;
    }
  }
}
  • I did calculations on the time it takes to do the count using a LINQ Count method, and it was about 80% of the time to find the primes. Using a Sum LINQ statement instead of the Count took slightly more time.

History

  • 09/13/2012: Fixed ending criteria for loop to be the square root of the count. This is because any number that is left that is not a prime would have to be the current "baseCounter" times the "baseCounter" + 2.
  • 09/14/2012: Major rewrite to include different options

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Clifford Nelson Consulting
United States United States
Has been working as a C# developer on contract for the last several years, including 3 years at Microsoft. Previously worked with Visual Basic and Microsoft Access VBA, and have developed code for Word, Excel and Outlook. Started working with WPF in 2007 when part of the Microsoft WPF team. For the last eight years has been working primarily as a senior WPF/C# and Silverlight/C# developer. Currently working as WPF developer with BioNano Genomics in San Diego, CA redesigning their UI for their camera system. he can be reached at qck1@hotmail.com.

Comments and Discussions

 
AnswerRe: Very neat Pin
Clifford Nelson13-Sep-12 6:13
Clifford Nelson13-Sep-12 6:13 
GeneralRe: Very neat Pin
Richard MacCutchan13-Sep-12 6:01
mveRichard MacCutchan13-Sep-12 6:01 
GeneralRe: Very neat Pin
Pete O'Hanlon13-Sep-12 6:14
mvePete O'Hanlon13-Sep-12 6:14 
GeneralRe: Very neat Pin
Clifford Nelson13-Sep-12 6:42
Clifford Nelson13-Sep-12 6:42 
QuestionThank you Pin
Albarhami13-Sep-12 4:10
Albarhami13-Sep-12 4:10 
GeneralMy vote of 3 Pin
Albarhami13-Sep-12 4:10
Albarhami13-Sep-12 4:10 
GeneralRe: My vote of 3 Pin
Clifford Nelson13-Sep-12 6:45
Clifford Nelson13-Sep-12 6:45 
QuestionToo bad... Pin
Kenneth Haugland12-Sep-12 23:12
mvaKenneth Haugland12-Sep-12 23:12 
The discussion from the submission didnt follow. As you stated in the submission:
Clifford Nelson:
My ending criteria is not correct. Actually want to end much sooner than the algorithm. Added the square root of count as ending criteria.


C#
private static void Run()
{
    const int count = 2000000;
    _numbers = new bool[count];
    for (int i = 2; i < count; i++)
        _numbers[i] = true;
 
    int baseCounter = 1;
    int countLimit = (int) Math.Sqrt(count);
 
    while (baseCounter < countLimit)
    {
        do
        {
            baseCounter++;
            if (baseCounter == count) return;
        } while (!_numbers[baseCounter]);
 
        int counter = baseCounter << 1;
        while (counter < count)
        {
            _numbers[counter] = false;
            counter += baseCounter;
        }
    }


Some explanation and comments on the code:
C#
private static void Run()
{
    //Find the primes from 2 to 2 000 000
    const int count = 2000000;
    
    //The boolean array of the result, True if the number is prime and False if not
    //This has 2000001 prime checks as the index starts at 0
    _numbers = new bool[count];

    //Be aware that this would check for primes up to 2 000 001 as you didnt include the number 1
    //To change this you should have ended on count - 1, this sets all the records True
    for (int i = 2; i < count; i++)
        _numbers[i] = true;
 
    //The actual step that the crossout would occur
    int baseCounter = 1;

    //When we need to stop the check to cross out of non primes
    int countLimit = (int) Math.Sqrt(count);
 
    //Do the chack form 2 to sqrt of the count
    while (baseCounter < countLimit)
    {
        do
        {
            //We initially started at the number 1 but would add 1 so the first cross out starts with the     number 2
            baseCounter++;
            
            //exit the do loop when we have come to the end of the boolean array 
            if (baseCounter == count) return;
        } while (!_numbers[baseCounter]);
 
        //Multyply the number by 2 using bit shifts so 2 becomes 4 and 3 becomse 6 etc.. It should be faster than using the * operator :)
        int counter = baseCounter << 1;
        //Loop though the array and cross out non primes
        while (counter < count)
        {
            //Number is not prime
            _numbers[counter] = false;

            //increment the check by the original number, whitch is always a prime number btw
            counter += baseCounter;
        }
    }


To get the primes:
Clifford Nelson:
C#
static void Main(string[] args)
		{
			var sw = new Stopwatch();
			sw.Start();
			Run();
			sw.Stop();
			Console.WriteLine("Milliseconds run: " + sw.ElapsedMilliseconds);
			Console.WriteLine("Primes: " + _numbers.Count(i => i));
			var sum = 0;
			for (int i = 2; i < _numbers.Count(); i++)
				if (_numbers[i]) sum += i;
			Console.WriteLine("Sum of Primes: " + sum.ToString("n0"));
			for (int i = 0; i < 200; i++)
				if (_numbers[i]) Console.WriteLine("   " + i);
			Console.ReadLine();
		}


The quotes is from the discussion with me and Clifford Nelson while he submitted the alternative, while the comments is on my part Smile | :) I gave you a 4, since you didnt include the comments, but would change it to 5 if you did Smile | :)

As a side not you could also use the BitArray, whitch is slower than the bool count[] array, but uses significantly less storage according to the test by Clifford Nelson.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.