简体   繁体   English

按时间复杂度 O(1) C++ 代码计算 Byte 中 1 的位数

[英]Counting bits of ones in Byte by time Complexity O(1) C++ code

I've searched an algorithm that counts the number of ones in Byte by time complexity of O(1) and what I found in google:我搜索了一种算法,该算法通过 O(1) 的时间复杂度以及我在谷歌中找到的内容来计算 Byte 中的个数:

// C++ implementation of the approach  
#include <bits/stdc++.h> 
using namespace std; 
  
int BitsSetTable256[256]; 
  
// Function to initialise the lookup table  
void initialize()  
{  
  
    // To initially generate the  
    // table algorithmically  
    BitsSetTable256[0] = 0;  
    for (int i = 0; i < 256; i++) 
    {  
        BitsSetTable256[i] = (i & 1) +  
        BitsSetTable256[i / 2];  
    }  
}  
  
// Function to return the count  
// of set bits in n  
int countSetBits(int n)  
{  
    return (BitsSetTable256[n & 0xff] +  
            BitsSetTable256[(n >> 8) & 0xff] +  
            BitsSetTable256[(n >> 16) & 0xff] +  
            BitsSetTable256[n >> 24]);  
}  
  
// Driver code  
int main()  
{  
    // Initialise the lookup table  
    initialize();  
    int n = 9;  
    cout << countSetBits(n); 
}  
  

I understand what I need 256 size of the array (in other words size of the look up table) for indexing from 0 to 255 which they are all the decimals value that Byte represents !我明白我需要 256 大小的数组(换句话说,查找表的大小)从 0 到 255 的索引,它们都是 Byte 表示的小数值!

but in the function initialize I didn't understand the terms inside the for loop: BitsSetTable256[i] = (i & 1) + BitsSetTable256[i / 2];但在函数 initialize 我不明白 for 循环中的术语:BitsSetTable256[i] = (i & 1) + BitsSetTable256[i / 2];
Why Im doing that?!为什么我这样做?! I didn't understand what's the purpose of this row code inside the for loop.我不明白 for 循环中此行代码的目的是什么。

In addition , in the function countSetBits , this function returns:此外,在函数 countSetBits 中,该函数返回:

 return (BitsSetTable256[n & 0xff] +  
            BitsSetTable256[(n >> 8) & 0xff] +  
            BitsSetTable256[(n >> 16) & 0xff] +  
            BitsSetTable256[n >> 24]); 

I didn't understand at all what Im doing and bitwise with 0xff and why Im doing right shift .. may please anyone explain to me the concept?!我完全不明白我在做什么和 0xff 按位以及为什么我做右移.. 请有人向我解释这个概念吗?! I didn't understand at all why in function countSetBits at BitsSetTable256[n >> 24] we didn't do and wise by 0xff ?我完全不明白为什么在 BitsSetTable256[n >> 24] 的函数 countSetBits 中我们没有做到 0xff 明智?

I understand why I need the lookup table with size 2^8 , but the other code rows that I mentioned above didn't understand, could anyone please explain them to me in simple words?我明白为什么我需要大小为 2^8 的查找表,但是我上面提到的其他代码行不明白,谁能用简单的话向我解释一下? and what's purpose for counting the number of ones in Byte?以及计算 Byte 中的个数的目的是什么?

thanks alot guys!非常感谢你们!

Concerning the first part of question:关于问题的第一部分:

// Function to initialise the lookup table  
void initialize()  
{  
  
    // To initially generate the  
    // table algorithmically  
    BitsSetTable256[0] = 0;  
    for (int i = 0; i < 256; i++) 
    {  
        BitsSetTable256[i] = (i & 1) +  
        BitsSetTable256[i / 2];  
    }  
}  

