垂直按位计数(同一位置上的总和)

Vertical bitwise COUNT (sum of ones on the same position)

有什么有效的方法可以在多个变量的相同位置上计算 1 个吗?计数函数应该用相应位数的总和填充数组。例如,我们有以下三个变量(为了简单起见,我使用 8 位变量):

uint8_t a = 0xF;  // 0000 1111
uint8_t b = 0x3C; // 0011 1100
uint8_t c = 0xF0; // 1111 0000

int result[8];

// some operations ...

count << result[0] << result[1] << .... // prints 1122 2211

我找到了很多求和整个单个变量的解决方案,但没有解决上述问题。

确定变量中特定位置是否为 1 比进行正常人口计数要简单得多。

bool hasOneAtPosition(int position, int target) {
    int mask = 1 << position;
    return (target & mask) != 0;
}

... 只需在所有输入上调用它并在每次 returns 为真时增加一个计数器。简单

这段小代码完全符合您的要求。您可以通过一个小查找数组轻松扩展它以支持 N 个变量。注意双非操作的使用。就是拖拽输出为0或者1。

#include <iostream>

using namespace std;

int main() {
    uint8_t a = 0xF;  // 0000 1111
    uint8_t b = 0x3C; // 0011 1100
    uint8_t c = 0xF0; // 1111 0000

    unsigned result[8];
    for(int i = 0; i < 8; ++i) {
        unsigned mask = 1 << i;
        result[i] = !!(a & mask) + !!(b & mask) + !!(c & mask);
    }

    for(int i = 0; i < 8; ++i)
        cout << result[i];
}

将每个 uint8_t 二进制数字扩展为 uint32_t 十六进制数字和 "add them up"。只要不超过15位就好了

#include <stdio.h>
#include <stdint.h>

// See below for tighter code
uint32_t shift_nibble(uint8_t x) {
  uint32_t y = 0;
  uint32_t mask = 1;
  while (x) {
    if (x & 1) {
      y |= mask;
    }
    mask <<= 4;
    x >>= 1;
  }
  return y;
}

void PrintVerticalBitwiseCount(const  uint8_t *x, size_t size) {
  uint32_t y = 0;
  for (size_t i=0; i<size; i++) {
    y += shift_nibble(x[i]);
  }
  printf("%08lX\n", (unsigned long) y);
}

int main(void) {
  const uint8_t a[] = { 0xF, 0x3C,  0xF0 };
  PrintVerticalBitwiseCount(a, sizeof a/sizeof *a);
  return 0;
}

输出

11222211

候选人更快shift_nibble()。戴上你的八进制帽子

uint32_t shift_nibble(uint8_t x) {
  uint32_t y;
  y  = UINT32_C(0x01010101) & (UINT32_C(0001010101) * (x & 0x55));
  y |= UINT32_C(0x10101010) & (UINT32_C(0010101010) * (x & 0xAA));
  return y;
}

作为模板,我建议使用 C++11 中的以下函数。返回的列表在每个位的适当位置都有位计数,也就是说最低有效位计数在位置 0,下一个最重要的位在位置 1 等。 希望这对某人有所帮助。

template<typename T>
std::list<long>
vertical_bit_sum(std::vector<T> items)
{
    size_t bits = sizeof(T) * 8;
    std::list<long> result;
    do
    {
        long count = 0;
        for ( T item : items)
        {
            count += (0x1 & (item >> (bits-1)));
        }

        result.push_front (count);
        --bits;
    }
    while( bits > 0);

    return result;
}

std::list<long> result= vertical_bit_sum<uint8_t>( { 0xF, 0x3C, 0xF0  });

做这样的事情:

uint64_t accum = 0;
uint64_t table[0x100] = {.. precomputed vals ...};
int count = 0xFF;
while(bytes_available()) {
  if(--count == 0) {
    count = 0xFF;
    for(int i = 0; i < 8; i++)
      result[i] = ((uint8_t*)&accum)[i];
    accum = 0;
  }
  accum += table[(uint8_t)get_next_byte()];
}

为了纯粹的速度,您可以通过将 8 个字节打包在一个 64 位累加器中来并行处理 8 个计数。

使用 256 个 64 位条目初始化查找 -table 将原始 8 位扩展为字节。 (例如,条目 Lookup[0x17u] 映射到 0x0000000100010101ul。)

计数只是用

Acc+= Lookup[Byte];

您可以通过在 64 位上映射一个 8 字节的数组来检索各个计数器。

在发生溢出之前,您可以执行256次累加;如果您需要更多,请以 256 个块为单位累加,并在处理完一个块后,将计数传输到更大的累加器。

如果你需要累加不超过16次,32位累加器就足够了。