Why does order not matter when adding binary numbers python - python

Came across a pretty neat solution on adding binary numbers
class Solution:
def addBinary(self, a: str, b: str) -> str:
carry = 0
result = ''
a = list(a)
b = list(b)
while a or b or carry:
if a:
carry += int(a.pop())
if b:
carry += int(b.pop())
result += str(carry %2)
carry //= 2
return result[::-1]
For me it is intuitive to read numbers from the right to left, in which case the 1 that is carried over(1+1=10) will be from right to left as well. So why is it that reading it left to right and reversing it is the same, even when say length of strings a and b are different?
[EDIT] misunderstand and assumed why he did result[::-1] Thanks for the replies!

Your algorithm is not reading the strings left to right. It is reading them right to left.
You are reading the digits with list.pop(). See the documentation: https://docs.python.org/3/tutorial/datastructures.html
list.pop([i]) Remove the item at the given position in the list, and return it. If
no index is specified, a.pop() removes and returns the last item in
the list. (The square brackets around the i in the method signature denote that the parameter is optional, not that you should type square brackets at that position. You will see this notation frequently in the Python Library Reference.)
However, the resulting digits are added right to left to the string result, because of result += str(carry %2). This is why result needs to be reversed at the end.
If you replace result += str(carry %2) with result = str(carry %2) + result and return result[::-1] with return result, you will see that the result is in the correct order.

Sure, the code could have collected the digits from "left to right", like this:
result = str(carry %2) + result
... and then omit the reversal at the end. It would not make a difference for the output. Possibly the author considered that doing it with += would give a performance gain, even when taking the reversal operation into account. In my tests I can confirm this performance gain.
Note that either way, the input values are read from right to left, as the digits are taken from both lists with pop(). It is only the placement of the resulting digits which is reversed.

Related

Rotating an array with slices and tuple assignment

I am trying to solve the LeetCode problem 189. Rotate Array in Python:
Given an array, rotate the array to the right by k steps, where k is non-negative.
class Solution:
def rotate(self, nums: List[int], k: int) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
The following code works:
n = len(nums)
k = k % n
nums[k:], nums[:k] = nums[:-k], nums[-k:]
But if I change the order of the assignments in the last line, i.e. replace it with
nums[:k], nums[k:] = nums[-k:], nums[:-k]
the code doesn't work for the case of k=0. Clearly in this case, nums[:k] and nums[:-k] would be empty and the sizes of the lists on the left hand side and the right hand side don't match, but somehow the first code works and the second one doesn't.
Can someone explain?
The order in which the tuple assignment is executed, is indeed relevant here.
Let's say nums is [1,2,3,4] (and k 0)
Then the non-working assignment comes down to this:
nums[:0], nums[0:] = [1,2,3,4], []
...which is executed in this order:
# First the right hand side expressions are evaluated
complete = [1,2,3,4]
empty = []
# Then these values are assigned to the left hand side
nums[:0] = complete
nums[0:] = empty
Note that the second phase has no magical simultaneous assignment: these assignments happen in a left-to-right order.
It is now clear that the latter assignment destroys the effect of the first. It is also clear if they were executed in the opposite order, the end result would be fine.
So the order of assignments matter, even in tuple assignments.
The deeper reason why the behaviour is different specifically when k is 0, is related to how -k works in a slice notation: this always counts abs(k) steps backwards from the end, except when k=0: then it represents the very first index.
You would not have this different behaviour if you would do this:
nums[:k], nums[k:] = nums[len(nums)-k:], nums[:len(nums)-k]
Now it is explicit that the index should be determined by stepping backwards from the end, even when k is 0. This tuple assignment can be swapped, and it will still work, also for k equal to 0.
Well, consider this:
nums = [1, 2, 3]
nums[:0], nums[0:] = [1, 2, 3], []
print(nums)
Given the next to last line, what would you expect nums to be? The problem is that the statement is ambiguous to begin with and it just happens to be that the second implementation gets you an undesirable result - it resets the entire list to [] after adding [1,2,3] to the start. The first implementation does it the other way around.
What you were really after:
nums = nums[k:] + nums[:k]

