Search code examples
pythonbinarybit-manipulationbitbitwise-operators

How to replace the independent 1 with 0 in binary number using only bitwise operations?


By "independent 1" I mean 1 that has no other 1 next to it ("010" or "10" and "01" at the ends of the num.) If the 1 doesn´t have any 1 next to it, it will change to 0.

For example:

11010 = 11000

10101 = 00000

1111 = 1111

I can´t use for or while loops. Only bitwise operators.

I tried something like this:

num = 0b11010
count = 0

if num & 1 == 1:
    if (num >> 1) & 1 == 1 or (num << 1) & 1 == 1:
        count += 1
else:
    count += 0

if (num >> 1) & 1 == 1:
    if (num >> 2) & 1 == 1 or (num >> 0) & 1 == 1:
        count += 2
else:
    count += 0
.
.
.
#(Same principle)

But the code will get too long when I try to implement it for bigger numbers.


Solution

  • This is a good exercise in bit shifting and bit comparison. One way this can be solved (there's probably several different ways) is by storing a left shifted and right shifted version of the original number. From there, you can use the XOR function to detect which bits are different. If both neighbors of a 1 are 0, then the XORs (as shown below) will simultaneously be 1. At that point, we have the 1's which are neighbored by two zeros. Then we can invert the value to highlight all the bits except for the bits we just found. Comparing that number with the original number with the AND function will 'erase' the bits from the original number.

    It's not so easy to explain this without visuals, so hopefully a look at the code will help in understanding. I'm assuming a 32-bit length here.

    num = 0b11010
    
    # Assume 32 bit, make sure only zeros are shifted in on either side
    num_l1 = (num<<1)&(~0x1)  # make sure the new right bit is zero
    num_r1 = (num>>1)&(~0x80000000)  # make sure the new left bit is zero
    
    # Print the left and right shifted numbers along with num. As we can see, we
    #  are looking for bits which are a 1 in the num, but a 0 in both left and right
    #  shift.
    print(f' Left Shifted Number: {"{:032b}".format((num<<1)&(~0x1))}')
    print(f'              Number: {"{:032b}".format(num)}')
    print(f'Right Shifted Number: {"{:032b}".format((num>>1)&(~0x80000000))}')
    
    # XORing with left and right will highlight values that are either 010 or 101 in
    #  the original num. We only want 010, so we then AND the new sequence with
    #  the original num to only allow 010 sequences to pass.
    find_lonely_ones = num & ((num^num_l1) & (num^num_r1))
    
    # We can then invert the lonely bits and AND with the original num
    new_num = num & (~find_lonely_ones)
    print()
    print(f'      Number: {"{:0b}".format(num)}')
    print(f'Final Answer: {"{:0b}".format(new_num)}')
    
    
    # We can print each step for clarification
    print()
    print(f' Left Shifted Number XOR Number: {"{:032b}".format(num^num_l1)}')
    print(f'Right Shifted Number XOR Number: {"{:032b}".format(num^num_r1)}')
    print(f'    AND of the above operations: {"{:032b}".format((num^num_l1) & (num^num_r1))}')
    print(f'     The above value AND Number: {"{:032b}".format(find_lonely_ones)}')
    print(f' NOT the above value AND Number: {"{:032b}".format(num & (~find_lonely_ones))}')