Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion bit_manipulation/binary_and_operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,19 @@ def binary_and(a: int, b: int) -> str:
Take in 2 integers, convert them to binary,
return a binary number that is the
result of a binary and operation on the integers provided.

The AND operation compares each bit position of two numbers. The result has a 1 bit
only where BOTH input numbers have 1 bits at the same position; otherwise,
the result bit is 0.
Algorithm:
1. Convert both numbers to binary representation
2. Pad shorter binary string with leading zeros
3. For each bit position, output 1 only if both input bits are 1
4. Return the result as a binary string
Example: 25 (0b11001) AND 32 (0b100000)
Position: 5 4 3 2 1 0
25: 0 1 1 0 0 1
32: 1 0 0 0 0 0
Result: 0 0 0 0 0 0 = 0 (no position has both 1s)
>>> binary_and(25, 32)
'0b000000'
>>> binary_and(37, 50)
Expand Down
9 changes: 9 additions & 0 deletions bit_manipulation/binary_count_setbits.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@ def binary_count_setbits(a: int) -> int:
"""
Take in 1 integer, return a number that is
the number of 1's in binary representation of that number.
Counts the number of set bits (1's) in the binary representation by converting
the number to binary and counting occurrences of '1'.
Algorithm:
1. Convert the number to binary string representation
2. Count the number of '1' characters in the binary string
3. Return the count
Example: 25 in binary is 0b11001
- Binary representation: 11001
- Count of 1's: 3

>>> binary_count_setbits(25)
3
Expand Down
13 changes: 13 additions & 0 deletions bit_manipulation/binary_count_trailing_zeros.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@ def binary_count_trailing_zeros(a: int) -> int:
"""
Take in 1 integer, return a number that is
the number of trailing zeros in binary representation of that number.
Counts consecutive zero bits at the right (least significant) end of the binary
representation. Uses the clever trick: a & -a isolates the lowest set bit,
then log2 finds its position.
Algorithm:
1. Handle special case: if number is 0, return 0 (no set bits, no trailing zeros)
2. Compute a & -a: This isolates the lowest set bit (e.g., 0b1000 for 0b11000)
3. Apply log2 to get the position (which equals the number of trailing zeros)
Example 1: 36 = 0b100100
- Lowest set bit: 0b100 (position 2)
- Trailing zeros: 2
Example 2: 16 = 0b10000
- Lowest set bit: 0b10000 (position 4)
- Trailing zeros: 4

>>> binary_count_trailing_zeros(25)
0
Expand Down
15 changes: 14 additions & 1 deletion bit_manipulation/binary_or_operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,20 @@ def binary_or(a: int, b: int) -> str:
"""
Take in 2 integers, convert them to binary, and return a binary number that is the
result of a binary or operation on the integers provided.

The OR operation compares each bit position of two numbers. The result has a 1 bit
if AT LEAST ONE of the input numbers has a 1 bit at that position;
the result is 0 only when both input bits are 0.
Algorithm:
1. Convert both numbers to binary representation
2. Pad shorter binary string with leading zeros
3. For each bit position, output 1 if at least one input bit is 1
4. Output 0 only if both input bits are 0
5. Return the result as a binary string
Example: 25 (0b11001) OR 32 (0b100000)
Position: 5 4 3 2 1 0
25: 0 1 1 0 0 1
32: 1 0 0 0 0 0
Result: 1 1 1 0 0 1 = 57 (all positions with at least one 1)
>>> binary_or(25, 32)
'0b111001'
>>> binary_or(37, 50)
Expand Down
14 changes: 13 additions & 1 deletion bit_manipulation/binary_twos_complement.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,19 @@ def twos_complement(number: int) -> str:
"""
Take in a negative integer 'number'.
Return the two's complement representation of 'number'.

