Conditional list comprehension - python

I am trying to gain a better understanding of how list comprehensions work.
I have the following function that returns true or false if a number is prime (which I found somewhere on the forum but cant remember where):
import math
def is_prime(n):
if n % 2 == 0 and n > 2:
return False
for i in range(3, int(math.sqrt(n)) + 1, 2):
if n % i == 0:
return False
return True
if I run:
[x for x in range(2, num) if is_prime(x)]
i get the desired result
[2, 3, 5, 7, 11, 13, 17, 19]
in trying to convert the whole function to a list comprehension I came up with:
[x if not (x%2==0 and x > 2) else x for x in range (2, num)
for i in range(3, int(x**(1/2))+1, 2) if not (x%i==0)]
which produces:
[10, 11, 13, 14, 16, 17, 19]
not sure where I am going wrong and would appreciate some help. In truth I think it is better to use the function in this case, but like I said I am trying to understand the list comprehension and its capabilities.

You can do this:
[n for n in range(2, num) if n % 2 != 0 and n > 2 and all(n % i != 0 for i in range(3, int(math.sqrt(n)) + 1, 2))]
although one liner just for the sake of it is not necessarily a good thing. IMO using a prime tester function like you do is better...
NOTE: what doesn't work in your try is that you modified the logic of your outer list comprehension. You still want a structure like [n for n in range(...) if (expression testing if n is prime)].

The main problem is that you're making a convoluted translation. Try simplifying the original condition so that you return "x if x==2, else x ..." as you've already done. I'm just giving you a leading hint here; from what you've done already, I think you can figure it out.
If not, try simplifying the "else" expression so you learn the logic of that clause. Then I'm sure you'll get it. You're doing well.

There are logical errors in the code above
1/2 evaluates to 0, not to 0.5. To do that, either use 0.5 or 1/2.0 or 1.0/2
Also it does not take care of 2 as a special case. as (for i in range(3, int(x**(1/2.0))+1, 2) if not (x%i==0)) is not executed
for i in range(3, int(x**(1/2.0))+1, 2) if not (x%i==0) is also a logical error as whenever this condition is true, it causes multiple entries to be added
the correct way would be
[x for x in range (2, num) if x == 2 or (x > 2 and x % 2 == 1 and len([i for i in range(3, int(x**(1/2.0))+1, 2) if x%i == 0 ])==0)]

Related

Why is my function skipping the else statement?

def pi_euler2(n):
pi=[1,2]
if n==1:
return 1
if n==2:
two=sum([x**-1 for x in pi])
return two
for i in range(3,n+1):
if isprime(i)==True:
if (i+1)%4==0:
i=abs(i)
pi.append(i)
if (i-1)%4==0:
pi.append(i*-1)
else:
for j in range(2,int(math.sqrt(i))+1):
if isinstance(i/j,int)==True:
k=i/j
print(k)
pi.append((pi[j-1])*pi[k-1])
return pi
I am trying to make a python program which estimates the value of pi by making it do an infinite series (up to length n). But the problem with my code is that for some reason it skips over the else statement (for when the number is not prime).
The problem is part of this section of the code
for i in range(3,n+1):
if isprime(i)==True:
if (i+1)%4==0:
i=abs(i)
pi.append(i)
if (i-1)%4==0:
pi.append(i*-1)
else:
for j in range(2,int(math.sqrt(i))+1):
if isinstance(i/j,int)==True:
k=i/j
print(k)
pi.append((pi[j-1])*pi[k-1])
The output only ever consists of prime numbers (e.g pi_euler2(50) outputs [1, 2, 3, -5, 7, 11, -13, -17, 19, 23, -29, 31, -37, -41, 43, 47])
The isprime function is
def isprime(n):
for j in range(2,int(n**0.5)+1):
if n%j == 0:
return False
return True
the problem isn't with else part of your code, it works every time. the problematic part of your code is: isinstance(i/j,int)
every time you divide 2 numbers in python, it gives you a decimal number like 9/3 isn't just 3. the output is 3.0
so the isinstance() gives false every time. hence no append in pi.
you can try to use if(i%j == 0).
then the if in j loop becomes:
if i%j==0: k=int(i/j) ...
and the output for 10 in your function comes out to be:
[1, 2, 3, 4, -5, 6, 7, 8, 9, -10] try and correct that
Also, your isprime function seems to be incomplete. It should look something like this.
def isprime(n):
if n < 2:
return False
for j in range(2, int(n ** 0.5) + 1):
if n % j == 0:
return False
return True
You could probably just use already-provided prime checking functions (libraries such as mpmath, sympy, etc. include this functionality) which could do the job more accurately and efficiently.