Given 2 strings, return number of positions where the two strings contain the same length 2 substring

here is my code:
def string_match(a, b):
count = 0
if len(a) < 2 or len(b) < 2:
return 0
for i in range(len(a)):
if a[i:i+2] == b[i:i+2]:
count = count + 1
return count
And here are the results:
Correct me if I am wrong but, I see that it didn't work probably because the two string lengths are the same. If I were to change the for loop statement to:
for i in range(len(a)-1):
then it would work for all cases provided. But can someone explain to me why adding the -1 makes it work? Perhaps I'm comprehending how the for loop works in this case. And can someone tell me a more optimal way to write this because this is probably really bad code. Thank you!
But can someone explain to me why adding the -1 makes it work?
Observe:
test = 'food'
i = len(test) - 1
test[i:i+2] # produces 'd'
Using len(a) as your bound means that len(a) - 1 will be used as an i value, and therefore a slice is taken at the end of a that would extend past the end. In Python, such slices succeed, but produce fewer characters.
String slicing can return strings that are shorter than requested. In your first failing example that checks "abc" against "abc", in the third iteration of the for loop, both a[i:i+2] and b[i:i+2] are equal to "c", and therefore count is incremented.
Using range(len(a)-1) ensures that your loop stops before it gets to a slice that would be just one letter long.
Since the strings may be of different lengths, you want to iterate only up to the end of the shortest one. In addition, you're accessing i+2, so you only want i to iterate up to the index before the last item (otherwise you might get a false positive at the end of the string by going off the end and getting a single-character string).
def string_match(a: str, b: str) -> int:
return len([
a[i:i+2]
for i in range(min(len(a), len(b)) - 1)
if a[i:i+2] == b[i:i+2]
])
(You could also do this counting with a sum, but this makes it easy to get the actual matches as well!)
You can use this :
def string_match(a, b):
if len(a) < 2 or len(b) < 0:
return 0
subs = [a[i:i+2] for i in range(len(a)-1)]
occurence = list(map(lambda x: x in b, subs))
return occurence.count(True)

