Python program Query - python

This is a question from John Guttag's book Introduction to Computation and Programming Using Python
Write a program that prints out the greatest odd number out of three numbers and an alternate error message if none of the numbers are odd.
This is my program. Is it correct? Does it cover all possibilities? I tried thinking it this way - exhaust all possibilities for the 3 statements - x is the largest, y is the largest and z is the largest - one by one. Is it the correct approach? Anyway, this is my program.
if ((x>y and x>z) or (x>y and x<z and z%2==0) or (x>z and x<y and
y%2==0) or (x<z and x<y and y%2==0 and z%2==0)) and x%2!=0:
print(x)
elif (y>z and y%2!=0) or (y<z and z%2==0)
print(y)
elif z%2!=0:
print(z)
else:
print('None of the numbers are odd')

Three simple steps:
Filter the list
Check if empty
Get max
filtered_list = filter(lambda x:x%2!=0, [x, y, z])
if filtered_list:
print max(filtered_list)
else:
print "No Odd number given"

It's almost correct
You are very close but you've forgotten a condition for when x is not printed and y and z are both even. Try the example of (4,2,6) for (x,y,z). Your exhaustive reasoning makes sense, but it is hard to program like this. I want to walk through your current logic then show how your code can be simplified, but your approach is clear using only if-else and indentation.
Your logic
In your logic you check all possibilities where you would ever print x which is if any following are true so long as x is odd:
x is the largest of x, y, and z
x is bigger than y when z is even
x is bigger than z when y is even
y and z are even
Then in the first elif you test whether to print y and make the correct decision to no longer check x at all since the first if handled all the cases where you would want to print x.
This is where your mistake is, however, since you should keep the same logical structure as your if statement for x:
elif ((y>z) or (y<z and z%2==0)) and y%2!=0:
You do the same for z in the last elif and then use else if all are even.
Simplifying your logic
We can simplify the first if statement where you check for x. One small note is that it is better to check that x is odd before doing all the or checks, since if x is even the computer doesn't have to look at all the or logic (short-circuiting):
if x%2 != 0 and ((x>y and x>z) or (x>y and x<z and z%2==0) or (x>z and x<y and
y%2==0) or (x<z and x<y and y%2==0 and z%2==0)):
Next the or logic can be simplified to:
if x%2 != 0 and ((x>=y and x>=z) or (x>=y and z%2==0) or (x>=z and
y%2==0) or (y%2==0 and z%2==0)):
All the simplifications came from the idea that if y or z is even we don't need to compare it to x since we'd want to print x regardless. I've also changed the > to >= or you would have had errors if some of the variables had the same values.
With the same reasoning we can simplify the first elif to:
elif ((y>=z) or (z%2==0)) and y%2!=0:
Bonus
If we know x, y, and z are not negative you can do this little trick. Try to work it out, leave a comment if you have questions or figure out how to make it work with negatives.
x = x*(x%2)
y = y*(y%2)
z = z*(z%2)
if x+y+z == 0:
print('None of the numbers are odd')
elif x >= y and x >= z:
print(x)
elif y >= x and y >= z:
print(y)
elif z >= x and z >= y:
print(z)

This is the Efficient code for finding whether the given number is odd or not.
Here I used '&' (and bitwise operator) which do bitwise and with one .
let the number 5(00000101)
when we do 5 & 1 then it will do (00000101) & (00000001) = (00000001).
This will happen with all other odd numbers.
ip = []
ip = map(int,raw_input().split())
odds = filter(lambda x : x & 1 != 0 , ip)
if len(odds) > 0 :
print (max(odds))
else :
print ("None of the numbers are odd")

Related

Python: "If" function

