List comprehensions are a powerful tool in Python. Think of them as a for-loop in steroids. :-) You can use them to implement trial division, which is a simple way of finding primes.
It works like this:
In [4]: sum(prime_list(100))
Out[4]: 1061
The prime_list
function:
def prime_list(num):
"""Returns a list of all prime numbers up to and including num.
Based on trial division.
:num: highest number to test
:returns: a list of primes up to num
"""
if num < 3:
raise ValueError('this function only accepts arguments > 2')
candidates = range(3, num+1, 2) # (a)
L = [c for c in candidates if all(c % p != 0 for p in range(3, c, 2))] #(b)
return [1, 2] + L
Now for the explanation. With the exception of 2, all prime numbers are odd. So all the odd numbers from 3 to num
(100 in this case) are candidates for prime numbers. Let's generate a list of those as done at (a):
In [5]: num = 100
In [6]: range(3, num+1, 2)
Out[6]: [3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95, 97, 99]
For an odd number c
to be prime, one must ensure that c
modulo all previous odd numbers p
must be non-zero. Let's say c
is 25.
In [7]: c = 25
Then p
is in:
In [8]: range(3, c, 2)
Out[8]: [3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23]
Now check c
modulo p
:
In [9]: [c % p != 0 for p in range(3, c, 2)]
Out[9]: [True, False, True, True, True, True, True, True, True, True, True]
As we know 25 % 5 == 0, so the second item in the list is False
. However, for a number to be prime, all items in the list must be true:
In [10]: all(c % p != 0 for p in range(3, c, 2))
Out[10]: False
So 25 is not a prime.
Let's try again for c
is 41:
In [11]: c = 41
In [12]: range(3, c, 2)
Out[12]: [3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39]
In [13]: [c % p != 0 for p in range(3, c, 2)]
Out[13]: [True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True]
In [14]: all(c % p != 0 for p in range(3, c, 2))
Out[14]: True
And indeed 41 is a prime.
So prime_list returns a list of primes:
In [15]: prime_list(100)
Out[15]: [1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
To sum that up we simply use the sum()
function:
In [16]: sum(prime_list(100))
Out[16]: 1061
Edit: Based on the comments, I tried the improvement that WillNess suggested and a real sieve using sets:
def prime_list(num):
if num < 3:
raise ValueError('this function only accepts arguments > 2')
candidates = range(3, num+1, 2)
L = [c for c in candidates if all(c % p != 0 for p in range(3, c, 2))]
return [1, 2] + L
def prime_list2(num):
if num < 3:
raise ValueError('this function only accepts arguments > 2')
candidates = range(3, num+1, 2)
L = [c for c in candidates if all(c % p != 0 for p in
range(3, int(math.sqrt(c))+1, 2))]
return [1, 2] + L
def prime_list3(num):
candidates = set(range(3, num+1, 2))
results = [1, 2]
while candidates:
t = list(candidates)[0]
results.append(t)
candidates -= set(range(t, num+1, t))
return results
Some timings for num=100
:
In [8]: %timeit prime_list(100)
1000 loops, best of 3: 180 us per loop
In [9]: %timeit prime_list2(100)
1000 loops, best of 3: 192 us per loop
In [10]: %timeit prime_list3(100)
10000 loops, best of 3: 83.9 us per loop
And num=1000
:
In [11]: %timeit prime_list(1000)
100 loops, best of 3: 8.05 ms per loop
In [12]: %timeit prime_list2(1000)
100 loops, best of 3: 2.43 ms per loop
In [13]: %timeit prime_list3(1000)
1000 loops, best of 3: 1.26 ms per loop
num = 5000
:
In [14]: %timeit prime_list(5000)
1 loops, best of 3: 166 ms per loop
In [15]: %timeit prime_list2(5000)
100 loops, best of 3: 11.1 ms per loop
In [16]: %timeit prime_list3(5000)
100 loops, best of 3: 15.3 ms per loop
And finally num=50000
:
In [18]: %timeit prime_list3(50000)
1 loops, best of 3: 1.49 s per loop
In [19]: %timeit prime_list2(50000)
1 loops, best of 3: 170 ms per loop