This is a neat kind of recursion.这是一种简洁的递归。 (Please, note I don't mean "recursive function" but recursion in a more mathematical sense.) (请注意,我的意思不是“递归函数”,而是更数学意义上的递归。)

The seed is BitsSetTable256[0] = 0;种子是BitsSetTable256[0] = 0;

Then every element is initialized using the (already existing) result for i / 2 and adds 1 or 0 for this.然后使用i / 2的(已经存在的)结果初始化每个元素,并为此添加 1 或 0。 Thereby,从而,

  • 1 is added if the last bit of index i is 1如果索引i的最后一位为 1,则加 1
  • 0 is added if the last bit of index i is 0.如果索引i的最后一位为 0,则添加 0。

To get the value of last bit of i , i & 1 is the usual C/C++ bit mask trick.要获得i的最后一位的值, i & 1是通常的 C/C++ 位掩码技巧。

Why is the result of BitsSetTable256[i / 2] a value to built upon?为什么BitsSetTable256[i / 2]的结果是一个要建立的值? The result of BitsSetTable256[i / 2] is the number of all bits of i the last one excluded. BitsSetTable256[i / 2]i的最后一位排除的所有位数。

Please, note that i / 2 and i >> 1 (the value (or bits) shifted to right by 1 whereby the least/last bit is dropped) are equivalent expressions (for positive numbers in the resp. range – edge cases excluded).请注意, i / 2i >> 1 (值(或位)右移 1 从而删除最小/最后一位)是等效表达式(对于相应范围内的正数 - 排除边缘情况) .


Concerning the other part of the question:关于问题的另一部分:

    return (BitsSetTable256[n & 0xff] +  
            BitsSetTable256[(n >> 8) & 0xff] +  
            BitsSetTable256[(n >> 16) & 0xff] +  
            BitsSetTable256[n >> 24]);
  • n & 0xff masks out the upper bits isolating the lower 8 bits. n & 0xff屏蔽了隔离低 8 位的高位。
  • (n >> 8) & 0xff shifts the value of n 8 bits to right (whereby the 8 least bits are dropped) and then again masks out the upper bits isolating the lower 8 bits. (n >> 8) & 0xffn 8 位的值右移(从而丢弃最少的 8 位),然后再次屏蔽隔离低 8 位的高位。
  • (n >> 16) & 0xff shifts the value of n 16 bits to right (whereby the 16 least bits are dropped) and then again masks out the upper bits isolating the lower 8 bits. (n >> 16) & 0xffn 16 位的值右移(从而丢弃最少的 16 位),然后再次屏蔽隔离低 8 位的高位。
  • (n >> 24) & 0xff shifts the value of n 24 bits to right (whereby the 24 least bits are dropped) which should make effectively the upper 8 bits the lower 8 bits. (n >> 24) & 0xffn 24 位的值右移(从而丢弃最少的 24 位),这应该有效地使高 8 位成为低 8 位。

Assuming that int and unsigned have usually 32 bits on nowadays common platforms this covers all bits of n .假设intunsigned在当今常见的平台上通常有 32 位,这涵盖了n所有位。

Please, note that the right shift of a negative value is implementation-defined.请注意,负值的右移是实现定义的。

(I recalled Bitwise shift operators to be sure.) (我肯定记得按位移位运算符。)

So, a right-shift of a negative value may fill all upper bits with 1s.因此,负值的右移可能会用 1 填充所有高位。
That can break BitsSetTable256[n >> 24] resulting in (n >> 24) > 256这可以打破BitsSetTable256[n >> 24]导致(n >> 24) > 256
and hence BitsSetTable256[n >> 24] an out of bound access.因此BitsSetTable256[n >> 24]BitsSetTable256[n >> 24]访问。

The better solution would've been:更好的解决方案是:

    return (BitsSetTable256[n & 0xff] +  
            BitsSetTable256[(n >> 8) & 0xff] +  
            BitsSetTable256[(n >> 16) & 0xff] +  
            BitsSetTable256[(n >> 24) & 0xff]);
BitsSetTable256[0] = 0;
...
BitsSetTable256[i] = (i & 1) +  
     BitsSetTable256[i / 2];  

The above code seeds the look-up table where each index contains the number of ones for the number used as index and works as:上面的代码为查找表提供种子,其中每个索引包含用作索引的数字的个数,其工作方式如下:

  • (i & 1) gives 1 for odd numbers, otherwise 0. (i & 1)为奇数给出 1,否则为 0。
  • An even number will have as many binary 1 as that number divided by 2.偶数将具有与该数字除以 2 一样多的二进制1
  • An odd number will have one more binary 1 than that number divided by 2.一个奇数将比该数字除以 2 多一个二进制1

Examples:例子:

  • if i==8 (1000b) then (i & 1) + BitsSetTable256[i / 2] ->如果i==8 (1000b) 那么(i & 1) + BitsSetTable256[i / 2] ->
    0 + BitsSetTable256[8 / 2] = 0 + index 4 (0100b) = 0 + 1 . 0 + BitsSetTable256[8 / 2] = 0 + 索引 4 (0100b) = 0 + 1 。
  • if i==7 (0111b) then 1 + BitsSetTable256[7 / 2] = 1 + BitsSetTable256[3] = 1 + index 3 (0011b) = 1 + 2.如果i==7 (0111b) 那么1 + BitsSetTable256[7 / 2] = 1 + BitsSetTable256[3] = 1 + index 3 (0011b) = 1 + 2。

If you want some formal mathematical proof why this is so, then I'm not the right person to ask, I'd poke one of the math sites for that.如果你想要一些正式的数学证明为什么会这样,那么我不是合适的人,我会为此戳一个数学网站。


As for the shift part, it's just the normal way of splitting up a 32 bit value in 4x8, portably without care about endianess (any other method to do that is highly questionable).至于移位部分,这只是在 4x8 中拆分 32 位值的正常方式,可移植而不关心字节序(任何其他方法都非常值得怀疑)。 If we un-sloppify the code, we get this:如果我们取消对代码的处理,我们会得到:

BitsSetTable256[(n >>  0) & 0xFFu] +  
BitsSetTable256[(n >>  8) & 0xFFu] +  
BitsSetTable256[(n >> 16) & 0xFFu] +  
BitsSetTable256[(n >> 24) & 0xFFu] ;  

Each byte is shifted into the LS byte position, then masked out with a & 0xFFu byte mask.每个字节都移入 LS 字节位置,然后用& 0xFFu字节掩码屏蔽掉。

Using bit shifts on int is however code smell and potentially buggy.然而,在int上使用位移是代码异味和潜在的错误。 To avoid poorly-defined behavior, you need to change the function to this:为避免定义不明确的行为,您需要将函数更改为:

#include <stdint.h>

uint32_t countSetBits (uint32_t n);

The code in countSetBits takes an int as an argument; countSetBits的代码以int作为参数; apparently 32 bits are assumed.显然假设是 32 位。 The implementation there is extracting four single bytes from n by shifting and masking;那里的实现是通过移位和屏蔽从n提取四个单个字节; for these four separated bytes, the lookup is used and the number of bits per byte there are added to yield the result.对于这四个分隔的字节,使用查找并添加每个字节的位数以产生结果。

The initialization of the lookup table is a bit more tricky and can be seen as a form of dynamic programming.查找表的初始化有点棘手,可以看作是动态规划的一种形式。 The entries are filled in increasing index of the argument.这些条目填充在参数的递增索引中。 The first expression masks out the least significant bit and counts it;第一个表达式屏蔽了最低有效位并对其进行计数; the second expression halves the argument (which could be also done by shifting).第二个表达式将参数减半(也可以通过移位来完成)。 The resulting argument is smaller;结果参数较小; it is then correctly assumed that the necessary value for the smaller argument is already available in the lookup table.然后正确地假设较小参数的必要值已经在查找表中可用。

For the access to the lookup table, consider the following example:要访问查找表,请考虑以下示例:

input value (contains 5 ones):
01010000 00000010 00000100 00010000

input value, shifting is not necessary
masked with 0xff (11111111)
00000000 00000000 00000000 00010000     (contains 1 one)

input value shifted by 8
00000000 01010000 00000010 00000100
and masked with 0xff (11111111)
00000000 00000000 00000000 00000100     (contains 1 one)

input value shifted by 16
00000000 00000000 01010000 00000010
and masked with 0xff (11111111)
00000000 00000000 00000000 00000010     (contains 1 one)

input value shifted by 24,
masking is not necessary
00000000 00000000 00000000 01010000     (contains 2 ones)

The extracted values have only the lowermost 8 bits set, which means that the corresponding entries are available in the lookup table.提取的值仅设置了最低 8 位,这意味着相应的条目在查找表中可用。 The entries from the lookuptable are added.添加了查找表中的条目。 The underlying idea is that the number of ones in in the argument can be calculated byte-wise (in fact, any partition in bitstrings would be suitable).基本思想是参数中 1 的数量可以按字节计算(实际上,位串中的任何分区都是合适的)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM