統計整數二進制表示中 1 的個數(人口計數),最優雅的解法是 n &= (n - 1) 迴圈,每次清除最低位的 1。
#include <stdio.h>
// 計算 1 的個數
int count_ones(int n) {
int count = 0;
while (n) {
count++;
n &= (n - 1); // 清除最低位的 1
}
return count;
}
int main() {
printf("count(10) = %d // 1010 -> 2\n", count_ones(10));
printf("count(0) = %d // 0 -> 0\n", count_ones(0));
printf("count(-1) = %d // 全 1 (32位) -> 32\n", count_ones(-1));
printf("count(255) = %d // 11111111 -> 8\n", count_ones(255));
return 0;
}
n = 10 (1010₂) n-1 = 9 (1001₂) n & (n-1) = 1000₂ (8) ← 最低位 1 被清除 n = 8 (1000₂) n-1 = 7 (0111₂) n & (n-1) = 0000₂ (0) ← 再次清除,結束 總計 2 次迴圈,對應 2 個 1
| 演算法 | 時間複雜度 | 空間複雜度 | 說明 |
|---|---|---|---|
| 逐位檢查 | O(log n) = O(32) | O(1) | 固定 32 次迴圈 |
| 查表法 (8-bit) | O(4) | O(256) | 需預計算表 |
| Brian Kernighan | O(k) | O(1) | k = 1的個數,最優 |
| CPU 內建指令 | O(1) | O(1) | __builtin_popcount/POPCNT |
bool isPowerOfTwo(int n) {
return n > 0 && (n & (n - 1)) == 0;
}
// 只有 1 個 1 時,n & (n-1) 為 0
int hammingDistance(int a, int b) {
return count_ones(a ^ b); // 異或得出不同位,再計數
}
unsigned int reverseBits(unsigned int n) {
unsigned int rev = 0;
for (int i = 0; i < 32; i++) {
rev = (rev << 1) | (n & 1);
n >>= 1;
}
return rev;
}
// GCC/Clang int count = __builtin_popcount(n); // 32-bit int count = __builtin_popcountll(n); // 64-bit // MSVC (intrin.h) #include <intrin.h> int count = __popcnt(n); // 需 POPCNT 指令集支援
vcnt 向量計數指令cpop 擴展指令 (Zbb)n & (n-1) 清除最低位 1 的原理count_ones、isPowerOfTwo、hammingDistance