给定一个非整数整数数组(每个4个八位字节),找到第一个元素的最佳方法是什么,它至少有一个'0'位,它是LSB的索引。
例如:其中n = 9
unsinged int uIntArray[] = {
0xffffffff,
0xffffffff,
0xffffffff,
0xffffffff,
0xffffff9f,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
};
答:
element's index = 4
bit's index = 4
我只能想到:
int main (void)
{
bool found_f = false;
int n = 9; //our test case value
unsigned int uIntArray[] = {
0xffffffff,
0xffffffff,
0xffffffff,
0xffffffff,
0xffffff8f,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
};
unsigned int uIntBits [32] = {
1, 2, 4, 8, 16, 32, 64, 128,
256, 512, 1024, 2048, 4096, 8192, 16384, 32768,
65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608,
16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, 2147483648
};
unsigned int idx, jdx;
int ele_idx = -1;
int bit_idx = -1;
for (idx =0; idx < n; idx ++) {
if (uIntArray[idx] < UINT_MAX) { /* our candidate */
for (jdx =0; jdx < 32; jdx ++) {
if ((uIntBits[jdx] & uIntArray[idx])) {
ele_idx = idx;
bit_idx = jdx;
found_f = true;
break;
}
}
}
if(found_f) {
break;
}
}
fprintf (stderr, "\nEleIdx[%d] BitIdx[%d]\n", ele_idx, bit_idx);
return 0;
}
有没有更好的方法呢?
答案 0 :(得分:2)
0
中最不重要x
的索引是1
中最不重要~x
的索引。为了找到后者,您只需要计算~x
中的尾随零。有很多方法可以做到,请参阅此处http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightLinear
使用最后一种方法(基于DeBruijn序列),搜索看起来像
static const unsigned MultiplyDeBruijnBitPosition[32] =
{
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
};
for (idx = 0; idx < n; idx ++)
if (uIntArray[idx] < UINT_MAX)
break;
if (idx < n) {
unsigned v = ~uIntArray[idx];
int bit_idx = MultiplyDeBruijnBitPosition[((v & -v) * 0x077CB531u) >> 27];
fprintf(stderr, "\nEleIdx[%d] BitIdx[%d]\n", idx, bit_idx);
}
答案 1 :(得分:1)
使用更大的数据类型可以使速度更快。因此,不是测试每个int
是0xffffffff
,而是使用64位整数并针对0xffffffffffffffff
进行测试。
如果你愿意进行矢量化,你可以一次做128位(SSE)或256位(AVX)。
在所有情况下,请注意您的数据对齐方式。如果你没有对齐,它将无法工作,或者会使它变慢。
最后一步,您可以实际展开循环并一次测试多个单词/向量。这将为您提供更好的IPC。只有当你发现任何零时,你才会经历缩小范围的混乱局面。
编辑:
为了说明最后一点,您可以这样做:(我在idx % 4 != 0
}
for (idx =0; idx < n; idx += 4) {
unsigned int test = uIntArray[idx];
test &= uIntArray[idx + 1];
test &= uIntArray[idx + 2];
test &= uIntArray[idx + 3];
if (test < UINT_MAX){
// Find which bit it is.
}
}
除非您可以在更大的数据类型上执行此操作。 (如SSE / AVX载体)
这将使得找到前0区域的速度更快,但缩小精确位将会更加昂贵。因此,如果您的数据量很大,这种方法会更好。
答案 2 :(得分:1)
要查找第一个元素,您可以观察到,如果号没有<{1}}位,那么它必须是0
,所以不是明确检查每个位,您只需将其与0xff..ff
进行比较。
要找到该数字的最低有效位,我相信你仍然需要检查每一位。
答案 3 :(得分:1)
以下代码似乎运行良好,使用来自另一个(现已删除)答案的~x & (x + 1)
测试并对其进行扩展。不知道为什么其他答案被删除了。
/* Return the position of the first clear bit in the array,
* or -1 if none found.
* arr: array of uint32_t to search
* sz: number of elements in arr
*/
int findClearBit(uint32_t *arr, int sz)
{
int i;
for (i = 0; i < sz; i++) {
if (~arr[i]) {
switch (~arr[i] & (arr[i] + 1)) {
case 1 << 31: return (i * 32) + 31;
case 1 << 30: return (i * 32) + 30;
case 1 << 29: return (i * 32) + 29;
case 1 << 28: return (i * 32) + 28;
case 1 << 27: return (i * 32) + 27;
case 1 << 26: return (i * 32) + 26;
case 1 << 25: return (i * 32) + 25;
case 1 << 24: return (i * 32) + 24;
case 1 << 23: return (i * 32) + 23;
case 1 << 22: return (i * 32) + 22;
case 1 << 21: return (i * 32) + 21;
case 1 << 20: return (i * 32) + 20;
case 1 << 19: return (i * 32) + 19;
case 1 << 18: return (i * 32) + 18;
case 1 << 17: return (i * 32) + 17;
case 1 << 16: return (i * 32) + 16;
case 1 << 15: return (i * 32) + 15;
case 1 << 14: return (i * 32) + 14;
case 1 << 13: return (i * 32) + 13;
case 1 << 12: return (i * 32) + 12;
case 1 << 11: return (i * 32) + 11;
case 1 << 10: return (i * 32) + 10;
case 1 << 9: return (i * 32) + 9;
case 1 << 8: return (i * 32) + 8;
case 1 << 7: return (i * 32) + 7;
case 1 << 6: return (i * 32) + 6;
case 1 << 5: return (i * 32) + 5;
case 1 << 4: return (i * 32) + 4;
case 1 << 3: return (i * 32) + 3;
case 1 << 2: return (i * 32) + 2;
case 1 << 1: return (i * 32) + 1;
case 1: return (i * 32);
default: return -1;
}
}
}
return -1;
}