How print("YNEOS"[(w%2)|(w<=2)::2]) works - python

I was just trying to solve this problem on Codeforces: Check whether it's posible to split a number w (1 ≤ w ≤ 100) into a sum of 2 even numbers
I solved the problem and started to look others solutions for the problem. The I found that solution:
w=int(input())
print("YNEOS"[(w%2)|(w<=2)::2])
it took half of time to solve the problem then mine. Unfortunately I am not getting how it is working and why it is faster than mine. My solution is :
n = int(input())
if (n-2)%2==0 and n > 2:
print("YES")
else:
print("NO")
I am a beginner in python. So it will be very helpful for me it i get a proper explanation.

The :: part is called slicing. The general syntax is start:stop[:step] which will make a new array or string from the original one, beginning from index start to before stop every step (default = 1). It's essentially a syntactic sugar for slice(start, stop, step) which returns a slice object. If stop is omitted then all the elements till the end of the string will be extracted
Understanding slice notation
How do I use the slice notation in Python?
https://docs.python.org/2.3/whatsnew/section-slices.html
You may want to read An Informal Introduction to Python for more information about slicing
Now (w%2)|(w<=2) calculates the start index. | is the bitwise-OR operator. w<=2 is a bool expression, but in Python "Booleans are a subtype of integers", so True and False will be converted to 1 and 0 respectively
If w is an odd number then w % 2 == 1. Regardless of the remaining part, (w%2)|(w<=2) will always be 1 in this case and the expression becomes "YNEOS"[1::2] which takes every second letter in the string starting from index 1. The result is the string NO
If w is an even number then w % 2 == 0. According to the problem description 1 ≤ w ≤ 100 the only valid even numbers belong to the range [2, 100]
If w == 2 then w <= 2 returns 1 and the output is NO like above
If w > 2 then both the sides of (w%2)|(w<=2) is 0, and "YNEOS"[0::2] takes the 1st, 3rd and 5th letters which is YES
Realizing that the expression always returns 0 or 1 we'll have an alternative more readable solution that uses direct addressing instead of slicing
w=int(input())
print(['YES', 'NO'][(w % 2) | (w <= 2)])
Btw, you have a redundant subtraction: (n-2)%2==0 is exactly the same as n % 2 == 0

Related

Python homework regarding prime numbers