Python remainder operator using list

Trying to use the remainder operator to find positive values in a list and print them as it finds them.
ExampleList=[2,5,-34,0,389,202,8]
I guess I don't understand how to make the operator use the list and then print the values.
import sys
li = [2,5,-34,0,389,202,8]
m = sys.maxint
print [e for e in li if e == e%m]
ExampleList=[2,5,-34,0,389,202,8]
for i in ExampleList:
if ((i % 10) != (abs(i) % 10)):
print (i)
The above code works for this particular example but might not work generally because there are numbers which no matter if they are positive or negative give the same modulus results.
Well, any solution using < or related comparisons or abs isn't actually using any unique features of %. So just for fun, a kinda silly way to do it without using any special functions, and with no comparisons other than == that actually use a behavior of % in a meaningful way (as opposed to just adding % to code that would produce the correct result without it) for this purpose:
>>> li = [2,5,-34,0,389,202,8]
>>> print([x for x in li if x == x % (x ** 2 + 1)])
[2, 5, 0, 389, 202, 8]
We square x to create a value known to be larger than the absolute value of x (and add 1 to handle the special cases of 0 and 1).
Another kind of tricky approach is to use the number as the divisor in such a way as to always get a fixed value from non-negative inputs:
# 1 % (x+2 or x+1) returns 1 for 0 or positive, non-1 for negative
# Use x+2 or x+1 to avoid ZeroDivisionError for x == 0
>>> print([x for x in li if 1 == 1 % (x+2 or x+1)])
[2, 5, 0, 389, 202, 8]
# We can even avoid using == by substituting subtraction since the result
# is a fixed 1 for non-negative, though it's pretty ugly looking
>>> print([x for x in li if not 1 % (x+2 or x+1) - 1])
[2, 5, 0, 389, 202, 8]
# Or if 0 shouldn't be included in results, tweak the divisor differently:
>>> print([x for x in li if 1 == 1 % (x+1 or x)])
[2, 5, 389, 202, 8]

Modify the iterated list of a for loop inside the loop

A for loop iterate through a long list. I tried to accelerate the iteration modifying the list (without success).
the code:
from math import sqrt
def holeofStrainer():
isPrime = [False, False] + [True]*999999
for num in range(3, len(isPrime)):
if isPrime[num] == False:
continue
else:
for x in range(2, int(sqrt(num)) + 1):
if num % x == 0:
isPrime[num] = False
break
else:
isPrime[num] = True
for item in range (2, int(1000001/num) + 2):
ple = item * num
if ple < len(isPrime):
isPrime[ple] = False
return(isPrime)
print(holeofStrainer())
The goal of line 5 is to spare unnecessary calculation.
in lines 14-17 I make the modifications (following the sieve of Eratosthenes, I change the value of the multiples of prime numbers to False) avoiding by this way more calculation through the line 5.
SUMMARY:
Is it possible to modify the loop-iterated list from the loop itself?
If the answer is Yes, why is it not good in my code?
I'm pretty sure, you stumbled into a case of premature optimization, i.e. trying to build fast code without having working code first.
Your outer for loop
for num in isPrime:
iterates over the True and False values in isPrime, not over the numbers or index positions.
for item in range (2, int(1000001/num) + 2):
ple = item * num
if ple < len(isPrime):
isPrime[ple] = False
As also noted in the comments by Padraic Cunningham, I have no idea what this part of the code is supposed to achieve. There's also a DRY violation (duplicating the limit number).
If I clean all of this up, I end up with something like
def holeofStrainer(nprimes):
isPrime = [False, False] + [True] * (nprimes - 1)
for num in (num for num, numIsPrime in enumerate(isPrime) if numIsPrime):
for x in xrange(2, int(sqrt(num)) + 1):
if num % x == 0:
isPrime[num] = False
break
return isPrime
Note the use of xrange() instead of range(). If this is python 2, range() creates a full list when called, which hurts quite a lot for the larger numbers.
holeofStrainer(1000000) takes about 14s to run around here. This probably can be done faster (e.g. by checking and storing only odd numbers in the first place), but there are already working versions on SO that are faster.