This is probably really simple but I can't figure it out.
I have a bunch of lists and I want to call certain lists if they are in the range equal to a value x and any number between x-5 and x +5. i.e. x-5,x-4,x-3,x-2,x-1,x,x+1,x+2,x+3,x+4 and x+5.
At the moment I have
if sum(listname[i])==x:
if sum(listname[i])==x-1:
if sum(listname[i])==x-2:
etc
How can I do it so that it is combined in one "if" function.
I've been thinking on the lines of something like:
if sum(listname[i])==x-5>=x>=x+5:
or
if sum(listname[i])==x or x-1 or x-2 ".. etc":
but neither work.
Can anybody shine some light on this?
A scenario like if sum(listname[i])==x or x-1 or x-2 ".. etc": (which is not valid python) is usually solved with if value in range(start, stop, step):
So you would write:
if sum(listname[i) in range(x-2, x):
# Code for this case here...
Do you simply mean
if x-5 <= sum(listname[i]) <= x+5:
...
...
It looks like you want to check if the sum of the list is between x - 5 and x + 5. To put it in one if statement is simply:
s = sum(listname[i])
if s >= x - 5 and s <= x + 5:
# do stuff
From what I understand from your question. You what to check that whether sum(listname[i]) is between (x-5, x+5)
You can do this in a single if assuming x is a possitive value:
if (sum(listname[i]) >= (x - 5)) and (sum(listname[i]) <= (x+5))

min() arg is an empty sequence

I'm trying to find minimum element in matrix row, but there are two conditions:
1) it must be > 0
2) and this point must be not visited(is_visited[k] is False)
I'm trying to do next:
min(x for x in matr_sum[i] if x > 0 if is_visited[k] is False )
But there is an error: min() arg is an empty sequence
The full block of code:
for k in range(4):
if matr_sum[i][k] == min(x for x in matr_sum[i] if x > 0 if is_visited[k] is False ) and i!=k:
return k
How to resolve it? Or should I write my min() function? Because it works with one condition:
min(x for x in matr_sum[i] if x > 0)
But with two conditions, it doesn't work.
If you want to avoid this ValueError in general, you can set a default argument to min(), that will be returned in case of an empty list. See described here.
min([], default="EMPTY")
# returns EMPTY
Note that this only works in Python 3.4+
There is no problem with the syntax. It's certainly unusual to have two if clauses, but it's allowed. Consider:
print(min(x for x in range(1,300) if x % 3 == 0 if x % 5 == 0))
Output:
15
However:
print(min(x for x in range(1,300) if x % 2 != 0 if x % 2 != 1))
Output:
ValueError: min() arg is an empty sequence
There are no integers that are both odd and even, so there are no values for min to see, so it throws an exception.
I deduce that in your code, there are no values that pass both conditions. Python doesn't allow you to compute "the minimum of no values", mainly because it makes no sense.
You have to decide what you want to do in the case where there is no minimum because there are no values greater than 0. For example, if you don't want to return k in that case then I might re-write your code something like this:
for k in range(4):
if k != i and is_visited[k] is False:
if matr_sum[i][k] > 0 and matr_sum[i][k] == min(x for x in matr_sum[i] if x > 0):
return k
It might not be obvious why this helps, but assuming matr_sum[i] is a list or similar, then once we know matr_sum[i][k] > 0 then we know the generator passed to min isn't empty, so we won't get an exception. Whereas if matr_sum[i][k] <= 0, then it certainly isn't equal to the smallest positive value, so there's no need to compute the min at all. Another way to write that would be:
if matr_sum[i][k] > 0 and not any(0 < x < matr_sum[i][k] for x in matr_sum[i])
Actually, I'd normally write if not is_visited[k], but I leave it as is False since I don't know whether changing it would change the behaviour of your code.
Try this - it creates the list of x values xs and then only tries to find the min if xs is non-empty. You may need to add some logic to handle the case that xs is empty, depending on what your code is doing.
for k in range(4):
if is_visited[k] is False and i != k:
xs = [x for x in matr_sum[i] if x > 0]
if xs and matr_sum[i][k] == min(xs):
return k
Just use and operation for concatenate tow if statement :
min(x for x in matr_sum[i] if x > 0 and if is_visited[k] is False and i!=k)

Breaking a number to primes - Why does this code never stop?

def breaktoprimes(x):
primefactors = []
if x==1:
primefactors.append(1)
y=1
while y<=x or x!=1:
if(x%y==0):
primefactors.append(y)
x=x/y
y=1
else:
y=y+1
return primefactors
So basically what i am trying to do with this, is to fill the list "primefactors" with the prime factors of x. However, no matter what i enter, the code just runs and runs, and after some time, returns with this error:
Traceback (most recent call last):
File "<pyshell#36>", line 1, in <module>
breaktoprimes(15)
File "<pyshell#35>", line 8, in breaktoprimes
primefactors.append(y)
MemoryError
Basically what i intended the code to do is:
While y is smaller or equal to x and while x is not equal to one, which it should always apply to unless you enter a smaller number than 1 or x got divided by itself, in either case we are finished, we go on
test if the division of x by y results in the remainder of 0. If it does, y is sure to be a prime factor of x since y is not bigger than x and, y can't be a multiplication of a prime since we have tested the smaller numbers already.
If that didin't work, we add 1 to the value of y and try again
I have searched the web, but the solutions to this problem i have found are very different, so i haven't found any help in them. I have no idea where the problem in this code lies. I am a beginner in python with c# background and have coded for about a year now.
Thanks for reading!
All numbers are divisible by 1. Choosing 1 as an initival value for y is not good choice.
Reset y to 1 should be gone (same reason for 1)
def breaktoprimes(x):
primefactors = []
if x == 1:
primefactors.append(1)
y = 2
while y <= x or x != 1:
if x % y == 0:
primefactors.append(y)
x = x / y
else:
y = y + 1
return primefactors
If x=1 and y=1 then y<=x or x!=1 is true, and x=x/y sets x to 1, so both x and y will continue to be 1 and the loop will run forever.
In this part of the code:
y=1
while y<=x or x!=1:
if(x%y==0):
primefactors.append(y)
x=x/y
y=1
x%y is 0. Then y becomes 1 again. So the loop continues.
The problem is your x and y values are always remaining the same. So due to this your code runs in infinite loop.

