You are given two 32-bit numbers, N and M, and two bit positions, i and j. Write a method to set all bits between i and j in N equal to M (e.g., M becomes a substring of N located at i and starting at j). EXAMPLE: Input: N = 10000000000, M = 10101, i = 2, j = 6 Output: N = 10001010100
This problem is from Cracking the Coding interview. I was able to solve it using the following O(j - i) algorithm:
def set_bits(a, b, i, j):
if not b: return a
while i <= j:
if b & 1 == 1:
last_bit = (b & 1) << i
a |= last_bit
else:
set_bit = ~(1 << i)
a &= set_bit
b >>= 1
i += 1
return a
The author gave this O(1) algorithm as a solution:
def update_bits(n, m, i, j):
max = ~0 # All 1s
# 1s through position j, then zeroes
left = max - ((1 << j) - 1)
# 1s after position i
right = ((1 << i) - 1)
# 1’s, with 0s between i and j
mask = left | right
# Clear i through j, then put m in there
return (n & mask) | (m << i)
I noticed that for some test cases the author's algorithm seems to be outputting the wrong answer. For example for N = 488, M = 5, i = 2, j = 6 it outputs 468. When the output should be 404, as my O(j - i) algorithm does.
Question: Is there a way to get a constant time algorithm which works for all cases?
I think the author of the algorithm assumes the bound of j
(six in your example) to be exclusive; this boils down to the question whether a range from 2
to 6
should include 6
(in Python that is not the case). In other words, if the algorithm is modified to:
def update_bits(n, m, i, j):
max = ~0 # All 1s
# 1s through position j, then zeroes
left = max - ((1 << (j+1)) - 1)
# 1s after position i
right = ((1 << i) - 1)
# 1’s, with 0s between i and j
mask = left | right
# Clear i through j, then put m in there
return (n & mask) | (m << i)
It works.
Nevertheless you can speed up things a bit as follows:
def update_bits(n, m, i, j):
# 1s through position j, then zeroes
left = (~0) << (j+1)
# 1s after position i
right = ((1 << i) - 1)
# 1’s, with 0s between i and j
mask = left | right
# Clear i through j, then put m in there
return (n & mask) | (m << i)
In this example, we simply shift the ones out of the register.
Note that you made an error in your own algorithm, in case b = 0
, that does not mean you can simply return a
, since for that range, the bits should be cleared. Say a = '0b1011001111101111'
and b = '0b0'
and i
and j
are 6
and 8
respectively, one expects the result to be '0b1011001000101111'
. The algorithm thus should be:
def set_bits(a, b, i, j):
while i <= j:
if b & 1 == 1:
last_bit = (b & 1) << i
a |= last_bit
else:
set_bit = ~(1 << i)
a &= set_bit
b >>= 1
i += 1
return a
If I make this modification and I test the program with 10'000'000 random inputs, both algorithms always produce the same result:
for i in range(10000000):
m = randint(0,65536)
i = randint(0,15)
j = randint(i,16)
n = randint(0,2**(j-i))
if set_bits(m,n,i,j) != update_bits(m,n,i,j):
print((bin(m),bin(n),i,j,bin(set_bits(m,n,i,j)),bin(update_bits(m,n,i,j)))) #This line is never printed.
Of course this is not a proof both algorithms are equivalent (perhaps there is a tiny cornercase where they differ), but I'm quite confident that for valid input (i
and j
positive, i < j
, etc.) both should always produce the same result.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With