You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于位掩码的n/2组合计数高效实现方案问询

高效生成恰好n/2个1的位掩码方法

嘿,这个问题我太熟悉了!你当前遍历所有2ⁿ种状态再判断1的位数的方法,在n稍微大一点的时候就会变得极其低效——比如n=20时总状态数超过百万,n=30更是直奔十亿级别,完全不适合实际场景。

其实我们根本不需要遍历所有状态,直接生成恰好包含k个1的位掩码(这里k=n/2)就可以,效率能提升几个数量级。最经典的算法就是Gosper's Hack,它专门用来按顺序生成所有恰好有k个1的二进制数,时间复杂度是O(C(n,k)),也就是组合数的量级,比2ⁿ小太多了。

为什么Gosper's Hack高效?

它通过位运算直接跳转到下一个符合条件的掩码,完全跳过所有1的位数不等于k的状态。每个生成步骤都是O(1)的位运算操作,不需要额外的计数判断——生成的每个掩码天然满足1的位数是k,你的possible()函数直接可以删掉!

算法实现示例(Python)

def query(n):
    k = n // 2
    if k == 0 or k > n:
        return
    
    # 初始化第一个掩码:最低k位全为1
    mask = (1 << k) - 1
    max_mask = 1 << n
    
    while mask < max_mask:
        # 这里处理当前mask,比如你的业务逻辑
        # 示例:打印掩码的二进制形式
        print(f"当前掩码: {bin(mask)[2:].zfill(n)}")
        
        # Gosper's Hack 核心步骤
        c = mask & -mask  # 获取最右边的1的位置(孤立的1)
        r = mask + c      # 把最右边的连续1串进位
        mask = (((r ^ mask) >> 2) // c) | r  # 调整后续1的位置,生成下一个掩码

核心步骤解释

  1. 初始化:(1 << k) - 1生成最低k位全为1的二进制数,这是第一个符合条件的掩码。
  2. 获取最右1:mask & -mask利用补码特性,得到mask最右边的那个1对应的数值(比如mask=0b10110,结果就是0b10)。
  3. 进位操作:mask + c会把最右边的连续1串变成0,并把左边第一个0变成1(比如0b10110 + 0b10 = 0b11000)。
  4. 调整后续1:(r ^ mask) >> 2得到进位后变化的部分,除以c后右移两位,再和r合并,就能把剩下的k-1个1放到最右边的低位,形成下一个符合条件的掩码。

其他可选方法

如果你的场景不需要按顺序生成掩码,也可以用递归回溯的方式生成:每一位决定是否置1,累计置1的数量达到k时就记录当前掩码。不过递归的效率略低于Gosper's Hack,适合代码可读性优先的场景。

举个简单的递归示例:

def recursive_generate(n, k, current_mask=0, pos=0, count=0):
    if count == k:
        print(f"当前掩码: {bin(current_mask)[2:].zfill(n)}")
        return
    if pos >= n:
        return
    # 选择当前位置1
    recursive_generate(n, k, current_mask | (1 << pos), pos + 1, count + 1)
    # 选择当前位置0
    recursive_generate(n, k, current_mask, pos + 1, count)

注意事项

  • 确保n是偶数(因为你要选n/2个元素),否则k会是小数,不符合位掩码的逻辑。
  • 如果n特别大(比如n>40),组合数C(n,n/2)会变得非常巨大(比如n=40时C(40,20)=137846528820),这时候可能需要考虑采样或者分治优化,但常规规模下Gosper's Hack完全够用。

内容的提问来源于stack exchange,提问作者John

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 08:03:53