Can't figure out how to use all() to work in my "code"

m = range(1, 2000000, 2)
sum1 = 2
for x in xrange(1, 2000000, 2):
for y in m:
if x != y:
if x%y == 0:
m.remove(x)
if all(x%y != 0):
sum1 += x
That's what I've written. It's about a problem, trying to add all the primes bellow two million. My problem is in the all() statement. What I want to happen is to check if x is a prime; that is true only if every x%y gives a remainder.
Also if I use a can I use a statement (break?) to stop the loop if y > x/3 like so:
m = range(1, 2000000, 2)
sum1 = 2
for x in xrange(1, 2000000, 2):
for y in m:
if y > x/3:
break
else:
if x != y:
if x%y == 0:
m.remove(x)
if all(x%y != 0):
sum1 += x
You have to pass a sequence or iterable to all -- it just tests whether or not all the items passed to it evaluate as true. Here's the right way to use all:
>>> all([True, True, True])
True
>>> all([True, False, True])
False
>>> all([x > 5 for x in range(10)])
False
>>> all([x > 5 for x in range(6, 10)])
True
>>> all(x > 5 for x in range(6, 10))
True
That last one is the best, because it takes advantage of short-circuiting.
However, your call to all in your code is pointless. The idea behind your code, it seems to me, is to go through all the values in m and remove those that are divisible by any number between 2 and 2000000. Once you've done that, m will contain only prime numbers.
Of course, your code still won't work if you remove all. That's because you're actually testing whether each number in m is divisible by the numbers [1, 3, 5, 7...1999999]. (That's the sequence signified by xrange(1, 2000000, 2). Because you start at 1, and everything is divisible by 1, your code will count nothing as prime. And then, once you remove 1 from that sequence, anything divisible by 2 will be counted as prime by your code! You should think more carefully about which numbers you actually have to test in your inner loop.
Finally, you should think about how many loops this code will complete. Even once you have this working, it will take a very long time to generate a result. You should test it on smaller numbers first; and then, you should think about how to reduce the number of loops. (And -- only after you've thought about it a bit -- read this.)
But once you have this working, all you have to do is call sum on your list of primes.
Your use of all is incorrect, if you look at the documentation for it, it takes an iterable.
What you may be trying to do is use a generator expression, something of the form:
sum(x**2 for x in range(10))
which is very similar to the list comprehension
[x**2 for x in range(10)]
However, use of all in this manner wouldn't suddenly stop the generator expression, if it found a divisor. Use of any and checking for x == 0 would stop sooner, but as the code is currently formatted, would check for many divisors before deeming something prime.
This would be more appropriate:
primes = []
MAX = 2000000
number = 2
while number < MAX:
for prime in primes:
if number % prime == 0:
number += 1
continue
primes.append(number)
number += 1
total = sum(primes)
all() takes an iterable for an argument. In your situation, you would use it like this:
all(x%y for y in m)
where x%y for y in m is a generator expression.
If I have an iterable
[item1, item2, item3, item4...]
all(iterable) is equivalent to:
item1 and item2 and item3 and item4...'

List comprehension python

What is the equivalent list comprehension in python of the following Common Lisp code:
(loop for x = input then (if (evenp x)
(/ x 2)
(+1 (* 3 x)))
collect x
until (= x 1))
A list comprehension is used to take an existing sequence and perform some function and/or filter to it, resulting in a new list. So, in this case a list comprehension is not appropriate since you don't have a starting sequence. An example with a while loop:
numbers = []
x=input()
while x != 1:
numbers.append(x)
if x % 2 == 0: x /= 2
else: x = 3 * x + 1
I believe you are writing the hailstone sequence, although I could be wrong since I am not fluent in Lisp.
As far as I know, you can't do this in only a list comprehension, since each element depends on the last.
How I would do it would be this
def hailstone(n):
yield n
while n!=1
if n%2 == 0: # even
n = n / 2
else: # odd
n = 3 * n + 1
yield n
list = [ x for x in hailstone(input) ]
Of course, input would hold whatever your input was.
My hailstone function could probably be more concise. My goal was clarity.
Python doesn't have this kind of control structure built in, but you can generalize this into a function like this:
def unfold(evolve, initial, until):
state = initial
yield state
while not until(state):
state = evolve(state)
yield state
After this your expression can be written as:
def is_even(n): return not n % 2
unfold(lambda x: x/2 if is_even(x) else 3*x + 1,
initial=input, until=lambda x: x == 1)
But the Pythonic way to do it is using a generator function:
def produce(x):
yield x
while x != 1:
x = x / 2 if is_even(x) else 3*x + 1
yield x
The hackery referred to by Laurence:
You can do it in one list comprehension, it just ends up being AWFUL python. Unreadable python. Terrible python. I only present the following as a curiosity, not as an actual answer. Don't do this in code you actually want to use, only if you fancy having a play with the inner workings on python.
So, 3 approaches:
Helping List 1
1: Using a helping list, answer ends up in the helping list. This appends values to the list being iterated over until you've reached the value you want to stop at.
A = [10]
print [None if A[-1] == 1
else A.append(A[-1]/2) if (A[-1]%2==0)
else A.append(3*A[-1]+1)
for i in A]
print A
result:
[None, None, None, None, None, None, None]
[10, 5, 16, 8, 4, 2, 1]
Helping List 2
2: Using a helping list, but with the result being the output of the list comprehension. This mostly relies on list.append(...) returning None, not None evaluating as True and True being considered 1 for the purposes of arithmetic. Sigh.
A=[10]
print [A[0]*(not A.append(A[0])) if len(A) == 1
else 1 if A[-1] == 2 else (A[-1]/2)*(not A.append(A[-1]/2)) if (A[-1]%2==0)
else (3*A[-1]+1)*(not A.append(3*A[-1]+1))
for i in A]
result:
[10, 5, 16, 8, 4, 2, 1]
Referencing the List Comprehension from within
3: Not using a helping list, but referring back to the list comprehension as it's being built. This is a bit fragile, and probably wont work in all environments. If it doesn't work, try running the code on its own:
from itertools import chain, takewhile
initialValue = 10
print [i if len(locals()['_[1]']) == 0
else (locals()['_[1]'][-1]/2) if (locals()['_[1]'][-1]%2==0)
else (3*locals()['_[1]'][-1]+1)
for i in takewhile(lambda x:x>1, chain([initialValue],locals()['_[1]']))]
result:
[10, 5, 16, 8, 4, 2, 1]
So, now forget that you read this. This is dark, dark and dingy python. Evil python. And we all know python isn't evil. Python is lovely and nice. So you can't have read this, because this sort of thing can't exist. Good good.
As Kiv said, a list comprehension requires a known sequence to iterate over.
Having said that, if you had a sequence and were fixated on using a list comprehension, your solution would probably include something like this:
[not (x % 2) and (x / 2) or (3 * x + 1) for x in sequence]
Mike Cooper's answer is a better solution because it both retains the x != 1 termination, and this line doesn't read cleanly.
1
I have discovered a truly marvelous proof of this, which this margin is too narrow to contain.
In all seriousness though, I don't believe you can do this with Python list comprehensions. They have basically the same power as map and filter, so you can't break out or look at previous values without resorting to hackery.

Categories

Resources