Convert signed binary number into integer in Python - python

I found out that in python, if you use bin:
>>> bin(4)
'0b100'
>>> bin(-4)
'-0b100'
but if you try this:
-4 >> 8 => -1
in fact, when right shift more than 3 bits, the answer is always -1. I know this is implementation stuff, but I came across this problem:
In the following array, find out the number which appears only once, while others appears always three times
[-2,-2,1,1,-3,1,-3,-3,-4,-2]
This is the code
class Solution(object):
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if nums == None:
return None
ans = 0
for i in range(32):
numOfI = 0
for num in nums:
if (( num >> i) & 1) == 1 :
numOfI += 1
numOfI %=3
print i,numOfI
if numOfI != 0:
ans = ans + (numOfI << i)
return ans
the answer should be -4, but instead I get 11111111111111111111111111111100; how can I convert it into -4?

Bit manipulation on Python are easily done with int.from_bytes and int.to_bytes.
On the other hand, solving the "find the value appearing x times" is easily solved using the Counter class:
>>> from collections import Counter
>>> values = [-2,-2,1,1,-3,1,-3,-3,-4,-2]
>>> [value for value, count in Counter(values).items() if count == 1]
[-4]
Using from_bytes and to_bytes is clear and readable, so you don't want to try to do the conversion yourself.
Using Counter is also more readable than your cryptic implementation, yet my one-liner may not be the most readable imaginable version, it's still a single line and far more readeable.

Related

Suggestions for optimization Python

