Prime number calculation fun

前端 未结 18 1753
深忆病人
深忆病人 2020-12-05 15:32

We\'re having a bit of fun here at work. It all started with one of the guys setting up a Hackintosh and we were wondering whether it was faster than a Windows Box of (nearl

相关标签:
18条回答
  • 2020-12-05 15:52

    I found this code somewhere on my machine when I started reading this blog entry about prime numbers. The code is in C# and the algorithm I used came from my head although it is probably somewhere on Wikipedia. ;) Anyway, it can fetch the first 150000 prime numbers in about 300ms. I discovered that the sum of the n first odd numbers is equal to n^2. Again, there is probably a proof of this somewhere on wikipedia. So knowing this, I can write an algorithm that wil never have to calculate a square root but I have to calculate incrementally to find the primes. So if you want the Nth prime, this algo will have to find the (N-1) preceding primes before! So there it is. Enjoy!

    
    //
    // Finds the n first prime numbers.
    //
    //count: Number of prime numbers to find.
    //listPrimes: A reference to a list that will contain all n first prime if getLast is set to false.
    //getLast: If true, the list will only contain the nth prime number.
    //
    static ulong GetPrimes(ulong count, ref IList listPrimes, bool getLast)
    {
        if (count == 0)
            return 0;
        if (count == 1)
        {
            if (listPrimes != null)
            {
                if (!getLast || (count == 1))
                    listPrimes.Add(2);
            }
    
            return count;
        }
    
        ulong currentSquare = 1;
        ulong nextSquare = 9;
        ulong nextSquareIndex = 3;
        ulong primesCount = 1;
    
        List dividers = new List();
    
        //Only check for odd numbers starting with 3.
        for (ulong curNumber = 3; (curNumber  (nextSquareIndex % div) == 0) == false)
                    dividers.Add(nextSquareIndex);
    
                //Move to next square number
                currentSquare = nextSquare;
    
                //Skip the even dividers so take the next odd square number.
                nextSquare += (4 * (nextSquareIndex + 1));
                nextSquareIndex += 2;
    
                //We may continue as a square number is never a prime number for obvious reasons :).
                continue;
            }
    
            //Check if there is at least one divider for the current number.
            //If so, this is not a prime number.
            if (dividers.Exists(div => (curNumber % div) == 0) == false)
            {
                if (listPrimes != null)
                {
                    //Unless we requested only the last prime, add it to the list of found prime numbers.
                    if (!getLast || (primesCount + 1 == count))
                        listPrimes.Add(curNumber);
                }
                primesCount++;
            }
        }
    
        return primesCount;
    }
    
    0 讨论(0)
  • 2020-12-05 15:55

    Since you're searching for them in ascending order, you could keep a list of the primes you've already found and only check for divisibility against them, since all non-prime numbers can be reduced to a list of lesser prime factors. Combine that with the previous tip about not checking for factors over the square root of the current number, and you'll have yourself a pretty darn efficient implementation.

    0 讨论(0)
  • 2020-12-05 15:57

    Keeping in mind that there are better ways to look for primes...

    I think that you can take this loop:

    for (int i = 2; i < top; i++)

    and make it so that your counter variable i goes from 3 and only tries to do the mod on odd numbers, since all primes other than 2 are never divisible by any even numbers.

    0 讨论(0)
  • 2020-12-05 15:59

    That's a bit worse than my sieve did on a 8 Mhz 8088 in turbo pascal in 1986 or so. But that was after optimisations :)

    0 讨论(0)
  • 2020-12-05 16:02

    @ Mark Ransom - not sure if this is java code

    They will moan possibly but I wished to rewrite using paradigm I have learned to trust in Java and they said to have some fun, please make sure they understand that spec says nothing that effects ordering on the returned result set, also you would cast result set dot values() to a list type given my one-off in Notepad before taking a short errand

    =============== begin untested code ===============

    package demo;
    
    import java.util.List;
    import java.util.HashSet;
    
    class Primality
    {
      int current = 0;
      int minValue;
      private static final HashSet<Integer> resultSet = new HashSet<Integer>();
      final int increment = 2;
      // An obvious optimization is to use some already known work as an internal
      // constant table of some kind, reducing approaches to boundary conditions.
      int[] alreadyKown = 
      {
         2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 
         43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 
         127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197,
         199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281,
         283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379,
         383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463,
         467, 479, 487, 491, 499, 503, 509, 521, 523, 541
      };
      // Trivial constructor.
    
      public Primality(int minValue)
       {
          this.minValue = minValue;
      }
      List calcPrimes( int startValue )
      {
          // eliminate several hundred already known primes 
          // by hardcoding the first few dozen - implemented 
          // from prior work by J.F. Sebastian
          if( startValue > this.minValue )
          {
              // Duh.
              current = Math.abs( start );
               do
               {
                   boolean prime = true;
                   int index = current;
                   do
                   {
                      if(current % index == 0)
                      {
                          // here, current cannot be prime so break.
                          prime = false;
                          break;
                       }
                   while( --index > 0x00000000 );
    
                   // Unreachable if not prime
                   // Here for clarity
    
                   if ( prime )
                   {     
                       resultSet dot add ( or put or whatever it is )
                               new Integer ( current ) ;
                   }
               }
               while( ( current - increment ) > this.minValue );
               // Sanity check
               if resultSet dot size is greater that zero
               {
                  for ( int anInt : alreadyKown ) { resultSet.add( new Integer ( anInt ) );}
                 return resultSet;
               }
               else throw an exception ....
          }
    

    =============== end untested code ===============

    Using Hash Sets allows searching results as B-Trees, thus results could be stacked up until the machine begins to fail then that starting point could be used for another block of testing == the end of one run used as a Constructor value for another run, persisting to disk work already accomplished and allowing incremental feed-forward designs. Burnt out right now, loop logic needs analysis.

    patch (plus add sqrt) :

      if(current % 5 == 0 )
      if(current % 7 == 0 )
      if( ( ( ( current % 12 ) +1 ) == 0) || ( ( ( current % 12 ) -1 ) == 0) ){break;}
      if( ( ( ( current % 18 ) +1 ) == 0) || ( ( ( current % 18 ) -1 ) == 0) ){break;}
      if( ( ( ( current % 24 ) +1 ) == 0) || ( ( ( current % 24 ) -1 ) == 0) ){break;}
      if( ( ( ( current % 36 ) +1 ) == 0) || ( ( ( current % 36 ) -1 ) == 0) ){break;}
      if( ( ( ( current % 24 ) +1 ) == 0) || ( ( ( current % 42 ) -1 ) == 0) ){break;}
    
    
    // and - new work this morning:
    
    
    package demo;
    
    /**
     *
     * Buncha stuff deleted for posting .... duh.
     *
     * @author  Author
     * @version 0.2.1
     *
     * Note strings are base36
     */
    public final class Alice extends java.util.HashSet<java.lang.String>
    {
        // prints 14551 so it's 14 ½ seconds to get 40,000 likely primes
        // using Java built-in on amd sempron 1.8 ghz / 1600 mhz front side bus 256 k L-2
        public static void main(java.lang.String[] args)
        {
            try
            {
                final long start=System.currentTimeMillis();
                // VM exhibits spurious 16-bit pointer behaviour somewhere after 40,000
                final java.lang.Integer upperBound=new java.lang.Integer(40000);
                int index = upperBound.intValue();
    
                final java.util.HashSet<java.lang.String>hashSet
                = new java.util.HashSet<java.lang.String>(upperBound.intValue());//
                // Arbitraily chosen value, based on no idea where to start.
                java.math.BigInteger probablePrime
                = new java.math.BigInteger(16,java.security.SecureRandom.getInstance("SHA1PRNG"));
                do
                {
                    java.math.BigInteger nextProbablePrime = probablePrime.nextProbablePrime();
                    if(hashSet.add(new java.lang.String(nextProbablePrime.toString(Character.MAX_RADIX))))
                    {
                        probablePrime = nextProbablePrime;
                        if( ( index % 100 ) == 0x00000000 )
                        {
                            // System.out.println(nextProbablePrime.toString(Character.MAX_RADIX));//
                            continue;
                        }
                        else
                        {
                            continue;
                        }
                    }
                    else
                    {
                        throw new StackOverflowError(new String("hashSet.add(string) failed on iteration: "+
                        Integer.toString(upperBound.intValue() - index)));
                    }
                }
                while(--index > 0x00000000);
                System.err.println(Long.toString( System.currentTimeMillis() - start));
            }
            catch(java.security.NoSuchAlgorithmException nsae)
            {
                // Never happen
                return;
            }
            catch(java.lang.StackOverflowError soe)
            {
                // Might happen
                System.out.println(soe.getMessage());//
                return;
            }
        }
    }// end class Alice
    
    0 讨论(0)
  • 2020-12-05 16:03

    Well I see a couple of quick optimizations that can be done. First you don't have to try each number up to half of the current number.

    Instead you only have try up to the square root of the current number.

    And the other optimization was what BP said with a twist: Instead of

    int count = 0;
    ...
    for (int i = 2; i < top; i++)
    ...
    if (current == 2)
      current++;
    else
      current += 2;
    

    use

    int count = 1;
    ...
    for (int i = 3; i < top; i += 2)
    ...
    current += 2;
    

    This should speed things up quite a lot.

    Edit:
    More optimization courtesy of Joe Pineda:
    Remove the variable "top".

    int count = 1;
    ...
    for (int i = 3; i*i <= current; i += 2)
    ...
    current += 2;
    

    If this optimization indeed increases speed is up to java.
    Calculating the square root takes a lot of time compared to multiplying two numbers. However since we move the multiplication into the for loop this is done every single loop. So this COULD slow things down depending on how fast the square root algorithm in java is.

    0 讨论(0)
提交回复
热议问题