在C#中计数任意大的正整数

时间:2012-08-07 12:59:40

标签: c# .net optimization bit-manipulation bitcount

有很多位计数的实现,但在我的情况下,我需要测试一个任意大的数字是否包含最多两个设置位。

我编写了以下函数来完成这项工作并且似乎非常快,但我想知道它是否可以针对C#进一步优化。这个函数在循环中被调用几百万次。

public static byte [] BitCountLookupArray = new byte []
{
    0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
};

// The parameter [number] will NEVER be negative.
public static bool HasSetBitCountOfLessThenThree (System.Numerics.BigInteger number)
{
    int sum = 0;
    byte [] bytes = null;

    bytes = number.ToByteArray();

    for (int i=0; i < bytes.Length; i++)
    {
        sum += BitCountLookupArray [bytes [i]];
    }

    return (sum < 3);
}

重要:发送到该功能的参数[编号]将从不为负。

我想到的一些观点是:

  • 使功能静止。完成。
  • 使用静态查找数组。完成。
  • 使用指针而不是数组索引,因为字节数经常超过100,000。不确定这会有多大帮助。
  • 强制在.NET中无法保证内联函数。

对其他建议持开放态度。

3 个答案:

答案 0 :(得分:5)

这样你可以进一步优化它

for (int i=0; i < bytes.Length; i++)
{
    sum += BitCountLookupArray [bytes [i]];
    if(sum >= 3)
    {
         return false   // This will stop the execution of unnecessary lines  
                        // as we need to know whether sum is less than 3 or not.                         
    }
}

return true;

答案 1 :(得分:3)

由于您只需知道您的设置位是否少于3,我建议您这样做:

// remove two bits
number &= number - 1;
number &= number - 1;
// if number != 0, then there were 3 or more bits set
return number.IsZero;

当然Rain的方法也有效,我不确定哪种策略会更快。

替代:

//remove one bit
number &= number - 1;
// if the number of bits left is 0 or 1, there were < 3 bits set
return number.IsZero || number.IsPowerOfTwo;

首先测试可能会更快,稍后删除它:

return number.IsZero ||        // zero bits?
    number.IsPowerOfTwo ||     // one bit?
    (number & (number - 1)).IsPowerOfTwo;  // two bits?

答案 2 :(得分:1)

最明显的优化是尽快退出循环sum == 3,因为在此之后的任何进一步匹配都是无关紧要的。

也没有必要设置bytes两次;只需使用byte [] bytes = number.ToByteArray();,但这里的好处是微不足道的。