This is a fairly straight forward programming problem in Python and I am looking for suggestions for further optimization. I am successfully processing in time except for very large strings. I am not looking for code rather areas that I should research for optimization improvements. I have already identified that I can skip even numbers reducing the loop operation and given the nature of the operations the pattern eventually repeats which is why I track when repeat occurs. This allows me break out if n > repeat. I am not positive if converting the string to a list is the most effective.
Problem:
We have a string s and we have a number n that indicates the number of times to run the function. Here is a function that takes your string, concatenates the even-indexed chars to the front, odd-indexed chars to the back. You perform this operation n times.
Example:
example where s = "qwertyuio" and n = 2:
after 1 iteration s = "qetuowryi"
after 2 iterations s = "qtorieuwy"
return "qtorieuwy"
def jumbled_string(s, n):
sl = list(s)
repeat = 0
for y in range(0,n):
for i in range(1, (len(sl)//2)+1):
sl.append(sl.pop(i))
if repeat == 0 and ''.join(sl) == s:
repeat = y+1
break
if repeat != 0:
afterrepeat = n%repeat
for y in range(0,afterrepeat):
for i in range(1, (len(sl)//2)+1):
sl.append(sl.pop(i))
return ''.join(sl)
I don't know what you mean by "pattern repeats". But if we stick to the problem statement, it's a one liner in Python:
s='abecidofug'
from itertools import chain
s2 = ''.join(chain([s[c] for c in range(0, len(s), 2)],[s[c] for c in range(1, len(s), 2)]))
s2
'aeioubcdfg'
In python 3.8+ (due to := operator) you could do it like this:
import collections
def jumbled_string(s: str, n: int) -> str:
generator = (s:=s[::2]+s[1::2] for _ in range(n))
collections.deque(generator, maxlen=0)
return s
Using collections.deque as this is the Fastest (most Pythonic) way to consume an iterator.
Though, for small n I'm finding it faster to use:
def jumbled_string(s: str, n: int) -> str:
for _ in (s:=s[::2]+s[1::2] for _ in range(n)):
pass
return s
Test:
jumbled_string("qwertyuio", 2)
Output:
'qtorieuwy'
You don't explain what n does. The statement is this:
def jumbled_string(s: str) -> str:
even = s[::2]
odd = s[1::2]
return even+odd
print(jumbled_string("0123456789"))
>>>0246813579

How is an iterable created when the first argument to sum includes a for loop?

The Python documentation for sum shows that an iterable has to be passed in as the first argument for sum.
I was solving this leetcode problem 1295. Find Numbers with Even Number of Digits
:
Given an array nums of integers, return how many of them contain an even number of digits.
I was solving this the long way like this:
# # longer as in more lines of code
class Solution:
def findNumbers(self, nums: List[int]) -> int:
count = 0
for num in nums:
if len(str(num)) % 2 == 0:
count += 1
return count
but decided to look at the other suggested answers to see if I missed something. One never knows with Easy questions on Leetcode. That's when I ran into this line of code that made me wonder if I understood what was going on.
from typing import List
# one line solution
class Solution:
def findNumbers(self, nums: List[int]) -> int:
return sum(len(str(num)) %2 == 0 for num in nums)
Could someone clarify how "future" booleans lead to an iterable? Is a list being created from the for loop?
That's not something special to sum. len(str(num)) %2 == 0 for num in nums is a generator expression, it's like list comprehension but does not create a list. Instead an iterator that calculates values on demand when they're needed.
Notice that replacing the generator expression with a list comprehension gives the same result:
return sum([len(str(num)) %2 == 0 for num in nums])
But leads to more memory usage.
Then there is what Samwise mentioned, True == 1 and False == 0. You can change your code to use this fact
class Solution:
def findNumbers(self, nums: List[int]) -> int:
count = 0
for num in nums:
count += len(str(num)) % 2 == 0
return count
The ... for ... expression inside the sum() is a grammar sugar to create annoymous generator in python (see here for more details). The equlivent code shall be
def f(num):
for num in nums:
yield len(str(num)) %2 == 0
class Solution:
def findNumbers(self, nums: List[int]) -> int:
return sum(f(nums))

Why does the `eval` function seemingly take so long in Python?

TL;DR why is Python's eval function slow?
Hi. I was solving a coding exercise and noticed that the eval function was causing timeouts for some test cases and was wondering why this is the case as I don't particularly recall reading that the function is slow.
Here's the exercise:
You are given a list of n nonnegative integers and a target integer. Find out how many possible ways there are to add '+' or '-' in between the provided integers in order to obtain the target. For example, the input [1, 1, 1, 1, 1] and 3 would return 5 since there are five total ways to insert + or - to obtain 3: -1+1+1+1+1, +1-1+1+1+1, +1+1-1+1+1, +1+1+1-1+1, +1+1+1+1-1.
The solution I initially came up with used eval as follows:
from itertools import product
from typing import List
def solution(numbers: List[int], target: int) -> int:
numbers = [str(x) for x in numbers]
length = len(numbers)
all_operations = list(map(''.join, product('+-', repeat=length)))
answer = 0
for operation in all_operations:
eval_string = ''.join([''.join(x) for x in zip(operation, numbers)])
if eval(eval_string) == target:
answer += 1
return answer
The second solution that passes all test cases doesn't use eval and simply performs the arithmetic step by step:
from itertools import product
from typing import List
def solution(numbers: List[int], target: int) -> int:
numbers = [str(x) for x in numbers]
length = len(numbers)
all_signs = list(map(''.join, product('+-', repeat=length)))
answer = 0
for operation in all_signs:
value = 0
for op, number in zip(operation, numbers):
if op == '+':
value += int(number)
elif op == '-':
value -= int(number)
if value == target:
answer += 1
return answer
Converting your values to strings then running eval is not going to be efficient. This will be faster although it may be possible to improve this even further:
import itertools
INTS = [1, 1, 1, 1, 1]
CONST = 3
# build a new list comprised of the original INTS plus their negative values
P = list(map(lambda x: -x, INTS)) + INTS
# work out the unique permutations
s = set(itertools.permutations(P, len(INTS)))
# iterate over (map) each unique permutations to see if its sum matches CONST
print(list(map(lambda x: sum(x) == CONST, s)).count(True))

Find complement of number in Python

I am trying to find the complement of a number in Python. I know there are other solutions but I am trying to make my own.
My first try:
def findComplement(self, num):
"""
:type num: int
:rtype: int
"""
numb = str(num)
i = 0
while(i<=len(numb)):
if numb[i] == "0":
numb[i] = "1"
else:
numb[i] = "0"
i=i+1
return int(numb)
But strings are immutable so it gave an error,
My second try:
def findComplement(self, num):
"""
:type num: int
:rtype: int
"""
numb = str(num)
numb2 = []
k =0
for j in numb:
numb2[k] = j #error on this line
k=k+1
i = 0
while(i<=len(numb2)):
if numb2[i] == "0":
numb2[i] = "1"
else:
numb2[i] = "0"
i=i+1
return int(numb2)
Error in program 2:
Line 11: IndexError: list assignment index out of range
Since other answers cover your main problem, you can also just use a dictionary for mapping 1 to 0, and vice versa:
>>> d = {'0': '1', '1': '0'}
>>> s = '0101'
>>> ''.join(d[x] for x in s)
'1010'
So I wanted find the binary complement of a number myself
but was unstatisfied with how the mask is generated and other things, cause they involved using the string representation of the binray number.
So I dug a little deeper and came across this reddit post
where they generated the mask via bitwise shifts.
And I also found out that you can get the bit count of an integer
with int.bit_lenght(<your_number>),
instead of using len(bin(<your-number>)[2:]).
Only problem is: that it returns 0 for the number 0.
So you have to account for that.
Tho I have no clue what method is more performant
and whether it is worth it compared to string manipulation.
But in the end that was my final solution
that I found was pretty neat cause it doesn't involve strings.
def bin_complement(num, on_two=False, bit_count=0):
"""
num(int) dezimal number
on_two(bool) complement on 2 else on 1
bit_count = number of bits for the mask, defaults to bits in num
returns(int) complementary number
"""
bit_count = max(bit_count, num.bit_length(), 1)
mask = 2 ** bit_count - 1
complement = num ^ mask
if on_two:
complement += 1
return complement
Edit: 2**bit_count - 1 is a faster way for doing
for _ in range(bit_count):
mask <<= 1
mask |= 1
Edit2: If you want to take the complement of fixed length bit number.
E.G: C_1(0001) -> 0001 xor 1111 = 1110
and not just 0000.
You can now set a bit_count value for the mask
so the complement works correct.
You have a few problems.
First you are not converting your input to a binary string properly. This can be done by numb = bin(num)[2:] instead of num = str(num).
Second, you are trying to index into your empty numb2 list. Actually there is no need to create a separate numb2 list, you can just operate directly on your numb string, as in your first attempt. For example:
for i in range(len(numb)):
if numb[i] == "0":
numb[i] = "1"
else:
numb[i] = "0"
Finally, to convert your binary string back to an int you should do int(numb, 2) not int(numb).
numb2 is an empty list. It does not have any elements to be addressed (and updated). What you need is a bytearray, rather than a list (or a string):
def findComplement(self, num):
"""
:type num: int
:rtype: int
"""
numb = bytearray(str(num), 'utf-8')
i = 0
while(i<len(numb)):
if numb[i] == ord("0"):
numb[i] = ord("1")
else:
numb[i] = ord("0")
i=i+1
return int(numb)
As can be seen, this requires some minor other changes, as the stored data in a bytearray is a byte (0-255) rather than a char. The solution is to use the ord function, to get the byte number from "0" and "1". In addition there is a small issue (off by one) in your whileexpression. Since the indexing and your count start at zero i should be strictly smaller than the length of the string.
I would also like to add that there are simper ways to achive the complement of 0 and/or 1, though.
Let's say we have to find complement of 101 if we xor the input with the mask 111 then we will get 010 which is complement.
def findComplement(self, num: int) -> int:
mask_len=len(bin(num))-2
mask=int("1"*mask_len,2)
return mask^num
For those looking for a one-liner, here is one:
int(bin(value)[2:].zfill(numbits).translate(str.maketrans('01', '10')), 2)
The above computes a numbits-bit ones' complement for value.
Since str.maketrans('01', '10') is a constant (dictionary), one can compute it ahead of time:
dMatrix = str.maketrans('01', '10') # which is {48: 49, 49: 48}
int(bin(value)[2:].zfill(numbits).translate(dMatrix), 2)
Converting this one-liner to a function is straightforward:
def onescomplement(value, numbits):
return int(bin(value)[2:].zfill(numbits).translate(str.maketrans('01', '10')), 2)
HTH
Don't you just mean the ~ operator, e.g:
def findComplement(self, num):
return ~num

Merge Sort on Python: Unusual pattern of result obtained

I have a unsorted array of 10,000 integers from 0 to 9,999. I wanted to apply merge sort on this unsorted array and I wrote the following code-
import sys
def merge_sort(data):
result = []
if len(data) <= 1:
return data
else:
mid = int(len(data)/2)
left = data[:mid]
right = data[mid:]
sorted_left = merge_sort(left)
sorted_right = merge_sort(right)
i = j = k = 0
total_len = len(sorted_left) + len(sorted_right)
for k in range(0, total_len):
if i < len(sorted_left) and j < len(sorted_right):
if sorted_left[i] < sorted_right[j]:
result.append(sorted_left[i])
i = i+1
k = k+1
elif sorted_left[i] > sorted_right[j]:
result.append(sorted_right[j])
j = j+1
k = k+1
elif i < len(sorted_left):
result.append(sorted_left[i])
i = i+1
k = k+1
elif j < len(sorted_right):
result.append(sorted_right[j])
j = j+1
k = k+1
else:
sys.exit("There is some issue with the code")
return result
print merge_sort(data)
So when I sort this data, I get a correct sort order except for a few entries. For example- towards the end I get this kind of result-
[...'9989', '999', '9990', '9991', '9992', '9993', '9994', '9995', '9996', '9997', '9998', '9999']
As you might observe, number '999' is at the wrong place. Not just in this snippet but it happens in other places too like '995' appearing between '9949' and '9950'.So anybody has any idea why this is happening?
P.S.- I ran this code for debug and it ran without errors producing these results
You are ordering strings: '9989' < '999' < '9990'. If you want to order integers, you'll have to convert your input list to integers.
Your data is in strings, not numbers. To convert to integers, use:
data = [int(x) for x in data]
Python will "compare" a wide variety of objects. For example:
>>> "a" < "ab"
True
>>> None < "a"
True
>>> 1 < "a"
True
If you compare such items, python will not object.
Comparison in python
For integers and strings, python has built-in methods for comparisons. For objects that you create, you can define your own comparison methods. Methods that you can define for your objects that python will use for comparison include:
object.__lt__(self, other)
object.__le__(self, other)
object.__eq__(self, other)
object.__ne__(self, other)
object.__gt__(self, other)
object.__ge__(self, other)
By defining your own methods for your objects, there is great flexibility.
Is your data coming in as strings or integers? Based on your sample output, they are strings.
In such a case, '1' comes just before '10'. If you're expecting integers, then you could convert to int to do the sort.

Categories