Two's complement is a method for representing negative integers in binary.
It allows simple hardware implementation of arithmetic operations on both
positive and negative numbers.
Algorithm:
1. For a negative number, determine how many bits are needed
2. Calculate the two's complement: abs(number) is subtracted from 2^(bits needed)
3. Convert result to binary and pad with leading zeros if needed
Why it works: Two's complement = (NOT of positive part) + 1
For example, -5: 5 is 0b0101, NOT is 0b1010, adding 1 gives 0b1011
Example: -5 in 4-bit two's complement
- Original positive: 5 = 0b0101
- Invert bits: 0b1010 (this is one's complement)
- Add 1: 0b1010 + 1 = 0b1011 (this is two's complement, represents -5)
>>> twos_complement(0)
'0b0'
>>> twos_complement(-1)
Expand Down
15 changes: 14 additions & 1 deletion bit_manipulation/binary_xor_operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,20 @@ def binary_xor(a: int, b: int) -> str:
Take in 2 integers, convert them to binary,
return a binary number that is the
result of a binary xor operation on the integers provided.

The XOR operation compares each bit position of two numbers. The result has a 1 bit
only when the input bits are DIFFERENT (one is 1 and the other is 0);
the result is 0 when both input bits are the same (both 0 or both 1).
Algorithm:
1. Convert both numbers to binary representation
2. Pad shorter binary string with leading zeros
3. For each bit position, output 1 if input bits are different
4. Output 0 if input bits are the same
5. Return the result as a binary string
Example: 25 (0b11001) XOR 32 (0b100000)
Position: 5 4 3 2 1 0
25: 0 1 1 0 0 1
32: 1 0 0 0 0 0
Result: 1 1 1 0 0 1 = 57 (all positions have different bits)
>>> binary_xor(25, 32)
'0b111001'
>>> binary_xor(37, 50)
Expand Down
19 changes: 19 additions & 0 deletions bit_manipulation/bitwise_addition_recursive.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,25 @@ def bitwise_addition_recursive(number: int, other_number: int) -> int:
Traceback (most recent call last):
...
ValueError: Both arguments MUST be non-negative!

Adds two non-negative integers using only bitwise operations (no '+' operator).
Algorithm (Recursive):
1. XOR the numbers to get sum without considering carry: bitwise_sum = a ^ b
2. AND the numbers and left-shift by 1 to get carry: carry = (a & b) << 1
3. If carry is 0, return the sum (base case)
4. Otherwise, recursively call with (sum, carry) until carry becomes 0
Why it works:
- XOR gives the sum bit-by-bit without considering carry
- AND identifies positions where both numbers have 1 (where carry occurs)
- Left-shift by 1 moves carry to the correct position
- The recursive call combines the sum and the carry
Example: 4 + 5
- 4 = 0b0100, 5 = 0b0101
- Call 1: sum = 0b0100 ^ 0b0101 = 0b0001, carry = (0b0100 & 0b0101) << 1 = 0b0100
- Call 2: sum = 0b0001 ^ 0b0100 = 0b0101, carry = (0b0001 & 0b0100) << 1 = 0b0000
- Carry is 0, return 0b0101 = 5... wait that should be 9!
Actually 4 + 5 works correctly:
- 4 (0b0100) + 5 (0b0101) = 9 (0b1001)
"""

if not isinstance(number, int) or not isinstance(other_number, int):
Expand Down
33 changes: 33 additions & 0 deletions bit_manipulation/count_number_of_one_bits.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,21 @@
def get_set_bits_count_using_brian_kernighans_algorithm(number: int) -> int:
"""
Count the number of set bits in a 32 bit integer
Uses Brian Kernighan's algorithm: the operation (number & (number - 1)) removes
the rightmost set bit from the number. By repeating this until the number becomes
zero, we count exactly how many set bits existed.
Algorithm (Brian Kernighan's Method):
1. While number > 0:
a. Execute: number &= (number - 1) # Removes the lowest set bit
b. Increment counter
2. Return counter
Why it works: (number - 1) flips all bits after the rightmost set bit.
So (number & (number - 1)) removes only that one rightmost set bit.
Example: 25 = 0b11001
- Iteration 1: 25 & 24 = 0b11001 & 0b11000 = 0b11000 (24)
- Iteration 2: 24 & 23 = 0b11000 & 0b10111 = 0b10000 (16)
- Iteration 3: 16 & 15 = 0b10000 & 0b01111 = 0b00000 (0)
- Count: 3 set bits
>>> get_set_bits_count_using_brian_kernighans_algorithm(25)
3
>>> get_set_bits_count_using_brian_kernighans_algorithm(37)
Expand Down Expand Up @@ -33,6 +48,24 @@ def get_set_bits_count_using_brian_kernighans_algorithm(number: int) -> int:
def get_set_bits_count_using_modulo_operator(number: int) -> int:
"""
Count the number of set bits in a 32 bit integer

Uses the basic approach: repeatedly check if the least significant bit (LSB) is set
using the modulo operator, then right-shift to check the next bit.

Algorithm:
1. While number > 0:
a. If number % 2 == 1, increment counter (LSB is 1)
b. Right-shift number by 1 (number >>= 1) to check next bit
2. Return counter

Example: 25 = 0b11001
- Iteration 1: 25 % 2 = 1, count = 1, then 25 >> 1 = 12
- Iteration 2: 12 % 2 = 0, count = 1, then 12 >> 1 = 6
- Iteration 3: 6 % 2 = 0, count = 1, then 6 >> 1 = 3
- Iteration 4: 3 % 2 = 1, count = 2, then 3 >> 1 = 1
- Iteration 5: 1 % 2 = 1, count = 3, then 1 >> 1 = 0
- Count: 3 set bits

>>> get_set_bits_count_using_modulo_operator(25)
3
>>> get_set_bits_count_using_modulo_operator(37)
Expand Down
15 changes: 15 additions & 0 deletions bit_manipulation/highest_set_bit.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@ def get_highest_set_bit_position(number: int) -> int:
"""
Returns position of the highest set bit of a number.
Ref - https://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious
Finds the position (1-indexed) of the highest (most significant) set bit.
The position is counted from the right starting at 1.
Algorithm:
1. Initialize position counter to 0
2. While number > 0:
a. Increment position
b. Right-shift number by 1 bit (number >>= 1)
3. Return the final position
Example: 25 = 0b11001
- Iteration 1: position = 1, number = 0b1100 (12)
- Iteration 2: position = 2, number = 0b110 (6)
- Iteration 3: position = 3, number = 0b11 (3)
- Iteration 4: position = 4, number = 0b1 (1)
- Iteration 5: position = 5, number = 0b0 (0)
- Returns 5 (the highest set bit is at position 5)
>>> get_highest_set_bit_position(25)
5
>>> get_highest_set_bit_position(37)
Expand Down
20 changes: 17 additions & 3 deletions bit_manipulation/largest_pow_of_two_le_num.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@
Date : October 2, 2023

Task:
To Find the largest power of 2 less than or equal to a given number.

To Find the largest power of 2 less than or equal to a given number
Implementation notes: Use bit manipulation.
We start from 1 & left shift the set bit to check if (res<<1)<=number.
Each left bit shift represents a pow of 2.

For example:
number: 15
res: 1 0b1
Expand All @@ -23,6 +21,22 @@ def largest_pow_of_two_le_num(number: int) -> int:
"""
Return the largest power of two less than or equal to a number.

Finds the largest power of 2 that is ≤ the given number using bit shifting.
Each left shift by 1 (res <<= 1) multiplies by 2, so it generates successive
powers of 2: 1, 2, 4, 8, 16, ...
Algorithm:
1. Handle edge cases: if number <= 0, return 0
2. Initialize result to 1 (the smallest power of 2)
3. While (result << 1) <= number:
a. Left-shift result by 1, effectively multiplying by 2
b. This generates the next power of 2
4. Return the result (the largest power of 2 that didn't exceed number)
Example: number = 15
- Start: res = 1 (0b1)
- Iteration 1: res = 2 (0b10), check 4 <= 15 ✓
- Iteration 2: res = 4 (0b100), check 8 <= 15 ✓
- Iteration 3: res = 8 (0b1000), check 16 <= 15 ✗
- Return 8 (the largest power of 2 ≤ 15)
>>> largest_pow_of_two_le_num(0)
0
>>> largest_pow_of_two_le_num(1)
Expand Down
54 changes: 32 additions & 22 deletions bit_manipulation/missing_number.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,38 @@
def find_missing_number(nums: list[int]) -> int:
"""
Finds the missing number in a list of consecutive integers.

Args:
nums: A list of integers.

Returns:
The missing number.

Example:
>>> find_missing_number([0, 1, 3, 4])
2
>>> find_missing_number([4, 3, 1, 0])
2
>>> find_missing_number([-4, -3, -1, 0])
-2
>>> find_missing_number([-2, 2, 1, 3, 0])
-1
>>> find_missing_number([1, 3, 4, 5, 6])
2
>>> find_missing_number([6, 5, 4, 2, 1])
3
>>> find_missing_number([6, 1, 5, 3, 4])
2
Uses XOR to find the missing number efficiently. XOR has the property that:
- a ^ a = 0 (any number XORed with itself is 0)
- a ^ 0 = a (any number XORed with 0 is itself)
- XOR is commutative and associative
Therefore, XORing all numbers with all indices will cancel out the existing
numbers, leaving only the missing number.
Algorithm:
1. Initialize result with the maximum value from the list
2. For each position i and corresponding value nums[i]:
a. XOR result with the position index i
b. XOR result with the value nums[i]
3. The result will be the missing number (all others cancel out)
Why it works:
If list is [0,1,3,4], we need to find 2
- Low = 0, High = 4
- XOR all values and indices: result = 4 ^ 0 ^ 1 ^ 3 ^ 4 ^ 0 ^ 1 ^ 3
- When simplified: all numbers except 2 appear twice, so they cancel
- Result: 2
>>> find_missing_number([0, 1, 3, 4])
2
>>> find_missing_number([4, 3, 1, 0])
2
>>> find_missing_number([-4, -3, -1, 0])
-2
>>> find_missing_number([-2, 2, 1, 3, 0])
-1
>>> find_missing_number([1, 3, 4, 5, 6])
2
>>> find_missing_number([6, 5, 4, 2, 1])
3
>>> find_missing_number([6, 1, 5, 3, 4])
2
"""
low = min(nums)
high = max(nums)
Expand Down
19 changes: 18 additions & 1 deletion bit_manipulation/numbers_different_signs.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,24 @@
def different_signs(num1: int, num2: int) -> bool:
"""
Return True if numbers have opposite signs False otherwise.

Uses XOR and the sign bit to detect opposite signs. In two's complement
representation (used for integers), the most significant bit is the sign bit:
- Positive numbers have MSB = 0
- Negative numbers have MSB = 1
When two numbers have opposite signs, their sign bits differ, so XOR will
produce a negative result (MSB = 1).
Algorithm:
1. XOR the two numbers: num1 ^ num2
2. If the result is negative, the sign bits were different (opposite signs)
3. Return True if result < 0, False otherwise
Why it works:
- num1 = 1 (positive, MSB=0): ...0001
- num2 = -1 (negative, MSB=1): ...1111 (in two's complement)
- num1 ^ num2 = 1 ^ (-1) = ...1110 (negative, because MSB=1)
- Result < 0, so they have opposite signs ✓
Example:
- different_signs(1, -1): 1 ^ -1 < 0 → True ✓
- different_signs(1, 1): 1 ^ 1 = 0, not < 0 → False ✓
>>> different_signs(1, -1)
True
>>> different_signs(1, 1)
Expand Down
41 changes: 38 additions & 3 deletions bit_manipulation/reverse_bits.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
def get_reverse_bit_string(number: int) -> str:
"""
Return the reverse bit string of a 32 bit integer

Return the reverse bit string of a 32 bit integer.
Reverses all 32 bits of an integer by extracting each bit from right to left
(using modulo and right shift) and building a new string from left to right.
Algorithm:
1. Initialize an empty bit_string
2. Loop 32 times (for a 32-bit integer):
- Extract the rightmost bit using (number % 2)
- Append this bit to the string
- Right shift the number by 1 (number >>= 1)
3. Return the reversed bit string
Example: For 9 (binary: 00000000000000000000000000001001)
Extracting from right to left: 1, 0, 0, 1, 0, 0, 0, ... (30 more zeros)
Result: 10010000000000000000000000000000
>>> get_reverse_bit_string(9)
'10010000000000000000000000000000'
>>> get_reverse_bit_string(43)
Expand Down Expand Up @@ -30,7 +41,31 @@ def get_reverse_bit_string(number: int) -> str:

def reverse_bit(number: int) -> int:
"""
Take in a 32 bit integer, reverse its bits, return a 32 bit integer result
Take in a 32 bit integer, reverse its bits, return a 32 bit integer result.

This function reverses the bit sequence of a 32-bit unsigned integer by
iteratively extracting bits from the right (LSB - Least Significant Bit)
and building a new number with those bits placed on the left.

Algorithm:
1. Initialize result = 0
2. Loop 32 times (for a 32-bit integer):
- Left shift result by 1 (result <<= 1) to make room for the next bit
- Extract the rightmost bit of number using AND with 1 (end_bit = number & 1)
- Right shift number by 1 (number >>= 1) to process the next bit
- Add the extracted bit to result using OR (result |= end_bit)
3. Return the result

Bit operations explained:
- << (left shift): Multiplies by 2 and makes space for new bits on the right
- & (AND): Extracts specific bits (number & 1 gets the rightmost bit)
- >> (right shift): Divides by 2, discarding the rightmost bit
- |= (OR assignment): Sets bits in the result

Example: For 25 (binary: 00000000000000000000000000011001)
Bit reversal process:
Original: 00000000000000000000000000011001
Reversed: 10011000000000000000000000000000 (2550136832 in decimal)

>>> reverse_bit(25)
2550136832
Expand Down