My homework is simple, declare a function named printPrimeNumbersTo with a single argument named to
I created the skeleton of the code itself, however, I needed some help from the net.
GeeksforGeeks was the site where I "borrowed" a line of code, which I don't completely understand. (Site: https://www.geeksforgeeks.org/python-program-to-print-all-prime-numbers-in-an-interval/)
My code looks like this (I have comments on nearly every line, describing what I think that the line of code does):
def printPrimeNumbersTo(to):
x = 0
prime_list = [] # This was a string, however, I changed it to a list so I won't have to convert the numbers to a string every time I wanted to append it to the list
for i in range(x, to + 1): # Create a for loop, using the function range an starting the loop at number 0. Add 1 to 'to', because range excludes the end integer
if i == 0 or i == 1:
continue
else:
for j in range(2, i // 2 + 1): # <--
if i % j == 0: # If 'j' is divided by any number in the list and remainder is 0, then the number is not a prime number, which means I can break the loop
break
else:
prime_list.append(i) # Append the prime number to the list
return str(prime_list)[1:-1] # Returns '[2,3,5,7..]', I use the square brackets the get rid of the brackets themselves
print(printPrimeNumbersTo(7)) # >>> 2, 3, 5, 7
The one line I don't understand is marked with an arrow, it's the 8th line of the code.
Why am I dividing the number by 2? And then making it an integer? When I do the calculations, it works, but... where is the logic? Anybody help?
The biggest number which could possibly be an even factor of a number is the half of that number. The integer division operator // produces this number as an integer.
Because of how range works, the ending index needs to be the desired number plus one.
There are two points to note:
the code needs to be indented correctly, in Python indentation matters as it forms the code blocks.
aside from this and specifically adressing the question: the range function that you refer to requires integers otherwise it would throw an typeerror like this: 'float' object cannot be interpreted as an integer .
# this will throw an error
for i in range(1, 10.5):
print(i)
# this will work
for i in range(1, 10):
print(i)
So the reason why the line of code you queried was written like that was to ensure that the upper bound of the range was an integer.
You should also note that the // has a meaning, for example try this:
x = 5//2
print(x)
y = 5/2
print(y)
x is the integer part only (x=2)
y is the full number (y=2.5)
In terms of implementaiton, there are a number of methods that would be better (suggested here):
Print series of prime numbers in python
Dividing the number by 2 is done to reduce the number of iterations. For example, the number 12 you can divide it without a remainder by 1,2,3,4,6. Notice that there is no number bigger than (6) which is 12 / 2. And this goes on with all of the numbers.
16 ---> 1,2,8 no number bigger than its half (8)

Repeated String Match

I did Contest 52 of leetcode.com and I had trouble understanding the solution. The problem statement is:
Given two strings A and B, find the minimum number of times A has to be >repeated such that B is a substring of it. If no such solution, return -1.
For example, with A = "abcd" and B = "cdabcdab.
Return 3, because by repeating A three times (“abcdabcdabcd”), B is a >substring of it; and B is not a substring of A repeated two times
("abcdabcd").
The solution is:
def repeatedStringMatch(self, A, B):
"""
:type A: str
:type B: str
:rtype: int
"""
times = int(math.ceil(float(len(B)) / len(A)))
for i in range(2):
if B in (A * (times + i)):
return times + i
return -1
The explanation from one of the collaborators was:
A has to be repeated sufficient times such that it is at least as long as >B (or one more), hence we can conclude that the theoretical lower bound >for the answer would be length of B / length of A.
Let x be the theoretical lower bound, which is ceil(len(B)/len(A)).
The answer n can only be x or x + 1
I don't understand why n can only be x or x+1, can someone help?
If x+1 < n and B is a substring of A repeated n times and you've embedded B in it then either you can chop off the last copy of A without hitting B (meaning that n is not minimal) or else the start of B in A is after the end of the first copy so you can chop off the first copy (and again n is not minimal).
Therefore if it fits at all, it must fit within x+1 copies. Based on length alone it can't fit within < x copies. So the only possibilities left are x and x+1 copies. (And examples can be found where each is the answer.)
Suppose we have two strings m = "abcde" and any 11 character string "e_abcde_abcde", n to be searched. Since the 11 digits are to be matched, the minimum number of characters required is 11 which requires ((n/m)+1)=3 minimum sets for search due to integer rounding. Now the ending position depends on the beginning position and there are at most length(m) unique starting points in each set of m elements. So we can start at each of the positions till the first set is exhausted. The last try may go to m_last = 4, hence the last element of the matched string will go to (n/m+2) set. After the first set is exhausted, the above pattern is exhausted and there is no new searches. Hence we require (n/m + 2) * length (m) for full search.
I know this is an old question still, I would like to add my 2 cents here.
I think #btilly has provided enough clarity but a picture may help further.

HackerRank "AND product"

When I submit the below code for testcases in HackerRank challenge "AND product"...
You will be given two integers A and B. You are required to compute the bitwise AND amongst all natural numbers lying between A and B, both inclusive.
Input Format:
First line of the input contains T, the number of testcases to follow.
Each testcase in a newline contains A and B separated by a single space.
from math import log
for case in range(int(raw_input())):
l, u = map(int, (raw_input()).split())
if log(l, 2) == log(u, 2) or int(log(l,2))!=int(log(l,2)):
print 0
else:
s = ""
l, u = [x for x in str(bin(l))[2:]], [x for x in str(bin(u))[2:]]
while len(u)!=len(l):
u.pop(0)
Ll = len(l)
for n in range(0, len(l)):
if u[n]==l[n]:
s+=u[n]
while len(s)!=len(l):
s+="0"
print int(s, 2)
...it passes 9 of the test cases, Shows "Runtime error" in 1 test case and shows "Wrong Answer" in the rest 10 of them.
What's wrong in this?
It would be better for you to use the Bitwise operator in Python for AND. The operator is: '&'
Try this code:
def andProduct(a, b):
j=a+1
x=a
while(j<=b):
x = x&j
j+=1
return x
For more information on Bitwise operator you can see: https://wiki.python.org/moin/BitwiseOperators
Yeah you can do this much faster.
You are doing this very straightforward, calculating all ands in a for loop.
It should actually be possible to calculate this in O(1) (I think)
But here are some optimisations:
1) abort the for loop if you get the value 0, because it will stay 0 no matter what
2)If there is a power of 2 between l and u return 0 (you don't need a loop in that case)
My Idea for O(1) would be to think about which bits change between u and l.
Because every bit that changes somewhere between u and l becomes 0 in the answer.
EDIT 1: Here is an answer in O(same leading digits) time.
https://math.stackexchange.com/questions/1073532/how-to-find-bitwise-and-of-all-numbers-for-a-given-range
EDIT 2: Here is my code, I have not tested it extensively but it seems to work. (O(log(n))
from math import log
for case in [[i+1,j+i+1] for i in range(30) for j in range(30)]:
#Get input
l, u = case
invL=2**int(log(l,2)+1)-l
invU=2**int(log(u,2)+1)-u
#Calculate pseudo bitwise xnor of input and format to binary rep
e=format((u&l | invL & invU),'010b')
lBin=format(l,'010b')
#output to zero
res=0
#boolean to check if we have found any zero
anyZero=False
#boolean to check the first one because we have leading zeros
firstOne=False
for ind,i in enumerate(e):
#for every digit
#if it is a leading one
if i=='1' and (not anyZero):
firstOne=True
#leftshift result (multiply by 2)
res=res<<1
#and add 1
res=res+int(lBin[ind])
#else if we already had a one and find a zero this happens every time
elif(firstOne):
anyZero=True
#leftshift
res=res<<1
#test if we are in the same power, if not there was a power between
if(res!=0):
#print "test",(int(log(res,2))!=int(log(l,2))) | ((log(res,2))!=int(log(u,2)))
if((int(log(res,2))!=int(log(l,2))) or (int(log(res,2))!=int(log(u,2)))):
res=0
print res
Worked for every but a single testcase. Small change needed to get the last one. You'll have to find out what that small change is yourself. Seriously

Iterating over a large list

I have a list in the range [465868129, 988379794] both inclusive. When I use the following code I get a Memory Error. What can I do?
r = [465868129, 988379794]
list = [x for x in xrange(r[0], r[1]+1)]
You could iterate over the xrange directly instead of creating a list.
for x in xrange(r[0], r[1] + 1):
...
But iterating over such a large range is a very, very slow way to find squares. The fact that you run out of memory should alert you that a different approach is needed.
A much better way would be to take the square roots of each end point and then iterate between the square roots. Each integer between the square roots, when squared, would give you one of the numbers you're searching for.
In fact, if you're clever enough, you can generate all the squares with a single list comprehension and avoid an explicit for loop entirely.
Unless you have a very good reason to store the list items in a list, iterate over the generator instead, that way Python won't need to allocate a lot of memory (causing your Memory Error) to create that list:
init, end = (465868129, 988379794)
items = xrange(init, end + 1)
for item in items:
#Do something with item
To count squares on an arbitrary range consider the following formula:
import math
number_of_squares = int(math.sqrt(end) - math.sqrt(init)) +
op(is_perfect_square(init), is_perfect_square(end))
The is_perfect_square(n) is another problem on its own, so check this post if interested.
The op is used to adjust the number of squares when the init and end of the intervals init (or/and/neither) end are perfect squares. So we need a function with the following characteristics:
Both numbers are perfect squares: Eg: 25,64 => 8 - 5 = 3 (and there are 4 squares on that range). (it should sum 1 more)
End is a perfect square: Eg: 26,64 => 8 - 5 = 3 (There are 3 squares on that range). (it is correct => it should sum 0)
Init is a perfect square: Eg: 25,65 => 8 - 5 = 3 (There are 4 squares on that range). (it should sum 1 more)
None of the numbers are primes: Eg: 26, 65 => 8 - 5 = 3 (There are 3 squares on that range) (it is correct => it should sum 0)
So we need an operator with the following characteristics, based on the past examples:
1 op 1 = 1 (Both numbers are perfect squares)
0 op 1 = 0 (End is a perfect square)
1 op 0 = 1 (Init is a perfect square)
0 op 0 = 0 (None of the numbers are perfect squares)
Note that the max function almost fulfils our needs, but it fails on the second case max(0,1) = 1 and it should be 0.
So, looks like the result only depends on the first operator: if it's one, the result is 1, on the other hand if it's 0, it returns 0.
So, it's easy to write the function with that in mind:
import math
number_of_squares = int(math.sqrt(end) - math.sqrt(init)) +
int(is_perfect_square(init))
Thanks to #kojiro, we have this approach (having a similar idea), which is easier to read:
from math import sqrt, floor, ceil
number_of_squares = 1 + floor(sqrt(end)) - ceil(sqrt(init))

Debugging IndexError: Trivial sum generation problem

I can't wrap my head around what's causing the index error here, not exactly looking for a quick fix. Let me know however if my code repulses you/is incredibly ineffectual. The goal is to generate palindromes produced by the product of two four digit numbers.
Code:
for x in range(10000):
for y in range(10000):
product = str(x*y)
lengthprod = len(str(product))
for digit in range(lengthprod+1):
if (product[digit]==product[lengthprod-digit]):
print x,"x",y,"=",product
Traceback:
Traceback (most recent call last):
File "<pyshell#31>", line 6, in <module>
if (product[digit]==product[lengthprod-digit]):
IndexError: string index out of range
Converting a number to a string is generally a slow operation, since there are many possibilities in general (integers, floating point, scientific notation, maybe something exotic like fractions or imaginary numbers, not to mention things like handling leading zero or overwidth numbers or rounding to two decimal places). Thus, it is often a better approach for checking if a positive integer is a palindrome to reverse the digits numerically by repeatedly taking the input modulo 10 to extract the last digit, adding the digit to an accumulator that is multiplied by 10 at each step, then dividing the input number by 10 before looping. I don't speak Python, so here is my Scheme program to reverse a number:
(define (rev n)
(let loop ((n n) (r 0))
(if (zero? n) r
(loop (quotient n 10)
(+ (* r 10) (modulo n 10))))))
Then you can check if the number is a palindrome by checking if the input number equals its reversal.
Edit: Here it is in Python:
def rev(n):
r = 0
while n > 0:
r = r * 10 + n % 10
n = n // 10
return r
You iterate over values 0...lengthprod , but the legal subscripts for product are 0...lengthprod-1.
The last index is out of bounds. It references an element that is one byte beyond the end of the string.
Two changes:
1: range(0, lengthprod+1) should be range(0, lengthprod)
See documentation on range()
2: product[lengthprod-digit] should be product[lengthprod-digit-1]
Off by one error since lengthprod is a length (1 based) and digit is an index (0 based).
Note, this will only give you valid "single digit" palindromes, but gets you past the index out of range error.
Your code repulses me!
(Sorry, I wouldn't normally be so rude, but since you asked for it... ;)
Use xrange rather than range for long loops like this.
Start the range from 1 rather than 0 unless you don't mind all the duplicated trivial results.
Since multiplication commutes, you might want to loop over a "triangle" rather than a "square" to avoid duplicates.
Your variable name product shadows a function from numeric core.
The question says you're interested in the "product of two four digit numbers", but your code has no such restrictions on the number of digits in the numbers. If you want the four digit numbers as input, just start your xrange(start, stop) from 1000.
Since your stated "goal is to generate palindromes", how about to try it with the correct tool for the job: generators!
def pairs(n):
for x in xrange(n):
for y in xrange(n):
yield (x,y)
pairs_generator = pairs(100)
filter(None, ['{x}*{y}={xy}'.format(x=x,y=y,xy=x*y) if str(x*y) == str(x*y)[::-1] else None for x,y in pairs_generator])
I kept my generator simple for clarity's purpose. I will leave it as an exercise for you to simply make a generator to spit out the palindromes. This will involve moving the logic which I have put in my list comprehension into the generator (or you could make a new palindrome_generator which uses a pairs_generator).
Have fun!

Categories