我有byte
我正在使用bitflags。我知道byte
中的 一个且只有一个 位在任何给定时间设置。
例如:unsigned char b = 0x20; //(00100000) 6th most bit set
我目前使用以下循环来确定设置了哪个位:
int getSetBitLocation(unsigned char b) {
int i=0;
while( !((b >> i++) & 0x01) ) { ; }
return i;
}
如何最有效地确定设定位的位置?我可以不经迭代地完成这项工作吗?
答案 0 :(得分:7)
我可以不经迭代地完成这项工作吗?
确实可能。
如何最有效地确定设定位的位置?
您可以尝试此算法。它将char分成两半以搜索最高位,每次都转移到低位:
int getTopSetBit(unsigned char b) {
int res = 0;
if(b>15){
b = b >> 4;
res = res + 4;
}
if(b>3){
b = b >> 2;
res = res + 2;
}
//thanks @JasonD
return res + (b>>1);
}
它使用两个比较(三个用于uint16
s,四个用于uint32
s ...)。它可能比你的循环更快。绝对不会短。
根据Anton Kovalenko的想法(散列查找)和6502的评论(除法很慢),我也建议这个实现(使用de-Bruijn序列的8位=> 3位散列)
int[] lookup = {7, 0, 5, 1, 6, 4, 3, 2};
int getBitPosition(unsigned char b) {
// return lookup[(b | (b>>1) | (b>>2) | (b>>4)) & 0x7];
return lookup[((b * 0x1D) >> 4) & 0x7];
}
或(较大的LUT,但仅使用三个术语而不是四个术语)
int[] lookup = {0xFF, 0, 1, 4, 2, 0xFF, 5, 0xFF, 7, 3, 0xFF, 0xFF, 6, 0xFF, 0xFF, 0xFF};
int getBitPosition(unsigned char b) {
return lookup[(b | (b>>3) | (b>>4)) & 0xF];
}
答案 1 :(得分:5)
查找表很简单,如果值集稀疏,您可以减小其大小。让我们试试11个元素而不是128个:
unsigned char expt2mod11_bits[11]={0xFF,0,1,0xFF,2,4,0xFF,7,3,6,5};
unsigned char pos = expt2mod11_bits[b%11];
assert(pos < 8);
assert(1<<pos == b);
当然,它不一定更有效,特别是对于8位,但同样的技巧可以用于更大的尺寸,其中完整的查找表将非常大。我们来看看:
unsigned int w;
....
unsigned char expt2mod19_bits[19]={0xFF,0,1,13,2,0xFF,14,6,3,8,0xFF,12,15,5,7,11,4,10,9};
unsigned char pos = expt2mod19_bits[w%19];
assert(pos < 16);
assert(1<<pos == w);
答案 2 :(得分:3)
对于使用64位表示位置的国际象棋程序来说,这是一个相当普遍的问题(即,一个64位数字用于存储所有白色棋子的位置,另一个用于存储所有黑色棋子的位置等等)。
通过这种表示,有时需要找到第一个或最后一个设置位的索引0 ... 63,并且有几种可能的方法:
x & 0x00000000ffffffffULL
为零,则无需检查低32位)bsf
和bsr
)但速度更快取决于您的硬件和实际使用情况。 对于仅8位和现代处理器,我认为可能具有256个条目的查找表是最佳选择......
但你确定这是你的算法的瓶颈吗?
答案 3 :(得分:2)
unsigned getSetBitLocation(unsigned char b) {
unsigned pos=0;
pos = (b & 0xf0) ? 4 : 0; b |= b >>4;
pos += (b & 0xc) ? 2 : 0; b |= b >>2;
pos += (b & 0x2) ? 1 : 0;
return pos;
}
跳跃自然很难。也许与Bruin序列?
答案 4 :(得分:2)
基于Find the log base 2 of an N-bit integer in O(lg(N)) operations中的log2计算:
int getSetBitLocation(unsigned char c) {
// c is in {1, 2, 4, 8, 16, 32, 64, 128}, returned values are {0, 1, ..., 7}
return (((c & 0xAA) != 0) |
(((c & 0xCC) != 0) << 1) |
(((c & 0xF0) != 0) << 2));
}
答案 5 :(得分:1)
最简单的方法是创建一个查找表。最简单的一个是稀疏的(有256个元素)但从技术上来说它会避免迭代。
这里的评论在技术上避免了迭代,但是我们在开玩笑,它仍在进行相同数量的检查:How to write log base(2) in c/c++
封闭形式将是log2(),一个la,log2() + 1
但是我不确定它的效率如何 - 可能CPU有一个指令来获取基数为2的对数?
答案 6 :(得分:0)
如果你定义
const char bytes[]={1,2,4,8,16,32,64,128}
并使用
struct byte{
char data;
int pos;
}
void assign(struct byte b,int i){
b.data=bytes[i];
b.pos=i
}
您无需确定设置位的位置
答案 7 :(得分:0)
当CHAR_BIT == 8时,查找表快速而简单,但在某些系统上,CHAR_BIT == 16或32,查找表变得非常笨重。如果您正在考虑查找表,我建议将其包装;相反,使它成为“查找表函数”,以便在需要优化时可以交换逻辑。
使用分而治之,通过对已排序的数组执行二进制搜索,涉及基于log2 CHAR_BIT
的比较。该代码更复杂,涉及初始化unsigned char
数组以用作开始的查找表。初始化数组后,可以使用bsearch
进行搜索,例如:
#include <stdio.h>
#include <stdlib.h>
void uchar_bit_init(unsigned char *table) {
for (size_t x = 0; x < CHAR_BIT; x++) {
table[x] = 1U << x;
}
}
int uchar_compare(void const *x, void const *y) {
char const *X = x, *Y = y;
return (*X > *Y) - (*X < *Y);
}
size_t uchar_bit_lookup(unsigned char *table, unsigned char value) {
unsigned char *position = bsearch(lookup, c, sizeof lookup, 1, char_compare);
return position ? position - table + 1 : 0;
}
int main(void) {
unsigned char lookup[CHAR_BIT];
uchar_bit_init(lookup);
for (;;) {
int c = getchar();
if (c == EOF) { break; }
printf("Bit for %c found at %zu\n", c, uchar_bit_lookup(lookup, c));
}
}
P.S。这听起来像微优化。完成解决方案(将这些功能所需的操作抽象出来),然后根据您的分析担心优化。如果您要专注于微优化,请确保您的分析针对您的解决方案将运行的系统,因为微优化的效率差异很大,因为硬件差异甚至略有...通常更好的想法是购买更快的PC;)