how to make an imputed string to a list, change it to a palindrome(if it isn't already) and reverse it as a string back

A string is palindrome if it reads the same forward and backward. Given a string that contains only lower case English alphabets, you are required to create a new palindrome string from the given string following the rules gives below:
1. You can reduce (but not increase) any character in a string by one; for example you can reduce the character h to g but not from g to h
2. In order to achieve your goal, if you have to then you can reduce a character of a string repeatedly until it becomes the letter a; but once it becomes a, you cannot reduce it any further.
Each reduction operation is counted as one. So you need to count as well how many reductions you make. Write a Python program that reads a string from a user input (using raw_input statement), creates a palindrome string from the given string with the minimum possible number of operations and then prints the palindrome string created and the number of operations needed to create the new palindrome string.
I tried to convert the string to a list first, then modify the list so that should any string be given, if its not a palindrome, it automatically edits it to a palindrome and then prints the result.after modifying the list, convert it back to a string.
c=raw_input("enter a string ")
x=list(c)
y = ""
i = 0
j = len(x)-1
a = 0
while i < j:
if x[i] < x[j]:
a += ord(x[j]) - ord(x[i])
x[j] = x[i]
print x
else:
a += ord(x[i]) - ord(x[j])
x [i] = x[j]
print x
i = i + 1
j = (len(x)-1)-1
print "The number of operations is ",a print "The palindrome created is",( ''.join(x) )
Am i approaching it the right way or is there something I'm not adding up?
Since only reduction is allowed, it is clear that the number of reductions for each pair will be the difference between them. For example, consider the string 'abcd'.
Here the pairs to check are (a,d) and (b,c).
Now difference between 'a' and 'd' is 3, which is obtained by (ord('d')-ord('a')).
I am using absolute value to avoid checking which alphabet has higher ASCII value.
I hope this approach will help.
s=input()
l=len(s)
count=0
m=0
n=l-1
while m<n:
count+=abs(ord(s[m])-ord(s[n]))
m+=1
n-=1
print(count)
This is a common "homework" or competition question. The basic concept here is that you have to find a way to get to minimum values with as few reduction operations as possible. The trick here is to utilize string manipulation to keep that number low. For this particular problem, there are two very simple things to remember: 1) you have to split the string, and 2) you have to apply a bit of symmetry.
First, split the string in half. The following function should do it.
def split_string_to_halves(string):
half, rem = divmod(len(string), 2)
a, b, c = '', '', ''
a, b = string[:half], string[half:]
if rem > 0:
b, c = string[half + 1:], string[rem + 1]
return (a, b, c)
The above should recreate the string if you do a + c + b. Next is you have to convert a and b to lists and map the ord function on each half. Leave the remainder alone, if any.
def convert_to_ord_list(string):
return map(ord, list(string))
Since you just have to do a one-way operation (only reduction, no need for addition), you can assume that for each pair of elements in the two converted lists, the higher value less the lower value is the number of operations needed. Easier shown than said:
def convert_to_palindrome(string):
halfone, halftwo, rem = split_string_to_halves(string)
if halfone == halftwo[::-1]:
return halfone + halftwo + rem, 0
halftwo = halftwo[::-1]
zipped = zip(convert_to_ord_list(halfone), convert_to_ord_list(halftwo))
counter = sum([max(x) - min(x) for x in zipped])
floors = [min(x) for x in zipped]
res = "".join(map(chr, floors))
res += rem + res[::-1]
return res, counter
Finally, some tests:
target = 'ideal'
print convert_to_palindrome(target) # ('iaeai', 6)
target = 'euler'
print convert_to_palindrome(target) # ('eelee', 29)
target = 'ohmygodthisisinsane'
print convert_to_palindrome(target) # ('ehasgidihmhidigsahe', 84)
I'm not sure if this is optimized nor if I covered all bases. But I think this pretty much covers the general concept of the approach needed. Compared to your code, this is clearer and actually works (yours does not). Good luck and let us know how this works for you.

using recursion to find the maximum in a list

I'm trying to find the maximum element in a list using recursion.
the input needs to be the actual list, the left index, and the right index.
I have written a function and can't understand why it won't work. I drew the recursion tree, ran examples of lists in my head, and it makes sense , that's why it's even harder to find a solution now ! (it's fighting myself basically).
I know it's ugly, but try to ignore that. my idea is to split the list in half at each recursive call (it's required), and while the left index will remain 0, the right will be the length of the new halved list, minus 1.
the first call to the function will be from the tail function.
thanks for any help, and I hope I'm not missing something really stupid, or even worse- not even close !
by the way- didn't use slicing to cut list because I'm not allowed.
def max22(L,left,right):
if len(L)==1:
return L[0]
a = max22([L[i] for i in range(left, (left+right)//2)], 0 , len([L[i] for i in range(left, (left+right)//2)])-1)
b = max22([L[i] for i in range(((left+right)//2)+1, right)], 0 ,len([L[i] for i in range(left, (left+right)//2)])-1)
return max(a,b)
def max_list22(L):
return max22(L,0,len(L)-1)
input example -
for max_list22([1,20,3]) the output will be 20.
First off, for the sake of clarity I suggest assigning your list comprehensions to variables so you don't have to write each one twice. This should make the code easier to debug. You can also do the same for the (left+right)//2 value.
def max22(L,left,right):
if len(L)==1:
return L[0]
mid = (left+right)//2
left_L = [L[i] for i in range(left, mid)]
right_L = [L[i] for i in range(mid+1, right)]
a = max22(left_L, 0 , len(left_L)-1)
b = max22(right_L, 0 , len(left_L)-1)
return max(a,b)
def max_list22(L):
return max22(L,0,len(L)-1)
print max_list22([4,8,15,16,23,42])
I see four problems with this code.
On your b = line, the second argument is using len(left_L) instead of len(right_L).
You're missing an element between left_L and right_L. You should not be adding one to mid in the right_L list comprehension.
You're missing the last element of the list. You should be going up to right+1 in right_L, not just right.
Your mid value is off by one in the case of even sized lists. Ex. [1,2,3,4] should split into [1,2] and [3,4], but with your mid value you're getting [1] and [2,3,4]. (assuming you've already fixed the missing element problems in the previous bullet points).
Fixing these problems looks like:
def max22(L,left,right):
if len(L)==1:
return L[0]
mid = (left+right+1)//2
left_L = [L[i] for i in range(left, mid)]
right_L = [L[i] for i in range(mid, right+1)]
a = max22(left_L, 0 , len(left_L)-1)
b = max22(right_L, 0 , len(right_L)-1)
return max(a,b)
def max_list22(L):
return max22(L,0,len(L)-1)
print max_list22([4,8,15,16,23,42])
And if you insist on not using temporary variables, it looks like:
def max22(L,left,right):
if len(L)==1:
return L[0]
a = max22([L[i] for i in range(left, (left+right+1)//2)], 0 , len([L[i] for i in range(left, (left+right+1)//2)])-1)
b = max22([L[i] for i in range((left+right+1)//2, right+1)], 0 , len([L[i] for i in range((left+right+1)//2, right+1)])-1)
return max(a,b)
def max_list22(L):
return max22(L,0,len(L)-1)
print max_list22([4,8,15,16,23,42])
Bonus style tip: you don't necessarily need three arguments for max22, since left is always zero and right is always the length of the list minus one.
def max22(L):
if len(L)==1:
return L[0]
mid = (len(L))//2
left_L = [L[i] for i in range(0, mid)]
right_L = [L[i] for i in range(mid, len(L))]
a = max22(left_L)
b = max22(right_L)
return max(a,b)
print max22([4,8,15,16,23,42])
The problem is that you aren't handling empty lists at all. max_list22([]) recurses infinitely, and [L[i] for i in range(((left+right)//2)+1, right)] eventually produces an empty list.
Your problem is that you don't handle uneven splits. Lists could become empty using your code, but you can also stop on sizes 1 and 2 instead of 0 and 1 whichi s more natural (because you return a max, zero size lists don't have a max).
def max22(L,left,right):
if left == right:
# handle size 1
return L[left]
if left + 1 == right:
# handle size 2
return max(L[left], L[right])
# split the lists (could be uneven lists!)
split_index = (left + right) / 2
# solve two easier problems
return max (max22(L, left, split_index), max22(L, split_index, right))
print max22([1,20, 3], 0, 2)
Notes:
Lose the list comprehension, you don't have to create new lists since you have indices within the list.
When dealing with recursion, you have to think of:
1 - the stop condition(s), in this case there are two because list splits can be uneven, making the recursion stop at uneven conditions.
2 - the easier problem step . Assuming I can solve an easier problem, how can I solve this problem? This is usually what's in the end of the recursive function. In this case a call to the same function on two smaller (index-wise) lists. Recursion looks a lot like proof by induction if you're familiar with it.
Python prefers things to be done explicitly. While Python has some functional features it's best to let readers of the code know what you're up to ratehr than having a big one-liner that makes people scratch their head.
Good luck!

Printing from 2 lists on one line

What I have so far:
def balance_equation(species,coeff):
data=zip(coeff,species)
positive=[]
negative=[]
for (mul,el) in data:
if int(mul)<0:
negative.append((el,mul))
if int(mul)>0:
positive.append((el,mul))
I know this does not print anything. What I have is a function that takes in two lists species=['H2O','O2','CO2'] and coeff=['1','3','-4']. I need it to print like so:
1H20+3O2=4CO2
I started by putting the negative coeff and species in one list and the positive in the other. I just can seem to be able to get the two to print right.
Try this:
species = ["H2O", "CO2", "O2"]
coeff = ['1', '-4', '3']
pos = [c + s for c, s in zip(coeff, species) if int(c) > 0]
neg = [c[1:] + s for c, s in zip(coeff, species) if int(c) < 0]
print ("+".join(pos))+"="+("+".join(neg))
EDIT: I took out the spaces.
2nd EDIT: coeff is a list of strings.
You should also test if pos or neg are empty to replace them with 0s when appropriate. It appears that the coefficients are integers.
Breaking things down into steps is a good way to solve things (you can always recombine the steps later), and you've got 80% of the way there.
You already have positive and negative lists. So, you need to convert each one into a string, then just:
print poshalf, "=", neghalf
So, how do you convert positive into poshalf? Well, it's a representation of each member, separated by '+', so if you had a function stringify that could turn each member into its representation, it's just:
poshalf = '+'.join(stringify(el, mul) for (el, mul) in pos)
neghalf = '+'.join(stringify(el, mul)[1:] for (el, mul) in neg)
The [1:] there is to take out the - sign. If mul is actually an integer rather than a string, it probably makes sense to just negate the value before passing it to stringify:
neghalf = '+'.join(stringify(el, -mul) for (el, mul) in neg)
Now, what does that "stringify" function look like? Well, each one member is an (el, mul) pair. If they were both strings, you could just add them. From your previous questions, mul may end up being some kind of number at this point, but that's almost as easy:
def stringify(el, mul):
return str(mul) + el
Put it all together, and you're done.
One way to make this all simpler: If you never use the (el, mul) for any other purpose except to call stringify on it, just call stringify in the first place and store the result:
def balance_equation(species,coeff):
data=zip(coeff,species)
positive=[]
negative=[]
for (mul,el) in data:
if int(mul)<0:
negative.append(str(mul)[1:] + el)
if int(mul)>0:
positive.append(str(mul) + el)
return positive, negative
Remember that last line, which you've left off both versions of your previous question and this question! If you never return the values, all that effort figuring them out is wasted, and the caller just gets None as an answer.
Obviously either the str or the int is unnecessary, but I've left them both in for safety; you should look at your surrounding code and remove the unnecessary one. (If you're taking mul as an int, you probably want str(-mul) instead of str(mul)[1:], as described earlier.)
Once you've got this, if you understand list comprehensions, you might realize that this is a familiar pattern: start with [], loop over some other collection, and append each value that meets some test. In other words:
def balance_equation(species,coeff):
data=zip(coeff,species)
positive = [str(mul) + el for (mul, el) in data if int(mul) > 0]
negative = [str(mul) + el for (mul, el) in data if int(mul) < 0]
return positive, negative
You might notice that you can simplify this even further—the only thing you use the lists for is to build the strings, so maybe you just want a function that returns a string equation (in which case you can use a generator expression instead of a list comprehension—if you don't know about them yet, ignore that part).
def balance_equation(species,coeff):
data=zip(coeff,species)
positive = '+'.join(str(mul) + el for (mul, el) in data if int(mul) > 0)
negative = '-'.join(str(mul) + el for (mul, el) in data if int(mul) < 0)
return positive + '=' + negative
Of course now you're returning a string instead of a pair of lists, so you have to change your calling code to just print balance_equation(species, coeff) instead of combining the lists.
One last thing: You seem to reverse the order of the coefficients/multipliers and species/elements at each call. For example:
def balance_equation(species,coeff):
data=zip(coeff,species)
Unless there's a good reason to do otherwise, it's better to pick one order and be consistent throughout, or you're invariably going to run into a bug where you get them backwards.

Categories