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

如何高效枚举汉明重量≤k的受限格雷码?

受限格雷码(汉明重量≤k的n位二进制串)实现方案

针对你需要的可快速计算、满足汉明重量限制的格雷码枚举需求,以下是基于递归构造的高效实现方案,完全规避了逐个判断popcount的低效操作:

核心思路

通过递归构造符合要求的受限格雷码序列:

  • 序列中所有元素的汉明重量≤k
  • 相邻元素的汉明距离为1(符合格雷码核心特性)
  • 支持直接计算第m个元素,也可从当前元素快速推导下一个元素

递归构造逻辑:

n位、汉明重量≤k的受限格雷码 = [最高位为0 + n-1位、重量≤k的受限格雷码] + [最高位为1 + 反转后的n-1位、重量≤k-1的受限格雷码]

这种构造方式天然保证相邻元素汉明距离为1,且所有元素重量都符合限制。

具体实现

1. 组合数与前缀和计算

首先需要计算组合数C(n,k)及前缀和sum_{i=0}^w C(n,i),用于定位元素在序列中的位置:

#include <stdint.h>

// 计算组合数C(n,k),n≤50时无64位溢出
uint64_t comb(int n, int k) {
    if (k < 0 || k > n) return 0;
    if (k == 0 || k == n) return 1;
    k = k < n - k ? k : n - k; // 取较小值优化计算
    uint64_t res = 1;
    for (int i = 1; i <= k; ++i) {
        res = res * (n - k + i) / i;
    }
    return res;
}

// 计算前缀和:sum_{i=0}^w C(n,i)
uint64_t prefix_sum(int n, int w) {
    uint64_t sum = 0;
    for (int i = 0; i <= w; ++i) {
        sum += comb(n, i);
    }
    return sum;
}

2. 直接计算第m个受限格雷码

类似经典格雷码的i ^ (i >> 1),可直接生成序列中第m个元素(m从0开始):

// 递归计算第m个受限格雷码(n位,重量≤k)
uint64_t restricted_gray_code(int n, int k, uint64_t m) {
    if (n == 0) return 0;
    if (k == 0) return 0; // 仅返回全0串
    
    uint64_t count0 = prefix_sum(n-1, k); // 最高位为0的元素总数
    if (m < count0) {
        // 最高位为0,递归计算n-1位的第m个元素
        return restricted_gray_code(n-1, k, m);
    } else {
        uint64_t count1 = prefix_sum(n-1, k-1); // 最高位为1的元素总数
        uint64_t idx_in_original = count1 - 1 - (m - count0);
        // 最高位为1,递归计算反转后n-1位的对应元素
        return (1ULL << (n-1)) | restricted_gray_code(n-1, k-1, idx_in_original);
    }
}

3. 从当前元素快速获取下一个元素

通过反向查找当前元素的位置m,直接生成m+1对应的元素:

// 查找元素x在S(n,k)序列中的位置m
uint64_t find_position(int n, int k, uint64_t x) {
    if (n == 0) return 0;
    if (k == 0) return 0;
    
    uint64_t msb = (x >> (n-1)) & 1;
    uint64_t count0 = prefix_sum(n-1, k);
    if (msb == 0) {
        // 最高位为0,递归查找子串位置
        return find_position(n-1, k, x & ((1ULL << (n-1)) - 1));
    } else {
        uint64_t count1 = prefix_sum(n-1, k-1);
        uint64_t sub_x = x & ((1ULL << (n-1)) - 1);
        uint64_t sub_pos = find_position(n-1, k-1, sub_x);
        // 最高位为1,计算反转后的位置
        return count0 + (count1 - 1 - sub_pos);
    }
}

// 获取当前元素x的下一个受限格雷码(循环序列)
uint64_t next_restricted_gray(int n, int k, uint64_t x) {
    uint64_t total = prefix_sum(n, k);
    uint64_t m = find_position(n, k, x);
    if (m == total - 1) {
        return restricted_gray_code(n, k, 0); // 最后一个元素的下一个是第一个
    } else {
        return restricted_gray_code(n, k, m+1);
    }
}

验证示例(n=4, k=2)

按上述方法生成的序列为:

0000 (m=0)
0001 (m=1)
0011 (m=2)
0010 (m=3)
0110 (m=4)
0101 (m=5)
0100 (m=6)
1100 (m=7)
1010 (m=8)
1001 (m=9)
1000 (m=10)

完全匹配你给出的示例,且所有元素汉明重量≤2,相邻元素汉明距离为1。

性能优势

对于n=45-50、k=15-18的场景:

  • 无需遍历所有2n个元素,仅处理符合重量限制的约1013量级元素
  • 每个元素的计算/查找操作复杂度为O(n),远低于逐个判断popcount的开销
  • 递归逻辑可被编译器优化为循环,避免栈溢出问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 06:48:26