找到第一个可用位的最佳方法

时间:2011-09-19 08:07:41

标签: c

给定一个非整数整数数组(每个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;
}   

有没有更好的方法呢?

4 个答案:

答案 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)

使用更大的数据类型可以使速度更快。因此,不是测试每个int0xffffffff,而是使用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;
}