C++ 使用位集获取数组项的条件和,而不使用循环

C++ 使用位集获取数组项的条件和,而不使用循环,c++,arrays,algorithm,sum,bitset,C++,Arrays,Algorithm,Sum,Bitset,有一个位集b和一个数组arr。我想通过使用位集获得数组的和,这样只有设置了b[I]时才能对arr[I]求和 例如: bitset<4> b("1001"); arr[4] = {5,4,3,1}; ===> the sum would be 1+5 = 6. 位集b(“1001”); arr[4]={5,4,3,1}; =>总和为1+5=6。 显然,我可以使用一个循环。但这种计算会重复多次。有没有一种更有效的没有循环的方法 最快的方法可能是迭代一个循环,并根据当前位的

有一个位集
b
和一个数组
arr
。我想通过使用位集获得数组的和,这样只有设置了
b[I]
时才能对
arr[I]
求和

例如:

bitset<4> b("1001"); 
arr[4] = {5,4,3,1};

===> the sum would be 1+5 = 6. 
位集b(“1001”);
arr[4]={5,4,3,1};
=>总和为1+5=6。

显然,我可以使用一个循环。但这种计算会重复多次。有没有一种更有效的没有循环的方法

最快的方法可能是迭代一个循环,并根据当前位的设置进行添加

也许使用位移位而不是直接访问位n可以避免标准库提取特定位的一些隐藏操作,但这还需要通过基准测试来证明:

int sum=0;
for (size_t i=b.size(); i; i--, b>>=1) 
    if (b[0])
        sum+=arr[i-1];
为了完整性和乐趣,如果您可以使用
向量而不是
位集,您可以使用标准库:

vector<bool> b{1,0,0,1}; 
int arr[4] = {5,4,3,1};
int test=inner_product(begin(arr), end(arr), b.begin(), 0); 
向量b{1,0,0,1};
int-arr[4]={5,4,3,1};
int测试=内部产品(开始(arr),结束(arr),b.开始(),0);


编辑:附加信息-信任您的优化编译器 我使用GCC7.2 for x86-64平台在godbolt在线编译器上进行了实验,以查看由上述不同变体生成的程序集

最简单的解决方案是在循环中迭代并有条件地添加项,速度非常快:

  • 对于示例中的常量值,编译器可以通过常量传播立即确定
    6
    的结果(请参阅)
  • 如果您的数据结构较小,编译器将自动展开循环,使其变得快速(),而无需维护任何循环索引
  • 但是,对于较大的大小,优化器可能会选择不展开循环。在我的实验中,多达17个项目的循环被展开,有18个元素(a)
位移位方案非常接近:

  • 最多17项,循环展开,生成的代码与简单循环完全相同
  • 从18个元素开始,将生成一个循环,每个迭代都有一个循环。这似乎有点快,但只有一个基准才能真正发挥作用,可能在纳秒范围内
当然,
内积
要重得多,因为它确实要进行乘法,这需要将布尔值转换为整数,并且它必须处理布尔向量的动态大小

有没有一种更有效的没有循环的方法

常见的for-loop通常有三个效率低下的地方,很容易被忽略:步幅增加、循环结束测试和循环结束跳跃。然而,这些都很小,所以不要抱太大的希望

您可以轻松地“展开”循环以获得非常小的收益,代价是增加代码行(以空间换取性能)。随着位集和数组的增大,该方案变得单调乏味

std::bitset<4> b     (std::string("1001"));
std::array<int, 4> a {{5,4,3,1}};

// loop form
int sum = 0;               
for (uint i=0; i < 4; ++i) // stride increment, test end-of-loop
{
   if (b[i]) sum += a[i];
}                          // end-of-loop jump
std::cout << "\n  sum: " << sum << std::endl;


// unrolled loop form -- no stride increment, 
//                       no end-of-loop test, and
//                       no end-of-loop jump
sum = 0;
if (b[0]) sum += a[0];    
if (b[1]) sum += a[1];
if (b[2]) sum += a[2];
if (b[3]) sum += a[3];
// bigger array, more lines ...

std::cout << "\n  sum: " << sum << std::endl;
std::位集b(std::string(“1001”);
std::数组a{5,4,3,1};
//循环形式
整数和=0;
对于(uint i=0;i<4;++i)//步幅增量,循环测试结束
{
如果(b[i])和+=a[i];
}//循环结束跳转

std::不欢迎使用堆栈溢出!StackOverflow期待您的加入,我们也会。请更新您的问题,以显示您已在某个应用程序中尝试过的内容。有关更多信息,请参阅,并选择:)您将需要一个循环来添加多个位。循环的复杂程度取决于您的实现。对于您想要的内容,没有二进制操作。更正:您可能不需要循环,但操作需要重复。嗨,托马斯,谢谢您的回答。然而,我仍然感到困惑。你所说的“操作需要重复”是什么意思?许多现代编译器允许使用push和popping pragma指令,例如,专门指定要展开的给定(简单)循环,以防您想求助于这种优化。可能值得一提的是,我们不必改变
b
;在本例中,位集和数组的大小相同,因此我们可以使用
i
iterate(以及一些简单的索引映射逻辑)检查相应的位。