Skip to content
53 changes: 51 additions & 2 deletions bit_manipulation/count_number_of_one_bits.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,27 +60,76 @@ def get_set_bits_count_using_modulo_operator(number: int) -> int:
return result


def get_set_bits_count_using_lookup_table(number: int) -> int:
"""
Count the number of set bits in a 32-bit integer using a precomputed lookup table.

I see similar approach in GeeksforGeeks, but the implementation is different.
Link to Code:
https://www.geeksforgeeks.org/dsa/count-set-bits-integer-using-lookup-table/

>>> get_set_bits_count_using_lookup_table(25)
3
>>> get_set_bits_count_using_lookup_table(37)
3
>>> get_set_bits_count_using_lookup_table(21)
3
>>> get_set_bits_count_using_lookup_table(58)
4
>>> get_set_bits_count_using_lookup_table(0)
0
>>> get_set_bits_count_using_lookup_table(256)
1
>>> get_set_bits_count_using_lookup_table(-1)
Traceback (most recent call last):
...
ValueError: the value of input must not be negative
"""
_lookup_table = [bin(i).count("1") for i in range(256)]

if number < 0:
raise ValueError("the value of input must not be negative")

# Split 32-bit number into four 8-bit chunks and use lookup table
return (
_lookup_table[number & 0xFF]
+ _lookup_table[(number >> 8) & 0xFF]
+ _lookup_table[(number >> 16) & 0xFF]
+ _lookup_table[(number >> 24) & 0xFF]
)


def benchmark() -> None:
"""
Benchmark code for comparing 2 functions, with different length int values.
Brian Kernighan's algorithm is consistently faster than using modulo_operator.
Benchmark code for comparing 3 functions, with different length int values.
Brian Kernighan's algorithm is consistently faster than using modulo_operator,
and the lookup table method is often the fastest for repeated calls.
"""

def do_benchmark(number: int) -> None:
setup = "import __main__ as z"
print(f"Benchmark when {number = }:")

print(f"{get_set_bits_count_using_modulo_operator(number) = }")
timing = timeit(
f"z.get_set_bits_count_using_modulo_operator({number})", setup=setup
)
print(f"timeit() runs in {timing} seconds")

print(f"{get_set_bits_count_using_brian_kernighans_algorithm(number) = }")
timing = timeit(
f"z.get_set_bits_count_using_brian_kernighans_algorithm({number})",
setup=setup,
)
print(f"timeit() runs in {timing} seconds")

print(f"{get_set_bits_count_using_lookup_table(number) = }")
timing = timeit(
f"z.get_set_bits_count_using_lookup_table({number})",
setup=setup,
)
print(f"timeit() runs in {timing} seconds")

for number in (25, 37, 58, 0):
do_benchmark(number)
print()
Expand Down