Skip to content

Commit b3a5af2

Browse files
Update count_of_set_bits.cpp
1 parent ca180c5 commit b3a5af2

File tree

1 file changed

+97
-68
lines changed

1 file changed

+97
-68
lines changed
Lines changed: 97 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,120 @@
11
/**
22
* @file
3-
* @brief Implementation to [count number of set bits of a number]
4-
* (https://www.geeksforgeeks.org/count-set-bits-in-an-integer/) in an
5-
* integer.
3+
* @brief Optimized implementation to count number of set bits in an integer.
64
*
75
* @details
8-
* We are given an integer number. We need to calculate the number of set bits
9-
* in it.
6+
* Provides multiple approaches:
7+
* 1. Compiler built-ins (fastest)
8+
* 2. Lookup table method
9+
* 3. Brian Kernighan's algorithm
10+
* 4. Naive bit-by-bit method
1011
*
11-
* A binary number consists of two digits. They are 0 & 1. Digit 1 is known as
12-
* set bit in computer terms.
13-
* Worst Case Time Complexity: O(log n)
14-
* Space complexity: O(1)
15-
* @author [Swastika Gupta](https://github.com/Swastyy)
16-
* @author [Prashant Thakur](https://github.com/prashant-th18)
12+
* Time Complexity:
13+
* - O(1) for built-ins
14+
* - O(log n) for Brian Kernighan
15+
* Space Complexity: O(1)
16+
*
17+
* @author Swastika Gupta, Prashant Thakur
18+
* @author [Contributor] (Optimize set bit counting with compiler built-ins and
19+
* multiple algorithms)(https://github.com/kokatesaurabh)
1720
*/
18-
#include <cassert> /// for assert
21+
22+
#include <cassert>
1923
#include <cstdint>
20-
#include <iostream> /// for IO operations
21-
/**
22-
* @namespace bit_manipulation
23-
* @brief Bit manipulation algorithms
24-
*/
24+
#include <iostream>
25+
#include <vector>
26+
2527
namespace bit_manipulation {
26-
/**
27-
* @namespace count_of_set_bits
28-
* @brief Functions for the [count sets
29-
* bits](https://www.geeksforgeeks.org/count-set-bits-in-an-integer/)
30-
* implementation
31-
*/
3228
namespace count_of_set_bits {
33-
/**
34-
* @brief The main function implements set bit count
35-
* @param n is the number whose set bit will be counted
36-
* @returns total number of set-bits in the binary representation of number `n`
37-
*/
38-
std::uint64_t countSetBits(
39-
std ::uint64_t n) { // uint64_t is preferred over int so that
40-
// no Overflow can be there.
41-
//It's preferred over int64_t because it Guarantees that inputs are always non-negative,
42-
//which matches the algorithmic problem statement.
43-
//set bit counting is conceptually defined only for non-negative numbers.
44-
//Provides a type Safety: Using an unsigned type helps prevent accidental negative values,
4529

46-
std::uint64_t count = 0; // "count" variable is used to count number of set-bits('1')
47-
// in binary representation of number 'n'
48-
//Count is uint64_t because it Prevents theoretical overflow if someone passes very large integers.
49-
// Behavior stays the same for all normal inputs.
50-
// Safer for edge cases.
30+
// Lookup table for 8-bit numbers
31+
static constexpr unsigned char lookup_table[256] = {
32+
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4,
33+
2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
34+
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4,
35+
2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
36+
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,
37+
4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
38+
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5,
39+
3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
40+
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,
41+
4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
42+
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8};
43+
44+
// 1. Compiler built-ins
45+
inline std::uint64_t countSetBitsBuiltin(std::uint64_t n) {
46+
#if defined(__GNUC__) || defined(__clang__)
47+
return __builtin_popcountll(n);
48+
#elif defined(_MSC_VER)
49+
return __popcnt64(n);
50+
#else
51+
// Fallback
52+
std::uint64_t count = 0;
53+
while (n) {
54+
n &= (n - 1);
55+
++count;
56+
}
57+
return count;
58+
#endif
59+
}
60+
61+
// 2. Lookup table method
62+
inline std::uint64_t countSetBitsLookup(std::uint64_t n) {
63+
std::uint64_t count = 0;
64+
for (int i = 0; i < 8; ++i) {
65+
count += lookup_table[(n >> (i * 8)) & 0xFF];
66+
}
67+
return count;
68+
}
5169

52-
while (n != 0) {
70+
// 3. Brian Kernighan's algorithm
71+
inline std::uint64_t countSetBitsKernighan(std::uint64_t n) {
72+
std::uint64_t count = 0;
73+
while (n) {
74+
n &= (n - 1);
5375
++count;
54-
n = (n & (n - 1));
5576
}
5677
return count;
57-
// Why this algorithm is better than the standard one?
58-
// Because this algorithm runs the same number of times as the number of
59-
// set-bits in it. Means if my number is having "3" set bits, then this
60-
// while loop will run only "3" times!!
6178
}
79+
80+
// 4. Naive bit-by-bit
81+
inline std::uint64_t countSetBitsNaive(std::uint64_t n) {
82+
std::uint64_t count = 0;
83+
while (n) {
84+
count += (n & 1);
85+
n >>= 1;
86+
}
87+
return count;
88+
}
89+
90+
// Default function: uses fastest method available
91+
inline std::uint64_t countSetBits(std::uint64_t n) {
92+
return countSetBitsBuiltin(n);
93+
}
94+
6295
} // namespace count_of_set_bits
6396
} // namespace bit_manipulation
6497

98+
// ===================== Test Cases =====================
6599
static void test() {
66-
// n = 4 return 1
67-
assert(bit_manipulation::count_of_set_bits::countSetBits(4) == 1);
68-
// n = 6 return 2
69-
assert(bit_manipulation::count_of_set_bits::countSetBits(6) == 2);
70-
// n = 13 return 3
71-
assert(bit_manipulation::count_of_set_bits::countSetBits(13) == 3);
72-
// n = 9 return 2
73-
assert(bit_manipulation::count_of_set_bits::countSetBits(9) == 2);
74-
// n = 15 return 4
75-
assert(bit_manipulation::count_of_set_bits::countSetBits(15) == 4);
76-
// n = 25 return 3
77-
assert(bit_manipulation::count_of_set_bits::countSetBits(25) == 3);
78-
// n = 97 return 3
79-
assert(bit_manipulation::count_of_set_bits::countSetBits(97) == 3);
80-
// n = 31 return 5
81-
assert(bit_manipulation::count_of_set_bits::countSetBits(31) == 5);
82-
std::cout << "All test cases successfully passed!" << std::endl;
100+
using namespace bit_manipulation::count_of_set_bits;
101+
std::cout << "Running set bits counting tests...\n";
102+
103+
std::vector<std::uint64_t> numbers = {
104+
0, 1, 2, 3, 4, 7, 15, 31, 255, 256, 511, 1023, 123456789, ~0ULL};
105+
106+
for (auto n : numbers) {
107+
auto a = countSetBitsBuiltin(n);
108+
auto b = countSetBitsLookup(n);
109+
auto c = countSetBitsKernighan(n);
110+
auto d = countSetBitsNaive(n);
111+
assert(a == b && b == c && c == d);
112+
}
113+
114+
std::cout << "All tests passed! All methods produce consistent results.\n";
83115
}
84-
/**
85-
* @brief Main function
86-
* @returns 0 on exit
87-
*/
116+
88117
int main() {
89-
test(); // run self-test implementations
118+
test();
90119
return 0;
91120
}

0 commit comments

Comments
 (0)