如何在BitArray中获取最后设置的位?

时间:2016-05-06 11:16:03

标签: c# .net bit-manipulation

在BitArray中获取最后一个设置位的有效(快速)方法是什么。 (对于大型位图,LINQ或简单的向后循环并不是非常快。我需要快速)BitArray 我看到下一个算法:返回BitArray内部int数组数据并使用一些编译器Intrinsic Like C ++ _BitScanReverse(不知道C#中的模拟)。

3 个答案:

答案 0 :(得分:2)

“正常”解决方案:

    static long FindLastSetBit(BitArray array)
    {
        for (int i = array.Length - 1; i >= 0; i--)
        {
            if (array[i])
            {
                return i;
            }
        }

        return -1;
    }

反思解决方案(注意 - 依赖于BitArray的实施):

    static long FindLastSetBitReflection(BitArray array)
    {
        int[] intArray = (int[])array.GetType().GetField("m_array", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(array);

        for (var i = intArray.Length - 1; i >= 0; i--)
        {
            var b = intArray[i];
            if (b != 0)
            {
                var pos = (i << 5) + 31;
                for (int bit = 31; bit >= 0; bit--)
                {
                    if ((b & (1 << bit)) != 0)
                        return pos;

                    pos--;
                }

                return pos;
            }
        }

        return -1;
    }

对于我来说,在大BitArray s上,反射解决方案的速度提高了50-100倍(在非常小的情况下,反射的开销将开始出现)。我的机器每兆字节大约需要0.2毫秒。

主要的是if (b != 0)一次检查32位。当找到正确的单词时,检查特定位的内部循环只运行一次。

编辑:删除了不安全的代码,因为我意识到它几乎没有任何东西被获得,它只避免了数组边界检查,因为代码太快已经没那么重要了。为了记录,不安全的解决方案(对我来说快30%):

    static unsafe long FindLastSetBitUnsafe(BitArray array)
    {
        int[] intArray = (int[])array.GetType().GetField("m_array", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(array);

        fixed (int* buffer = intArray)
        {
            for (var i = intArray.Length - 1; i >= 0; i--)
            {
                var b = buffer[i];
                if (b != 0)
                {
                    var pos = (i << 5) + 31;
                    for (int bit = 31; bit >= 0; bit--)
                    {
                        if ((b & (1 << bit)) != 0)
                            return pos;

                        pos--;
                    }

                    return pos;
                }
            }
        }

        return -1;
    }

答案 1 :(得分:0)

如果您想要最后设置位的索引,可以在C#6中执行此操作。

int? index = array.Select((b,i)=>{Index = i, Value = b})
                  .LastOrDefault(x => x.Value)
                  ?.Index;

否则你必须做这样的事情

var last = array.Select((b,i)=>{Index = i, Value = b})
                  .LastOrDefault(x => x.Value);
int? index = last == null ? (int?)null : last.Index;

如果所有位都为零,则index将为null

答案 2 :(得分:0)

我不相信除了从最后一位到第一位进行迭代之外,还有什么可以做的,并且如果已经设置则请求每一位。可以通过以下方式完成:

BitArray bits = ...;
int lastSet = Enumerable.Range(1, bits.Length)
                  .Select(i => bits.Length - i)
                  .Where(i => bits[i])
                  .DefaultIfEmpty(-1)
                  .First();

那应该返回最后一位设置,如果没有则返回-1。 Haven自己测试过,所以可能需要调整一下。

希望它有所帮助。