Triangle side sum program with input from user (Think Python exercise 5-4-2)

I'm trying to solve Think Python's exercise 5-4-2, which asks to write a program that will prompt the user to input the size of three sticks in order to calculate if they can make up a triangle. The rule is that if any of the three length of the sticks is greater than the sum of the other two, the triangle cannot be made.
Here is my code. The problem is that it always return 'no triangle' (I tried with correct values such as 4, 5 and 3, and with values that should return wrong such as 1,2, and 12), and it always indicates "None" (as if I wasn't using "return"):
def is_triangle(x,y,z):
if (x>y+z) or (y>z+x) or (z>x+y):
print 'no triangle'
else:
print 'triangle yes'
return
prompt1 = 'Input firt stick length for your triangle please...'
x = raw_input(prompt1)
int(x)
prompt2 = 'Input second stick length for your triangle please...'
y = raw_input(prompt2)
int(y)
prompt3 = 'Input third stick length for your triangle please...'
z = raw_input(prompt3)
int(z)
print is_triangle(x,y,z)
Thanks in advance for your help!... Norpa
The issue here is that you're not reassigning your int casts back to x, y and z. Thus, they are treated as strings, and in your last comparison, you end up with (x + y) = 34 (as a string), and the comparison becomes True.
So all the prompt sections should be like this:
prompt1 = 'Input firt stick length for your triangle please...'
x = raw_input(prompt1)
x = int(x)
Or:
prompt1 = 'Input firt stick length for your triangle please...'
x = int(raw_input(prompt1))
You are not casting x y and z to integers properly, thus comparing the input strings with >.
Change
int(x)
to
x = int(x)
and do the same for y and z.
The issue here is that you forgot to assign the return value of int(x) to the variable x, thus int(x) has no meaningful effect.
While structuring the function you can set it out to be the integer.
def is_triangle(a,b,c):
if (int(a)>int(b+c)) or (int(b)>int(c+a)) or (int(c)>int(a+b)):
print("No")
else:
print("Yes")
This is considering the scope limited to your exercise. You can take it to little advanced level by making it dynamic to restrict the user input to be only the integer(in the prompt). Hope you can do that once you move to advance user. Good Luck!

Simplify Python iterations

Everytime I try to solve some math problem such as finding a specific product of certain number of factors I do this in Python
for x in xrange(1,10):
for y in xrange(1,10):
for z in xrange(1,10):
product = x * y * z
if product == 36:
print "factors : {0},{1},{2}".format(x,y,z)
It is very straightforward and gets the job done fast in this example, but I was wondering if you guys know an easier or simpler way to write this. Any ideas on how to do this without using that many for iterations or repeating almost the same code over and over. These is obviously for 3 factors, but the more factors I add the longer and more repetitive the code keeps getting. Any ideas on how to simplify code for this simple type of problem?
thanks
Itertool's cartesian product simulates the effect of multiple nested for loops.
import itertools
for x, y, z in itertools.product(range(1,10), range(1,10), range(1,10)):
product = x * y * z
if product == 36:
print "factors : {0},{1},{2}".format(x,y,z)
Result:
factors : 1,4,9
factors : 1,6,6
factors : 1,9,4
(...etc)
If the range is always the same for each of x,y, and z, you can specify it just once:
for x, y, z in itertools.product(range(1,10), repeat=3):
If you're sick of typing a zillion asterisks for the product = line, you can use reduce to multiply together an arbitrary number of arguments:
for factors in itertools.product(range(1,3), repeat=10):
product = reduce(lambda x, y: x*y, factors)
Once your format string becomes unwieldy, you can depend on join to string together factors:
if product == 512:
#use `map` to turn the factors into strings, first
print "factors: " + ",".join(map(str, factors))
Avoid duplicates by having y start at x. Calculate z instead of running another loop.
for x in xrange(1,10):
for y in xrange(x,10):
z, r = divmod(36, x*y)
if r == 0:
print "factors : {0},{1},{2}".format(x,y,z)
For more factors, I would use a recursive function.

Categories

Resources