【问题标题】:find set bits positions in a byte查找字节中的设置位位置
【发布时间】:2013-05-19 17:30:24
【问题描述】:

当我遇到这个问题时,我很想创建一个稀疏的八叉树实现,就像 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] for 42 (0010 1010)

谁能提供一个更优雅且可读的解决方案?


结果

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

【问题讨论】:

  • 重新分配数组并没有那么慢。对于您的目的而言,它是否太慢是另一回事(因为这可能是经常调用的内部循环代码)。您可以考虑首先计算汉明权重(位数设置为 1),以便您可以分配适当大小的数组,并测量这两种方法。 stackoverflow.com/questions/109023/…
  • 你知道,这就是我在这里问的原因。我写的最后一个方法就是尽可能快地获得叶子数......不敢相信我这样做时没有考虑我自己的代码。非常感谢您提出最明显的想法!

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


【解决方案1】:

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

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();

【讨论】:

  • 由于这只是一个字节,查找表可能是最快的方法。谢谢你的想法!
  • 查找表比switch语句快吗?
  • @EricJ。除了构建查找表的性能损失。
  • @Jodrell:如果您在其中编译查找表,则它是 O(1),如果在运行时构建查找表的 O(N) 时间有所不同,您可以这样做。
【解决方案2】:

这是一个使用 __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;
}

【讨论】:

    【解决方案3】:

    怎么样,

    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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-14
      • 1970-01-01
      • 1970-01-01
      • 2020-07-12
      相关资源
      最近更新 更多