7

I have been sweating over this piece of code -- which returns all the primes in a list:

primes = range(2, 20) 
for i in range(2, 8): 
     primes = filter(lambda x: x == i or x % i, primes)

print primes

It works... but I don't understand the role "x == i or x % i" plays in the whole thing.

I also don't understand why the second range is only 2 to 7.

I even created a Python implementation of the Sieve of Eratosthenes, hoping that might give me some insight, but it didn't.

When I remove the x % i component, I would expect this code to give me the numbers common to both sets, but it does not:

nums = [2, 20] 
for i in range(2, 8): 
     nums = filter(lambda x: x == i, nums)

print nums

Why is this?

Likewise, when I remove the x == i component, it returns the prime numbers from 11 to 19.

nums = range(2, 20) 

for i in range(2, 8): 
     nums = filter(lambda x: x % i, nums)

print nums

Again, I don't understand why it ignores all primes below 11.

Next, I tried this:

nums = [13] 

for i in range(2, 8): 
     nums = filter(lambda x: x % i, nums)

print nums

Again, this makes no sense to me. The lambda is iterating over x in nums correct? And i is iterating over the range 2 to 7. So, aren't we taking 13 % i... for 2 to 7? How does that result in "13"?

Using the same logic as immediately above, I did the same thing with "13", but using x == i in the lambda.

for i in range(2, 8): 
     nums = filter(lambda x: x == i, nums)

print nums

And as I expected, it returned an empty list -- which makes sense in my mind, because 13 never appears in the range of 2 to 7.

For reference to anyone trying to help, this is the mindset I'm in when I work with filter() and lambdas:

a = range (1, 11)
b = range (9, 20) 

for i in filter(lambda x: x in a, b):
    print i,

This, of course, gives us "9 10". I know the structure of the loop is different, but hopefully it will help you see where my confusion lies.

I have worked with filter() and lambdas somewhat extensively, so I thought I could figure it out, but I'm stumped! I just hope the answer isn't so blindingly obvious that I feel like an idiot...

mbomb007
  • 3,788
  • 3
  • 39
  • 68
fra
  • 205
  • 1
  • 3
  • 7
  • Where did you get that original snippet from? It's pretty bad. I would _not_ look to it as an example of how to find the prime numbers in a list. – dursk Jan 16 '15 at 18:37
  • 1
    I ran across it on Stack Overflow somewhere. I just want to find out how it works. – fra Jan 16 '15 at 18:53
  • 1
    `x == i or x % i` is the same as saying `x % i == 0 and x != i`. When `x == i`, `x % i` will never be zero. – dursk Jan 16 '15 at 18:59

7 Answers7

3

The first code block you posted is the easiest example for me to explain this:

primes = range(2, 20) 
for i in range(2, 8): 
    primes = filter(lambda x: x == i or x % i, primes)
print primes

When using the Sieve of Eratosthenes method, the important thing to note is that you only have to remove numbers that are products of numbers up to the square root of the max. The use of range(2,8) above implements this (it goes from 2 to 7, which is further than necessary). The square root of 19 (the highest number in the outer range that is checked) is between 4 and 5. So the highest number that should be checked in the range is 4 (we only need to check integers).

Using this knowledge, you could improve the code to be as follows (this finds primes <= 19):

import math
max = 19    #Set it here
max += 1
primes = range(2, max) 
for i in range(2, int( math.ceil(math.sqrt(max)) )): 
    primes = filter(lambda x: x == i or x % i, primes)
print primes

Note that instead of using floor and then adding one because range is exclusive, I use ceil.

Run it here: http://repl.it/8N8

Edit: I also realized this (and the code provided in the question) isn't a complete implementation of the sieve method, since according to the algorithm, we should only flag multiples of primes, meaning that the inner use of range is not as efficient as it should be.

See a graphical illustration of the algorithm in progress:

Sieve of Eratosthenes

