Bit Manipulation in TheAlgorithms/Python
Seven techniques that use bitwise operations to count, test, flip, reverse, and encode integers without arithmetic.
What you will learn
- How counting set bits with bin().count('1') delegates work to Python's internal bit representation
- Why Brian Kernighan's n &= n-1 trick clears exactly one set bit per iteration and why the loop runs in O(k) time where k is the number of set bits
- How OR, AND-NOT, and XOR with a shifted mask implement set, clear, and flip for a single bit position
- How shifting right by position and ANDing with 1 tests whether a specific bit is set
- How the 32-bit reversal loop extracts the lowest bit of the input and inserts it at the highest position of the result
- Why n & (n-1) == 0 detects powers of two in one expression without division or logarithms
- How the recursive reflection rule for Gray code ensures adjacent code words differ by exactly one bit
Prerequisites
- Familiarity with binary (base-2) integer representation
- Basic Python: loops, lists, integers
- Understanding of bitwise operators: &, |, ^, ~, <<, >>
Counting set bits the direct way: delegate to Python's bit string
bit_manipulation/binary_count_setbits.py:1Converting an integer to its binary string and counting '1' characters is the simplest correct popcount implementation.
Counting how many bits in an integer are set to 1 is a fundamental operation in hashing, compression, error detection, and combinatorics. This file answers that question with a single expression: bin(a).count("1"). The reason this works is that Python's built-in bin() function returns the integer's exact binary representation as a string prefixed with '0b', and str.count is a linear scan that costs O(log n) in the number of bits. The doctest confirms three set bits for 25 (binary 11001) and 32 for 4,294,967,295, which is the 32-bit all-ones integer. The guard against negatives is necessary because Python integers have unlimited precision and negative values encode as two's complement strings of theoretically infinite length, making popcount undefined without a bit-width bound.
bin(a).count("1") is the clearest correct popcount for arbitrary-precision Python integers; the guard against negatives prevents a two's complement trap.
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.
>>> binary_count_setbits(25)
3
>>> binary_count_setbits(36)
2
>>> binary_count_setbits(16)
1
>>> binary_count_setbits(58)
4
>>> binary_count_setbits(4294967295)
32
>>> binary_count_setbits(0)
0
>>> binary_count_setbits(-10)
Traceback (most recent call last):
...
ValueError: Input value must be a positive integer
>>> binary_count_setbits(0.8)
Traceback (most recent call last):
...
TypeError: Input value must be a 'int' type
>>> binary_count_setbits("0")
Traceback (most recent call last):
...
TypeError: '<' not supported between instances of 'str' and 'int'
"""
if a < 0:
raise ValueError("Input value must be a positive integer")
elif isinstance(a, float):
raise TypeError("Input value must be a 'int' type")
return bin(a).count("1")Brian Kernighan's method: clear one set bit per iteration
bit_manipulation/count_1s_brian_kernighan_method.py:30n &= n-1 clears the lowest set bit in n because subtracting 1 flips all bits from the lowest set bit downward, and AND masks them back to zero.
The naive approach to counting set bits loops over every bit position and checks each one, taking O(W) time where W is the word width. Brian Kernighan's method runs the loop only as many times as there are set bits. The identity n &= n - 1 works because subtracting 1 from n propagates a borrow that flips the lowest set bit to 0 and sets all lower bits to 1. ANDing the original n with this value zeroes the lowest set bit and leaves higher bits unchanged. The file's comment captures this exactly: the loop will only run the number of 1 times. For sparse integers, such as those produced by permission bitmasks or sparse feature vectors, this is substantially faster than scanning every bit position. The technique is documented in Kernighan and Ritchie's The C Programming Language and cited in the docstring's reference URL.
Brian Kernighan's n &= n - 1 clears the lowest set bit per iteration, making the loop cost O(k) rather than O(word width).
if not isinstance(number, int) or number < 0:
raise ValueError("Input must be a non-negative integer")
count = 0
while number:
# This way we arrive at next set bit (next 1) instead of looping
# through each bit and checking for 1s hence the
# loop won't run 32 times it will only run the number of `1` times
number &= number - 1
count += 1
return countSingle-bit primitives: set, clear, flip, and test with OR, AND-NOT, XOR, and shift
bit_manipulation/single_bit_manipulation_operations.py:6Each single-bit operation builds a position mask with 1 << position and applies the appropriate bitwise operator to the target number.
Compilers and embedded systems code manipulate individual bits inside registers and status words constantly. This file provides four composable building blocks: set_bit, clear_bit, flip_bit, and is_bit_set. Each one builds a mask by left-shifting the integer literal 1 to the target position, creating a number that is zero in every bit except position. The four operations then differ only in which bitwise operator they apply. Set uses OR so the target bit becomes 1 regardless of its current value. Clear uses AND with the bitwise NOT of the mask so the target bit becomes 0 regardless. Flip uses XOR because XOR with 1 inverts any bit. Test shifts the target bit to position 0 and ANDs with 1 to extract its value as a boolean. The doctest for set_bit(0b1101, 1) produces 15, which is 0b1111, confirming the second bit was set.
Four single-bit operations (set_bit, clear_bit, flip_bit, is_bit_set) all derive from the mask 1 << position, differing only in their operator.
def set_bit(number: int, position: int) -> int:
"""
Set the bit at position to 1.
Details: perform bitwise or for given number and X.
Where X is a number with all the bits - zeroes and bit on given
position - one.
>>> set_bit(0b1101, 1) # 0b1111
15
>>> set_bit(0b0, 5) # 0b100000
32
>>> set_bit(0b1111, 1) # 0b1111
15
"""
return number | (1 << position)Reversing 32 bits: extract the lowest bit and insert it at the top
bit_manipulation/reverse_bits.py:69Reversing a 32-bit integer peels off the input's low bit each iteration and deposits it into the result's currently available high bit position.
Bit reversal is used in fast Fourier transform butterfly stages, CRC polynomial evaluation, and certain hash functions. The correct approach for a fixed 32-bit width runs a loop exactly 32 times and uses no lookup table. Each iteration makes progress simultaneously on both integers: the input loses its lowest bit via a right shift, and the result gains that bit at its current top position via a left shift followed by OR. The key invariant is that after k iterations, the result holds the reversed version of the k least significant bits of the original input and the k most significant bit positions of the result are occupied. The doctest confirms: reverse_bit(25) returns 2,550,136,832, and reverse_bit(2550136832) returns 25, demonstrating that the operation is its own inverse.
The 32-bit reversal loop alternates left-shifting the result and right-shifting the input, depositing each extracted low bit into the result's high position over 32 fixed iterations.
result = 0
# iterator over [0 to 31], since we are dealing with a 32 bit integer
for _ in range(32):
# left shift the bits by unity
result <<= 1
# get the end bit
end_bit = number & 1
# right shift the bits by unity
number >>= 1
# add that bit to our answer
result |= end_bit
return resultFinding the unique number with XOR: duplicates cancel, the singleton survives
bit_manipulation/find_unique_number.py:28XOR is its own inverse: a ^ a == 0 for any value a, so XORing a list where all but one value appears twice collapses duplicates to zero, leaving only the unique element.
Finding the single non-duplicate element in a list where every other value appears exactly twice looks like it requires a hash set for O(n) time and O(n) space. XOR collapses this to O(n) time and O(1) space by exploiting two properties of XOR: a ^ a == 0 (any value XORed with itself is zero) and a ^ 0 == a (any value XORed with zero is itself). Folding the entire list with XOR means every pair cancels to 0, and 0 XORed with the singleton is the singleton. The doctest confirms: find_unique_number([1, 1, 2, 2, 3]) returns 3 because both 1s and both 2s cancel. This is not a trick specific to integers; the same pattern applies to any abelian group where every element is its own inverse, which is exactly the structure XOR provides over fixed-width bit patterns.
XOR over a list where every value appears an even number of times except one collapses all pairs to 0, leaving the unique element via result ^= num.
result = 0
for num in arr:
result ^= num
return resultDetecting powers of two: n & (n-1) == 0 without division
bit_manipulation/is_power_of_two.py:18Powers of two have a single set bit; n - 1 flips that bit and all lower bits, so n & (n-1) is zero if and only if n is a power of two.
Powers of two have exactly one bit set, which makes them recognizable in constant time without iteration. Subtracting one from a power of two flips that single set bit to zero and turns every lower zero into a one, so n & (n - 1) equals zero only when no other bits are set. Lines 32 and 33 reject negative inputs because the same check would silently misclassify two's-complement representations of negative numbers. The single return on line 34 (number & (number - 1) == 0) is the entire decision: positive inputs that are powers of two zero out, everything else leaves at least one bit standing. The doctests cover 0, 1, 2, 4, 6, 8, 17, and a 64-bit value to verify the boundary cases and confirm that 0 is treated as not-a-power-of-two via the lone n > 0 guard.
XOR over a list where every value appears an even number of times except one collapses all pairs to 0, leaving only the unique element.
def is_power_of_two(number: int) -> bool:
"""
Return True if this number is power of 2 or False otherwise.
>>> is_power_of_two(0)
True
>>> is_power_of_two(1)
True
>>> is_power_of_two(2)
True
>>> is_power_of_two(4)
True
>>> is_power_of_two(6)
False
>>> is_power_of_two(8)
True
>>> is_power_of_two(17)
False
>>> is_power_of_two(-1)
Traceback (most recent call last):
...
ValueError: number must not be negative
>>> is_power_of_two(1.2)
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for &: 'float' and 'float'
# Test all powers of 2 from 0 to 10,000
>>> all(is_power_of_two(int(2 ** i)) for i in range(10000))
True
"""
if number < 0:
raise ValueError("number must not be negative")
return number & (number - 1) == 0Gray code: recursive reflection builds a sequence where adjacent codes differ by one bit
bit_manipulation/gray_code_sequence.py:50Reflecting an (n-1)-bit Gray code, prepending '0' to the first half and '1' to the reversed copy, ensures adjacent elements differ by exactly one bit including the wrap-around pair.
Gray code is used wherever binary counting would cause multiple bits to change simultaneously, most notably in rotary encoders. The recursive construction exploits the reflection property: take the (n-1)-bit Gray code sequence, prepend '0' to each element of the first half, and prepend '1' to each element of the second half traversed in reverse. The reversal is the key. The last element of the '0' half and the first element of the '1' half are the same (n-1)-bit string, so prepending '0' versus '1' makes them differ by exactly one bit. The wrap-around pair also differs by one bit for the same reason. The outer gray_code function converts the string list to integers with int(s, 2). The doctest for gray_code(2) returns [0, 1, 3, 2], binary 00, 01, 11, 10, confirming each adjacent pair differs by one bit position.
Reflecting the (n-1)-bit sequence and prepending '0' then '1' to the mirrored copy produces a Gray code where every adjacent pair differs in exactly one bit.
def gray_code_sequence_string(bit_count: int) -> list:
"""
Will output the n-bit grey sequence as a
string of bits
>>> gray_code_sequence_string(2)
['00', '01', '11', '10']
>>> gray_code_sequence_string(1)
['0', '1']
"""
# The approach is a recursive one
# Base case achieved when either n = 0 or n=1
if bit_count == 0:
return ["0"]
if bit_count == 1:
return ["0", "1"]
seq_len = 1 << bit_count # defines the length of the sequence
# 1<< n is equivalent to 2^n
# recursive answer will generate answer for n-1 bits
smaller_sequence = gray_code_sequence_string(bit_count - 1)
sequence = []
# append 0 to first half of the smaller sequence generated
for i in range(seq_len // 2):
generated_no = "0" + smaller_sequence[i]
sequence.append(generated_no)
# append 1 to second half ... start from the end of the list
for i in reversed(range(seq_len // 2)):
generated_no = "1" + smaller_sequence[i]
sequence.append(generated_no)
return sequenceYou've walked through 7 key areas of the The Algorithms - Python codebase.
Continue: String Algorithms in TheAlgorithms/Python → Browse all projectsCreate code tours for your project
Intraview lets AI create interactive walkthroughs of any codebase. Install the free VS Code extension and generate your first tour in minutes.
Install Intraview Free