Skip to content

Instantly share code, notes, and snippets.

@sosi-deadeye
Created March 25, 2021 18:57
Show Gist options
  • Save sosi-deadeye/f0f592e6de54570060476ff8c5ad8094 to your computer and use it in GitHub Desktop.
Save sosi-deadeye/f0f592e6de54570060476ff8c5ad8094 to your computer and use it in GitHub Desktop.
made it more pythonnic
#!/usr/bin/env python3
"""
Python Prime Sieve
MyFirstPython Program (tm) Dave Plummer 8/9/2018
This is the main prime_sieve class. Call it with the number you wish as an upper limit, then
call the run method to do the calculation. print_results will dump the count to check validity.
Updated 3/22/2021 for Dave's Garage episode comparing C++, C#, and Python
Updated 3/22/2021 more Pythonic
"""
import timeit
from math import sqrt
class PrimeSieve:
prime_counts = {
10: 1,
100: 25,
1000: 168,
10000: 1229,
100000: 9592,
1000000: 78498,
10000000: 664579,
100000000: 5761455,
}
def __init__(self, limit: int):
self.sieve_size = limit
self.raw_bits = [True] * ((self.sieve_size + 1) // 2)
def validate_results(self) -> bool:
"""
Check to see if this is an upper_limit we can
"""
if self.sieve_size in self.prime_counts:
# the data, and (b) our count matches. Since it will return
return self.prime_counts[self.sieve_size] == self.count_primes()
# false for an unknown upper_limit, can't assume false == bad
return False
def get_bit(self, index: int) -> bool:
"""
Gets a bit from the array of bits, but automatically just filters out even numbers as false,
and then only uses half as many bits for actual storage
"""
# even numbers are automatically returned as non-prime
if index % 2 == 0:
return False
else:
return self.raw_bits[index // 2]
def clear_bit(self, index: int) -> None:
"""
Reciprocal of get_bit, ignores even numbers and just stores the odds. Since the prime sieve work should
never waste time clearing even numbers, this code will assert if you try to
"""
if index % 2 == 0:
return
self.raw_bits[index // 2] = False
def run(self) -> None:
"""
Reciprocal of get_bit, ignores even numbers and just stores the odds. Since the prime sieve work should
never waste time clearing even numbers, this code will assert if you try to
"""
factor = 3
q = sqrt(self.sieve_size)
while factor < q:
for num in range(factor, self.sieve_size):
if self.get_bit(num):
factor = num
break
# If marking factor 3, you wouldn't mark 6 (it's a multiple of 2) so start with the 3rd instance of this
# factor's multiple. We can then step by factor * 2 because every second one is going to be even by
# definition
for num in range(factor * 3, self.sieve_size, factor * 2):
self.clear_bit(num)
# No need to check evens, so skip to next odd (factor = 3, 5, 7, 9...)
factor += 2
def count_primes(self) -> int:
"""
Return the count of bits that are still set in the sieve. Assumes you've already called
run, of course!
"""
# yes, you can calculate with booleans
# True == 1 and False == 0
return sum(self.raw_bits)
def print_results(self, show: bool, duration: float, passes: int) -> None:
"""
Displays the primes found (or just the total count, depending on what you ask for)
"""
# Since we auto-filter evens, we have to special case the number 2 which is prime
if show:
# you've control what is concatenated to the end of the str
# an empty str will get rid of the default newline
print("2, ", end="")
count = 1
# Count (and optionally dump) the primes that were found below the limit
for num in range(3, self.sieve_size):
if self.get_bit(num):
if show:
print(f"{num}, ")
count += 1
assert count == self.count_primes()
print()
print("Passes:", passes)
print("Time:", duration)
print("Avg:", duration / passes)
print(f"Limit:", self.sieve_size)
print(f"Count: {count}")
print(f"Valid:", self.validate_results())
# if this source code is imported from another python module
# the __name__ != __main__
# and it will prevent to execute the code
# this let you run the script from commandline
# but you can also import this code here and
# reuse it in another Python module.
if __name__ == "__main__":
print("Please wait ⌛")
# Record our starting time
tStart = timeit.default_timer()
# We're going to count how many passes we make in fixed window of time
passes = 0
# Calc the primes up to a million
# Reusing the instance saves time
sieve = PrimeSieve(1000000)
# Run until more than 10 seconds have elapsed
while timeit.default_timer() - tStart < 10:
# Find the results
sieve.run()
# Count this pass
passes += 1
# After the "at least 10 seconds", get the actual elapsed
tD = timeit.default_timer() - tStart
# Display outcome
sieve.print_results(True, tD, passes)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment