找到一个字节中的设置位位置

时间:2013-05-19 17:30:25

标签: c# bit-manipulation bit-shift octree

当我遇到这个问题时,我正在创建一个稀疏的八叉树实现,就像nVidia ("Efficient Sparse Voxel Octrees")的人正在为他们的体素做的那样:

我有一个类型字节的位域(只有8位)告诉我八叶树的叶子在哪里(1表示叶子,0表示没有叶子,8个节点附着 - > 8位)。我现在要做的是返回叶子位置的数组。我当前的实现是使用while循环来确定是否设置了LSB。之后输入移1。所以我就是这样做的:

int leafposition = _leafmask & _validmask;
int[] result = new int[8]; 
int arrayPosition = 0;
int iteration = 0;
while ( leafposition > 0 )
{
   iteration++; //nodes are not zero-indexed ... ?
   if ( (leafposition & 1) == 1 ) // LSB set?
   {
     result.SetValue( iteration, arrayPosition );
     arrayPosition++;
   };
   leafposition = leafposition >> 1;
}
return result;

这看起来并不优雅,有两件令人不安的事情:

  • 此while循环模仿for循环
  • 结果数组很可能小于8个值,但调整大小成本很高

我希望结果与[2,4,6]的{​​{1}}相似。

任何人都可以提供更优雅的解决方案吗?


结果

我正在使用我之前实现的八叉树叶数的函数来将数组设置为合适的大小。

3 个答案:

答案 0 :(得分:5)

如果你追求代码简洁,我会用这个:

int[] result = new int[8]; 
byte leafposition = 42;
int arrayPosition = 0;
for (int iteration = 0; iteration < 8; ++iteration)
    if ((leafposition & (1 << iteration)) != 0)
        result[arrayPosition++] = iteration + 1;   // one-indexed

如果您正在追求性能,我会使用预先填充的数组(256个条目)。你可以静态地(在编译时)或懒惰地(在第一次调用你的方法之前)生成它。

int[][] leaves =
{
    /* 00000000 */ new int[] { },
    /* 00000001 */ new int[] { 1 },
    /* 00000010 */ new int[] { 2 },
    /* 00000011 */ new int[] { 1, 2 },
    /* 00000100 */ new int[] { 3 },
    /* 00000101 */ new int[] { 1, 3 },
    /* 00000110 */ new int[] { 2, 3 },
    /* 00000111 */ new int[] { 1, 2, 3 },
    /* 00001000 */ new int[] { 4 },
    /* 00001001 */ new int[] { 1, 4 },
    /* ... */
};

byte leafposition = 42;
int[] result = leaves[leafposition];

编辑:如果您正在使用查找表并且可以进行一次性初始化(将通过许多后续使用进行摊销),我建议动态创建它(而不是膨胀你的源代码)。这是LINQ中的一些示例代码;你可以使用循环版本。

int[][] leaves = new int[256][];
for (int i = 0; i < 256; ++i)
    leaves[i] = Enumerable.Range(0, 8)
                          .Where(b => (i & (1 << b)) != 0)
                          .Select(b => b + 1)
                          .ToArray();

答案 1 :(得分:0)

这是一个使用__builtin_ffs

的C风格解决方案
int arrayPosition = 0;
unsigned int tmp_bitmap = original_bitfield;        
while (tmp_bitmap > 0) {
    int leafposition = __builtin_ffs(tmp_bitmap) - 1;
    tmp_bitmap &= (tmp_bitmap-1);
    result[arrayPosition++] = leafposition;
}

答案 2 :(得分:0)

怎么样,

public static IEnumerable<int> LeafPositions(byte octet)
{
    for (var i = 1; octet > 0; octet >>= 1, i++)
    {
        if ((octet & 1) == 1)
        {
            yield return i;
        }
    }
}

或者,在我看来更容易阅读。

IEnumerable<int> LeafPositions(byte octet)
{
    if ((octet & 1) == 1) yield return 1;
    if ((octet & 2) == 2) yield return 2;
    if ((octet & 4) == 4) yield return 3;
    if ((octet & 8) == 8) yield return 4;
    if ((octet & 16) == 16) yield return 5;
    if ((octet & 32) == 32) yield return 6;
    if ((octet & 64) == 64) yield return 7;
    if ((octet & 128) == 128) yield return 8;
}

或者,走向极端

IEnumerable<int> LeafPositions(byte octet)
{
    switch (octet)
    {
        case 1:
            yield return 1;
            break;

        case 2:
            yield return 2;
            break;

        case 3:
            yield return 1;
            yield return 2;
            break;

        ...

        case 255:
            yield return 1;
            yield return 2;
            yield return 3;
            yield return 4;
            yield return 5;
            yield return 6;
            yield return 7;
            yield return 8;
            break;
    }

    yield break;
}