5分钟学会微算法——Brian Kernighan 算法

Brian Kernighan 算法是一种高效计算二进制数中 1 的个数 (即 "汉明重量 / Hamming Weight")的经典算法,由计算机科学家 Brian Kernighan(C 语言设计者之一)提出。其核心优势是循环次数仅等于二进制中 1 的个数,比 "逐位遍历所有二进制位" 的暴力法更高效。

算法核心原理

Brian Kernighan 算法的核心依赖一个关键的位运算性质:对对于任意整数 x,令 x=x & (x−1),该运算将 x 的二进制表示的最后一个 1 变成 0,并保持其他位不变。

为什么 n & (n - 1) 能消除最右边的 1?

从二进制减法的逻辑可解释:

  1. n 的二进制末尾是 k 个 0(例如 n = 12,二进制为 1100,末尾 2 个 0):
    • n - 1 会将末尾的 k 个 0 全部变为 1,且将最右边的那个 1 变为 0(例如 12 - 1 = 11,二进制为 10111011 & 1100 = 1000)。
  2. n 的二进制末尾是 1(例如 n = 13,二进制为 1101):
    • n - 1 直接将末尾的 1 变为 0(例如 13 - 1 = 12,二进制为 1100,1100 & 1101 = 1100)。

此时对 nn - 1 做 "按位与(&)" 运算,由于最右边的 1 已被 n - 1 翻转为 0,且其右侧的位全为 1(与 n 的右侧 0 对应),最终结果会精准消除 n 最右边的 1。

示例 :以 n = 13(二进制 1101)为例

  • 第 1 次:n = 13 (1101)n-1 = 12 (1100)n & (n-1) = 12 (1100) → 消除最右边的 1(末尾的 1),计数 + 1。
  • 第 2 次:n = 12 (1100)n-1 = 11 (1011)n & (n-1) = 8 (1000) → 消除最右边的 1(第 3 位的 1),计数 + 1。
  • 第 3 次:n = 8 (1000)n-1 = 7 (0111)n & (n-1) = 0 (0000) → 消除最右边的 1(第 4 位的 1),计数 + 1。
  • 此时 n = 0,循环结束,最终计数为 3(13 的二进制确实有 3 个 1)。

算法流程

  1. 初始化计数 :设置一个计数器 count = 0,用于记录 1 的个数。

  2. 循环消除 1 :只要 n != 0,就执行以下操作:

    • 计算 n = n & (n - 1),消除 n 最右边的 1。
    • 计数器 count += 1(每消除一个 1,计数加 1)。
  3. 返回结果 :当 n 变为 0 时,count 即为二进制中 1 的总个数。

    cpp 复制代码
    int countOneBits(int n) {
        int count = 0;
        while (n) {          // 当 n 不为 0 时循环
            n &= (n - 1);    // 消除最右边的 1
            count++;         // 计数加 1
        }
        return count;
    }

简单推理

对于(n = 1,2,3,......)都是最高位是1,其余位都是0的,所以如果 i & (i - 1) = 0,则i是2的整数次幂。

方法开拓

如果想要线性的求n个数的二进制1的个数,可以采用动态规划的方法。

比如对于二进制数1010,可以看做1010 = 1000 + 10,我设x=1010,y=1000,z=10,其中z=x-y。

而且不难发现z的二进制中1的个数=y的二进制中1的个数+z的二进制中1的个数,而y是2的n次幂的类型,所有bit1[x] = bit1[x - y] + 1 =bit1[z] + 1;//这里的bit1代表二进制中1的个数。

leetcode例题

cpp 复制代码
class Solution {
public:
    vector<int> countBits(int n) {
        int level = 0;
        vector<int> ans(n+1);
        ans[0] = 0;
        for(int i = 1;i <= n;++i){
            if(!(i & (i - 1))){
                level = i;
            }
            ans[i] = ans[i - level] + 1;
        }
        return ans;
    }
};
相关推荐
不穿格子的程序员几秒前
从零开始刷算法——贪心篇1:跳跃游戏1 + 跳跃游戏2
算法·游戏·贪心
大江东去浪淘尽千古风流人物2 分钟前
【SLAM新范式】几何主导=》几何+学习+语义+高效表示的融合
深度学习·算法·slam
重生之我是Java开发战士17 分钟前
【优选算法】模拟算法:替换所有的问号,提莫攻击,N字形变换,外观数列,数青蛙
算法
仟濹22 分钟前
算法打卡 day1 (2026-02-06 周四) | 算法: DFS | 1_卡码网98 可达路径 | 2_力扣797_所有可能的路径
算法·leetcode·深度优先
yang)23 分钟前
欠采样时的相位倒置问题
算法
历程里程碑26 分钟前
Linux20 : IO
linux·c语言·开发语言·数据结构·c++·算法
A尘埃28 分钟前
物流公司配送路径动态优化(Q-Learning算法)
算法
天若有情67329 分钟前
【自研实战】轻量级ASCII字符串加密算法:从设计到落地(防查岗神器版)
网络·c++·算法·安全·数据安全·加密
啊森要自信1 小时前
CANN ops-cv:AI 硬件端视觉算法推理训练的算子性能调优与实战应用详解
人工智能·算法·cann
仟濹1 小时前
算法打卡day2 (2026-02-07 周五) | 算法: DFS | 3_卡码网99_计数孤岛_DFS
算法·深度优先