Skip to content

Commit 98ee074

Browse files
Add set bits count using lookup table method
Introduced get_set_bits_count_using_lookup_table to efficiently count set bits in a 32-bit integer using a precomputed lookup table. Updated benchmark to compare the new method alongside existing algorithms.
1 parent 1562ae1 commit 98ee074

File tree

1 file changed

+47
-2
lines changed

1 file changed

+47
-2
lines changed

bit_manipulation/count_number_of_one_bits.py

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,27 +60,72 @@ def get_set_bits_count_using_modulo_operator(number: int) -> int:
6060
return result
6161

6262

63+
def get_set_bits_count_using_lookup_table(number: int) -> int:
64+
"""
65+
Count the number of set bits in a 32-bit integer using a precomputed lookup table.
66+
>>> get_set_bits_count_using_lookup_table(25)
67+
3
68+
>>> get_set_bits_count_using_lookup_table(37)
69+
3
70+
>>> get_set_bits_count_using_lookup_table(21)
71+
3
72+
>>> get_set_bits_count_using_lookup_table(58)
73+
4
74+
>>> get_set_bits_count_using_lookup_table(0)
75+
0
76+
>>> get_set_bits_count_using_lookup_table(256)
77+
1
78+
>>> get_set_bits_count_using_lookup_table(-1)
79+
Traceback (most recent call last):
80+
...
81+
ValueError: the value of input must not be negative
82+
"""
83+
_LOOKUP_TABLE = [bin(i).count("1") for i in range(256)]
84+
85+
if number < 0:
86+
raise ValueError("the value of input must not be negative")
87+
88+
# Split 32-bit number into four 8-bit chunks and use lookup table
89+
return (
90+
_LOOKUP_TABLE[number & 0xFF]
91+
+ _LOOKUP_TABLE[(number >> 8) & 0xFF]
92+
+ _LOOKUP_TABLE[(number >> 16) & 0xFF]
93+
+ _LOOKUP_TABLE[(number >> 24) & 0xFF]
94+
)
95+
96+
97+
6398
def benchmark() -> None:
6499
"""
65-
Benchmark code for comparing 2 functions, with different length int values.
66-
Brian Kernighan's algorithm is consistently faster than using modulo_operator.
100+
Benchmark code for comparing 3 functions, with different length int values.
101+
Brian Kernighan's algorithm is consistently faster than using modulo_operator,
102+
and the lookup table method is often the fastest for repeated calls.
67103
"""
68104

69105
def do_benchmark(number: int) -> None:
70106
setup = "import __main__ as z"
71107
print(f"Benchmark when {number = }:")
108+
72109
print(f"{get_set_bits_count_using_modulo_operator(number) = }")
73110
timing = timeit(
74111
f"z.get_set_bits_count_using_modulo_operator({number})", setup=setup
75112
)
76113
print(f"timeit() runs in {timing} seconds")
114+
77115
print(f"{get_set_bits_count_using_brian_kernighans_algorithm(number) = }")
78116
timing = timeit(
79117
f"z.get_set_bits_count_using_brian_kernighans_algorithm({number})",
80118
setup=setup,
81119
)
82120
print(f"timeit() runs in {timing} seconds")
83121

122+
print(f"{get_set_bits_count_using_lookup_table(number) = }")
123+
timing = timeit(
124+
f"z.get_set_bits_count_using_lookup_table({number})",
125+
setup=setup,
126+
)
127+
print(f"timeit() runs in {timing} seconds")
128+
84129
for number in (25, 37, 58, 0):
85130
do_benchmark(number)
86131
print()

0 commit comments

Comments
 (0)