mbomb007
  • 3,788
  • 3
  • 39
  • 68
  • an obligatory clarification of the OP code [*not* being the sieve of *Eratosthenes*](http://stackoverflow.com/questions/27990094/finding-primes-with-modulo-in-python#comment44393470_27991297). The included picture shows why: on step 1 it marks the 2 and its multiples and starts from 3 on the next step (working on the same original list); whereas the OP code passes through the 2 and all the numbers that are *not* multiples of 2, on step 1, and then repeats from 2 again (with fewer remaining numbers in the list) on the next step. SoE marks 45 *twice*; OP's would remove it *once* (if up to 100). – Will Ness Jan 17 '15 at 17:49
  • Yeah. The important thing to note is that there is a trade-off. You can either start with a list and *remove* numbers, or you can *flag* numbers (which is what SoT does). If I was programming to be efficient, in both performance and memory, I would encode the flags with each flag represented by a single bit in memory (8 per byte). I would set flags by ANDing the byte with another one. At the end, then you just count which bits are still set. – mbomb007 Jan 18 '15 at 02:42
  • right. (correction: "(which is what *SoE* does)".) or your language could have BitSets (like Java), or bit-packed Boolean vectors (like C++'s `vector`). – Will Ness Jan 18 '15 at 07:42
  • OK, that was so subtle I missed it completely, and it fixes both of my objections. I'll remove my comment. – Mark Ransom Jan 20 '15 at 01:48
3

It looks like a compact (but somewhat obscure) implementation of the Sieve of Eratosthenes [EDIT: as pointed out in the comments, this is in fact an "unfaithful sieve" as the trial division causes worse time complexity than the actual Sieve of Eratosthenes].

The first line is just an arbitrary search range of consecutive integers to filter for primes:

primes = range(2, 20)

Next, following the sieve algorithm, we iterate with integer i in range (2, n) where n is naively the largest number in the search range (though in this case, 7 is the chosen upper bound -- more on this below).

for i in range(2, 8): 
    primes = filter(lambda x: x == i or x % i, primes)

The algorithm states that we include i and exclude multiples of i. That's what the lambda predicates filter for --

  • include i: x == 1
  • exclude multiples of i: x % i -- this is short hand for x % i != 0. In other words, x is not divisible by i, or alternatively, x is not a multiple of i.

The upper bound of 8 seems somewhat arbitrary -- minimally, we only need to search up to sqrt(n), since sqrt(n) * sqrt(n) = n means that sqrt(n) is an upper bound on the search space.

The square root of 19 is approximately 4.4, and in this example you see that the list of primes does not change after i = 3.

In [18]: primes = range(2, 20)

In [19]: for i in range(2, 8):
   ....:     primes = filter(lambda x: x == i or x % i, primes)
   ....:     print i, primes
   ....:
2 [2, 3, 5, 7, 9, 11, 13, 15, 17, 19]
3 [2, 3, 5, 7, 11, 13, 17, 19]
4 [2, 3, 5, 7, 11, 13, 17, 19]
5 [2, 3, 5, 7, 11, 13, 17, 19]
6 [2, 3, 5, 7, 11, 13, 17, 19]
7 [2, 3, 5, 7, 11, 13, 17, 19]
THK
  • 701
  • 4
  • 10
  • Also, the `range` function is not inclusive. It's only searching up to `19`. – mbomb007 Jan 16 '15 at 19:36
  • not of Eratosthenes though, but the sieve by trial division. sieve of Eratosthenes doesn't test for divisibility at all, it just enumerates multiples of each found prime, by addition in constant steps, so it generates the composites by itself, without testing. That's what special about it. – Will Ness Jan 17 '15 at 17:38
  • @WillNess, indeed, the time complexity in question is different... I shall edit my post. – THK Jan 19 '15 at 20:15
1

I have written a simple list comprehension to generate prime numbers. Of course the core idea was copied in stack overflow. To be honest it took time for me to understand it as I was beginner in python. I have used this list comprehension by calling a lambda function separately. So first I will discuss the lambda function.

lambda function: is_prime = lambda x: all(x % y != 0 for y in range(2,int(math.sqrt(x)) + 1))

Now the list comprehension using the above lambda.

primes = [x for x in range(30) if is_prime(x) == True]

0

Try this:

ip_list = [100, 200, 300, 17, 19, 23, 21]

is_prime = list(filter(lambda i: all(i%j!=0 for j in range(2, i//2)), ip_list))

print(is_prime)
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
0

Here is Code for getting prime number from 2 - 100

Code :

l=list(filter(lambda x: not list(filter(lambda y : x%y==0, range(2,x))),range(2,100)))
DhanushNayak
  • 171
  • 1
  • 3
-1

I think the answer is fairly simple. Increase your set of primes from range(2,20) to range(2,30) and try your thought experiment again. It will me much more obvious.

the filter function will return values for the range of range(2,20) that the

filter(lambda x: x == i or x % i, primes)

returns true.

In addition to increasing the primes from range(2,20) to range(2,30), play with your inner filter criteria and you will start to see the differences you are looking for.

#!/usr/bin/python

primes = range(2, 30) 
for i in range(2, 3): 
    primes = filter(lambda x: x == i or x % i, primes)

print primes

which results in:

[2, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29]

and

#!/usr/bin/python

primes = range(2, 30) 
for i in range(2, 4): 
    primes = filter(lambda x: x == i or x % i, primes)

print primes

results in

[2, 3, 5, 7, 11, 13, 17, 19, 23, 25, 29]
condor
  • 22
  • 1
-2

This code snippet will print prime numbers from 1 to 15:

lst = filter(lambda x: len(list(filter(lambda n: x % n != 0, range(2, x)))) == x - 2, range(15))
print (list(lst))
MrBean Bremen
  • 14,916
  • 3
  • 26
  • 46
  • 1
    In programming, you should rarely use a one-liner such as the one above. By so much unnecessary nesting, you decrease the readability of the code. It is also best to not just give a code answer on SO. Try and write something with it to explain what you have done and why it works. – incarnadine Sep 18 '20 at 18:27
  • The question is not "how do I print prime numbers", so this is not answering the question at all. – C. Fennell Sep 18 '20 at 21:51