I've been fooling around with problem 7 from project Euler and I noticed that two of my prime finding methods are very similar but run at very different speeds.
#!/usr/bin/env python3
import timeit
def lazySieve (num_primes):
if num_primes == 0: return []
primes = [2]
test = 3
while len(primes) < num_primes:
sqrt_test = sqrt(test)
if all(test % p != 0 for p in primes[1:]): # I figured this would be faster
primes.append(test)
test += 2
return primes
def betterLazySieve (num_primes):
if num_primes == 0: return []
primes = [2]
test = 3
while len(primes) < num_primes:
for p in primes[1:]: # and this would be slower
if test % p == 0: break
else:
primes.append(test)
test += 2
return primes
if __name__ == "__main__":
ls_time = timeit.repeat("lazySieve(10001)",
setup="from __main__ import lazySieve",
repeat=10,
number=1)
bls_time = timeit.repeat("betterLazySieve(10001)",
setup="from __main__ import betterLazySieve",
repeat=10,
number=1)
print("lazySieve runtime: {}".format(min(ls_time)))
print("betterLazySieve runtime: {}".format(min(bls_time)))
This runs with the following output:
lazySieve runtime: 4.931611961917952
betterLazySieve runtime: 3.7906006319681183
And unlike this question, I don't simply want the returned value of any/all.
Is the return from all()
so slow that if overrides it's usage in all the but most niche of cases? Is the for-else
break somehow faster than the short circuited all()?
What do you think?
Edit: Added in square root loop termination check suggested by Reblochon Masque
Update: ShadowRanger's answer was correct.
After changing
all(test % p != 0 for p in primes[1:])
to
all(map(test.__mod__, primes[1:]))
I recorded the following decrease in runtime:
lazySieve runtime: 3.5917471940629184
betterLazySieve runtime: 3.7998314710566774
Edit: Removed Reblochon's speed up to keep the question clear. Sorry man.