It's slow because there are 999900 loops run (minus the ones that are skipped due to the break
, but also not counting the ones done from k in primes
).
A couple things you can do to optimize:
- Use a set instead of a list to store the primes (
k in primes
will end up being much faster). Then return sorted(primes)
if you need them in order. This is made pointless by the next optimization, though.
- reduce the number of iterations in the inner loop by:
- add 2 to the primes collection initially
- only iterate through the primes collection, since any number that is larger than a known prime is either a prime itself or has prime factors that are less than itself. It makes no sense to iterate through a bunch of numbers that are known to not be primes then. You clearly realize this, since you only bother to check divisibility by primes.
- To optimize this further, you can make sure you only iterate through the primes that are less than the square root, but this is practically a micro optimization in comparison to the rest.
Edit: I tried this, and apparently calculating the square root takes longer than iterating through the primes list.
My Runtimes:
- Your code: runtime of 77ms
- Using set instead of list: 37 ms; 48% of the time needed, more than twice as fast
- Using primes instead of range to square root: 18ms; 38% of the time from using a set, more than 4x faster than original
- Same as previous, but limiting the primes to just those under
i
's square root: 27ms; still 2.9x faster than original, but slower and more complicated than the previous
Note: I was using pypy, so my run time of 77ms vs your 5-ish seconds is partially due to that. But when I ran it on my regular python 2.7 interpreter, I got 980ms (.98 seconds). So it seems that you ALSO have a slow computer, as well as a poorly optimized algorithm.
So, here's my final code for you:
def primes_n(n):
primes = [2]
for i in range(3, n+1):
for prime in primes:
if i % prime == 0:
break
else:
primes.append(i)
return primes
UPDATE: I stopped writing mine in the middle to break for lunch, so I didn't get to see the other guy's additional optimizations.
I had done a poor job of using the square root bit, so using it ISN'T actually slower (poorly implementing it is). So, after applying his optimizations, plus the little one I mentioned in the comment on his answer, I get a run time of 3ms, which is 6x faster than my previous best. Here's that code:
def primes3_2(n):
primes = [2]
for i in range(3, n+1, 2):
root_of_i = sqrt(i)
for prime in primes:
if prime <= root_of_i:
primes.append(i)
break
if i % prime == 0:
break
return primes