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

基于std::bitset的异或最优解问题:效率与简洁实现探讨

关于std::bitset在N位序列异或求最小1个数的问题

需求与现有代码

需要找出一个N位(N最大为50)的二进制序列(用std::bitset<N>存储),与一系列前缀全1序列(从0b0、0b1、0b11,直到与目标序列最高位对齐的全1序列)异或后,结果中1的个数的最小值。

现有实现代码如下:

#include <bitset>
#include <vector>
#include <algorithm>
#include <iostream>
#include <bit>

constexpr std::size_t N = 8;

std::vector<int> countPenalty(std::bitset<N> set)
{
    std::bitset<N> closing{0b0};
    std::bitset<N> eins{0b1};
    std::vector<int> countPen; 
    countPen.reserve(N);

    while(std::bit_width(closing.to_ulong()) <= std::bit_width(set.to_ulong()))
    {  
        std::bitset<N> tempSet = set ^ closing; 
        countPen.push_back(std::popcount(tempSet.to_ulong()));
        closing <<= 1;
        closing |= eins;    
    }
     return countPen;
}

int main()
{
   std::string s{"-11--111"};
   std::bitset<N> visitorSet(s, 0, s.size(), '0', '1');
   std::vector<int> penalties = countPenalty(visitorSet);
   auto minRes = std::min_element(penalties.begin(), penalties.end());
   std::cout << *minRes << std::endl; // 输出2
   return 0;
}

实际业务场景

该问题对应售票柜台的最优闭店策略:

  • 两类损失:柜台营业但无顾客、柜台关闭但有顾客
  • 每日时段顾客记录用二进制表示(1=有顾客,0=无顾客),闭店记录用二进制表示(1=闭店,0=营业)
  • 顾客记录与闭店记录的异或结果中,1的个数即为总损失次数,目标是找到损失最小的闭店方案。

核心疑问解答

1. 大尺寸std::bitset效率不高的原因

  • 类型转换开销:现有代码频繁调用to_ulong()将bitset转换为unsigned long,当N接近平台字长(比如50位接近64位),转换需要拼接内部存储的多个字,带来额外开销;若N超过64位,to_ulong()会直接溢出或抛出异常,存在隐患。
  • 编译期固定大小限制:std::bitset的大小N是编译期常量,内部用数组存储位数据,当N不是平台字长的整数倍时,位运算需要额外的边界掩码处理,相比原生整数位运算有冗余操作。
  • API适配问题:std::popcount等C++20位操作函数对原生整数更友好,bitset需要先转换才能使用,间接增加了开销。

2. 更简洁高效的实现方式

针对N最大50的场景,用64位整数(uint64_t)直接处理是最优选择,避免bitset的类型转换和模板限制:

#include <vector>
#include <bit>
#include <algorithm>
#include <iostream>

std::vector<int> countPenalty(uint64_t set) {
    std::vector<int> countPen;
    const auto max_bits = std::bit_width(set);
    countPen.reserve(max_bits + 1); // 包含0个1的情况
    
    uint64_t closing = 0;
    do {
        countPen.push_back(std::popcount(set ^ closing));
        closing = (closing << 1) | 1;
    } while (std::bit_width(closing) <= max_bits);
    
    return countPen;
}

int main() {
    // 把字符串转换为uint64_t,处理占位符'-'为0
    std::string s{"-11--111"};
    uint64_t visitorSet = 0;
    for (char c : s) {
        visitorSet <<= 1;
        if (c == '1') visitorSet |= 1;
        // '-'视为0,不做额外操作
    }
    
    std::vector<int> penalties = countPenalty(visitorSet);
    auto minRes = std::min_element(penalties.begin(), penalties.end());
    std::cout << *minRes << std::endl; // 输出2
    return 0;
}

如果坚持用std::bitset,也可以优化掉类型转换,直接用bitset自带的count()方法统计1的个数:

template<std::size_t N>
std::vector<int> countPenalty(const std::bitset<N>& set) {
    std::vector<int> countPen;
    const auto max_bits = std::bit_width(static_cast<uint64_t>(set.to_ullong()));
    countPen.reserve(max_bits + 1);
    
    std::bitset<N> closing;
    do {
        countPen.push_back((set ^ closing).count());
        closing = (closing << 1) | 1;
    } while (std::bit_width(static_cast<uint64_t>(closing.to_ullong())) <= max_bits);
    
    return countPen;
}

3. C++20环境下std::bitset是否仍合适

  • 若N是编译期固定值且不超过64位,std::bitset可以使用,但效率不如原生64位整数;如果需要编译期计算,std::bitset的constexpr API(C++20支持count()、位运算等constexpr操作)有一定优势。
  • 若N可能超过64位,std::bitset的类型转换会出现溢出问题,此时更适合用std::vector<bool>或动态位集库;但本场景N最大50,64位整数完全覆盖,原生整数实现更简洁高效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 07:56:02