Example code - Prime numbers optimization

From 22112
Revision as of 12:27, 17 June 2024 by WikiSysop (talk | contribs) (Created page with "This is going to be a long, but quite instructive rant about various optimization ideas and techniques.<br> Let us start with my "classic" prime number generator. The idea is to see how long time is takes to find/generate the primes between 0 and 10 000 000 and use that as a measure for performance. We know that primes are integers which can only be divided by 1 and the primes itself. Therefore it is an obvious idea to evaluate if a number is a prime by starting to divi...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

This is going to be a long, but quite instructive rant about various optimization ideas and techniques.
Let us start with my "classic" prime number generator. The idea is to see how long time is takes to find/generate the primes between 0 and 10 000 000 and use that as a measure for performance.

We know that primes are integers which can only be divided by 1 and the primes itself. Therefore it is an obvious idea to evaluate if a number is a prime by starting to divide it by 2, 3, 4 ...n all the way up to the number itself and if it can be divided at any point, it is not a prime.
This basic idea can be improved in various ways.

  • It is not necessary to divide with all the numbers up to n (the prospective prime). It is sufficient to divide with the numbers up to sqrt(n). This is because for any number where bignum * smallnum = n it also goes that smallnum * bignum = n, which means we only need to find the small number which is a divisor, to identify the number as not-prime. The small number will always be less or equal to sqrt(n), and the big number will always be greater or equal to sqrt(n). sqrt(n) is the equilibrium.
  • All even numbers do not need to be checked. They are all not-primes, except 2. This means the sequence of numbers you test for division of n are odd, i.e. 3, 5, 7, 9, 11 ... sqrt(n).
  • Actually, this can be improved as you only have to divide n by the primes in the testing, not all odd numbers. If you can divide n with an odd number, which is not a prime, then the odd number is a composite of at least two other smaller numbers, which you have tested for earlier.

Math definition: All non-primes are called composite numbers because they can be calculated as a multiplication of a number of primes.


#!/usr/bin/env python3
# Prime number generator

class PrimeGenerator:
    # Class varible, known primes in consecutive order, can be extended, but must contain these
    knownprimes = [2, 3]
    # Highest tested number for prime
    highesttested = 3

    # Instatiation
    def __init__(self, number=None):
        if number is not None:
            if not isinstance(number, int):
                raise ValueError("Integer expected")
        self.target = number                
    
    # Initializing iteration
    def __iter__(self):
        if self.target is None:
            raise ValueError("No number specified")
        self.pos = 0
        return self
    
    # Find next prime
    def __next__(self):
        # Can we use the list of known primes to find the next?
        if self.pos < len(self.knownprimes):
            nextprime = self.knownprimes[self.pos]
            if nextprime >= self.target:
                raise StopIteration
            self.pos += 1
            return nextprime
        # No, start computing the next prime
        while self.target > PrimeGenerator.highesttested+1:
            PrimeGenerator.highesttested += 1
            if self._isprime(PrimeGenerator.highesttested):
                self.knownprimes.append(PrimeGenerator.highesttested)
                self.pos += 1
                return self.highesttested
        raise StopIteration

    # Private method for identifying a prime
    def _isprime(self, number):
        factor = 0
        pos = 0
        while factor*factor <= number:
            # find next potential factor either in known primes or odd numbers above last known prime
            if pos < len(self.knownprimes):
                factor = self.knownprimes[pos]
                pos += 1
            else:
                factor += 2
            # test if it truly is a factor
            if number % factor == 0:
                return False
        return True

    # It is nice be able to ask if an number is a prime
    def isprime(self, number=None):
        if number is None:
            number = self.target
        if not isinstance(number, int):
            raise ValueError("Integer expected")
        if number in PrimeGenerator.knownprimes:
            return True
        if number <= PrimeGenerator.highesttested:
            return False
        return self._isprime(number)

if __name__ == "__main__":
    # Small testing

    for i in PrimeGenerator(10000000):
        print(i)

#    print(PrimeGenerator().isprime(1000000016531)) # True
#    print(PrimeGenerator().isprime(1000000016521)) # False

    # Big prime, don't try
    # 999296950101072104250052714631