koupuh

prime getter class

Feb 20th, 2023
765
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.69 KB | None | 0 0
  1. import math
  2. import itertools
  3.  
  4. class JPrime():
  5.     """
  6.    Class that stores a list of primes and extends it using Erotasthenes' Sieve.
  7.    """
  8.  
  9.     def __init__(self):
  10.         self.primes = [2, 3]
  11.         self._multiples = {}
  12.         self._greatest_checked_val = 3
  13.        
  14.     def clear_primes(self):
  15.         self.primes = [2, 3]
  16.         self._multiples = {}
  17.         self._greatest_checked_val = 3
  18.  
  19.     def isprime(self, num):
  20.         # trivial non-prime check
  21.         if num != 2 and (num % 2 == 0 or num < 2):
  22.             return False
  23.         self._extend_primes(num)
  24.         return num in self.primes
  25.  
  26.     def get_prime_range(self, upperbound, lowerbound = 2):
  27.         """return a list of all primes from lowerbound to upperbound, inclusive."""
  28.         self._extend_primes(upperbound)
  29.         return list(itertools.filterfalse(lambda x: x < lowerbound or x > upperbound, self.primes))
  30.        
  31.  
  32.     def _extend_primes(self, upperbound):
  33.         """extend the class object's list of primes by doing a sieve on the values that haven't been checked previously."""
  34.         if self._greatest_checked_val < upperbound - 1:
  35.             extension = list(range(self._greatest_checked_val + 2, upperbound + 1, 2))
  36.             extension = self._sieve(extension)
  37.             self.primes.extend(extension)
  38.             self.primes = list(set(self.primes)) # remove duplicates. Have not yet figured out a way to stop them from being produced in the first place.
  39.  
  40.     def _sieve(self, num_range):
  41.         """Erotasthenes' Sieve
  42.  
  43.        A "base" is selected. The multiples of that base are calculated and removed from num_range.
  44.        The base is incremented and the process repeats. The remaining values in num_range are primes.
  45.        
  46.        @param num_range: ordered list of all odd integers in some arbitary range.
  47.        
  48.        Notes:
  49.            Erotasthenes' sieve for the range 0-x only requires checking multiples of numbers less than sqrt(x), so
  50.            base is only incremented up to sqrt(x).
  51.  
  52.            An updateable sieve with no need to store a full list of checked multiples was desired. For this, the
  53.            dictionary _multiples is used. The keys of _multiples represent bases. The values represent the largest multiple
  54.            of that base that has been calculated and crossed-off to date. Only as many as sqrt(biggest_range_ever_checked) KV pairs
  55.            have to be stored in _multiples.
  56.  
  57.            For any K, the multiplier which produced its current V is obtained like: m = K/V. From there, this sieve can pick up where
  58.            the last sieve left off by incrementing m and crossing off (m*K) until that product exceeds the biggest number in num_range.
  59.  
  60.            Any multiple of an even will be even (not prime). Thus: base += 2 to avoid checking them.
  61.        """
  62.         range_max = num_range[-1]
  63.         root = math.ceil(math.sqrt(range_max))
  64.         for base in range(3, root+1, 2):
  65.             if base not in self._multiples.keys():
  66.                 # multiplying by (base-2) ensures that the first m for a new base is base. So that you don't get redunant checks like base=5, m=3. That was already checked when base=m, m=5
  67.                 self._multiples[base] = base*(base-2)
  68.  
  69.             m = int((self._multiples[base] / base)) + 2
  70.             while True:
  71.                 multiple = base * m
  72.                 if multiple > range_max:
  73.                     break
  74.                 if multiple > self._greatest_checked_val:
  75.                     self._greatest_checked_val = multiple
  76.                 try:
  77.                     num_range.remove(multiple)
  78.                 except ValueError:
  79.                     pass
  80.                 m += 2
  81.             self._multiples[base] = base * (m - 2)
  82.         return num_range
  83.  
  84.  
  85.  
  86. if __name__ == '__main__':
  87.     from time import time
  88.  
  89.     upperbounds = [100, 1000, 10000, 30000, 50000, 100000, 500000]
  90.    
  91.    
  92.     for upperbound in upperbounds:
  93.         print('=====================')
  94.         print(f'Time comparison for getting primes in range 2-{upperbound}')
  95.         p1 = JPrime()
  96.         p2 = JPrime()
  97.  
  98.  
  99.         # direct approach
  100.         start = time()
  101.         p2.get_prime_range(upperbound)
  102.         direct_time = time() - start
  103.         print('directly:\n\t', direct_time)
  104.  
  105.         #incremental approach
  106.         increments = int(math.ceil(.0005 * upperbound))
  107.         start = time()
  108.         for x in range(1, increments+1):
  109.             p1.get_prime_range(upperbound//increments*x)
  110.         incremental_time = time() - start
  111.         print(f'in increments({increments}):\n\t{incremental_time}')
  112.  
  113.  
  114.         print('ratio direct/incremental:', direct_time/incremental_time)
  115.         print('=====================\n\n')
Advertisement
Add Comment
Please, Sign In to add comment