【问题标题】:Converting a range into a bit array将范围转换为位数组
【发布时间】:2010-10-15 20:33:29
【问题描述】:

我正在用 C# 编写一段时间要求严格的代码,它要求我将定义包含范围的两个无符号整数转换为位字段。例如:

uint x1 = 3;
uint x2 = 9;
  //defines the range [3-9]
  //                              98  7654 3
  //must be converted to:  0000 0011  1111 1000

以相反的顺序可视化位可能会有所帮助

这个范围的最大值是在运行时给出的一个参数,我们称之为max_val。因此,位域变量应该定义为一个大小等于max_val/32UInt32数组:

UInt32 MAX_DIV_32 = max_val / 32;
UInt32[] bitArray = new UInt32[MAX_DIV_32];

给定由变量x1x2 定义的范围,执行此转换的最快方法是什么?

【问题讨论】:

  • 大多数情况下 max_val >= 32 吗?否则使用数组是没有意义的。如果是,那么我假设您希望能够说我需要将 200 位设置为 1,并在右侧将 100 位的填充设置为 0。
  • 如果你的 max_val 不能被 32 整除,那么你的 bitArray 将会丢失一些位。
  • 塞缪尔——不知道你在说什么。吉姆 - 是的,我知道,这很好。
  • 模组消失了……你必须进行基准测试。我怀疑表查找会显着提高任何速度。
  • 看起来很漂亮。而且我刚刚发现我提供的用于查找表的代码无论如何都是错误的。感谢您的精彩回答!

标签: c# bit-manipulation bitarray


【解决方案1】:

试试这个。计算必须用全填充的数组项的范围,并通过迭代此范围来完成此操作。最后设置两个边框的项目。

Int32 startIndex = x1 >> 5;
Int32 endIndex = x2 >> 5;

bitArray[startIndex] = UInt32.MaxValue << (x1 & 31);

for (Int32 i = startIndex + 1; i <= endIndex; i++)
{
   bitArray[i] = UInt32.MaxValue;
}

bitArray[endIndex] &= UInt32.MaxValue >> (31 - (x2 & 31));

可能代码不是 100% 正确,但这个想法应该可行。


刚刚测试了它,发现了三个错误。开始索引处的计算需要一个 mod 32,而结束索引处的 32 必须是 31 并且是一个逻辑 and 而不是分配来处理开始索引和结束索引相同的情况。应该很快。


刚刚用 x1 和 x2 在阵列上的相等分布对其进行了基准测试。 Intel Core 2 Duo E8400 3.0 GHz,MS VirtualPC 和 Windows XP 主机上的 Server 2003 R2。

Array length [bits]           320         160         64
Performance [executions/s]    33 million  43 million  54 million

还有一个优化 x % 32 == x & 31 但我无法衡量性能提升。由于我的测试中只有 10.000.000 次迭代,因此波动非常大。而且我在 VirtualPC 中运行,这使得情况更加不可预测。

【讨论】:

  • 将值设置为startIndex 在这里是错误的。您不想左移 x1。我想你想左移(x1 % 32)
  • 你是对的......刚刚启动了我的 IDE 并尝试了代码。如果要设置的所有位都在单个数组项中,则当前未处理特殊情况。我现在正在修复它。
  • 这看起来很不错。我想知道我是否可以以某种方式优化那些模数运算符。实际上,我什至不知道查表是否会更快。
  • 很快就谈到了。如果有的话,我会替换班次操作员......我想知道这是否会提高性能。
  • 移位操作真的很快。我会尝试去掉 mods 和减法,并且可能会尝试用 while 循环替换 for 循环。但我不确定什么最有帮助 - 有一段时间没有进行汇编程序优化。
【解决方案2】:

我将 BitArray 中的所有位设置为真或假的解决方案:

public static BitArray SetRange(BitArray bitArray, Int32 offset, Int32 length, Boolean value)
{

    Int32[] ints = new Int32[(bitArray.Count >> 5) + 1];

    bitArray.CopyTo(ints, 0);

    var firstInt = offset >> 5;
    var lastInt = (offset + length) >> 5;

    Int32 mask = 0;

    if (value)
    {
        // set first and last int
        mask = (-1 << (offset & 31));
        if (lastInt != firstInt)
            ints[lastInt] |= ~(-1 << ((offset + length) & 31));
        else
            mask &= ~(-1 << ((offset + length) & 31));

        ints[firstInt] |= mask;

        // set all ints in between
        for (Int32 i = firstInt + 1; i < lastInt; i++)
            ints[i] = -1;
    }

    else
    {
        // set first and last int
        mask = ~(-1 << (offset & 31));
        if (lastInt != firstInt)
            ints[lastInt] &= -1 << ((offset + length) & 31);
        else
            mask |= -1 << ((offset + length) & 31);

        ints[firstInt] &= mask;

        // set all ints in between
        for (Int32 i = firstInt + 1; i < lastInt; i++)
            ints[i] = 0;

    }

    return new BitArray(ints) { Length = bitArray.Length };

}

【讨论】:

    【解决方案3】:

    你可以试试:

    UInt32 x1 = 3;
    UInt32 x2 = 9;
    UInt32 newInteger = (UInt32)(Math.Pow(2, x2 + 1) - 1) & 
                       ~(UInt32)(Math.Pow(2, x1)-1);
    

    【讨论】:

    • 我可以使用左移运算符而不是 Math.Pow。 IE。 ((2
    • 也许我可以不使用移位运算符,而是使用预先计算的值表来提高性能。
    【解决方案4】:

    是否有理由不使用 System.Collections.BitArray 类而不是 UInt32[]?否则,我会尝试这样的事情:

    int minIndex = (int)x1/32;
    int maxIndex = (int)x2/32;
    // first handle the all zero regions and the all one region (if any)
    for (int i = 0; i < minIndex; i++) {
        bitArray[i] = 0;
    }
    for (int i = minIndex + 1; i < maxIndex; i++) {
        bitArray[i] = UInt32.MaxValue; // set to all 1s
    }
    for (int i = maxIndex + 1; i < MAX_DIV_32; i++) {
        bitArray[i] = 0;
    }
    
    // now handle the tricky parts
    uint maxBits = (2u << ((int)x2 - 32 * maxIndex)) - 1; // set to 1s up to max
    uint minBits = ~((1u << ((int)x1 - 32 * minIndex)) - 1); // set to 1s after min
    
    if (minIndex == maxIndex) {
        bitArray[minIndex] = maxBits & minBits;
    }
    else {
        bitArray[minIndex] = minBits;
        bitArray[maxIndex] = maxBits;
    }
    

    【讨论】:

    • System.Collections.BitArray 很方便,但速度非常慢。所有这些范围检查确实会妨碍您。
    • kvb,这看起来真不错。我去看看能不能优化一下。
    【解决方案5】:

    我很无聊,尝试使用 char 数组并使用 Convert.ToUInt32(string, int) 从基数 2 转换为 uint

    uint Range(int l, int h)
    {
      char[] buffer = new char[h];
      for (int i = 0; i < buffer.Length; i++)
      {
        buffer[i] = i < h - l ? '1' : '0';
      }
      return Convert.ToUInt32(new string(buffer), 2);
    }
    

    一个简单的基准测试表明,我的方法比 Angrey Jim 的方法快 5%(即使你用位移位替换第二个 Pow。)

    如果上限太大而无法放入单个int,则转换为生成uint 数组可能是最容易的。这有点神秘,但我相信它有效。

    uint[] Range(int l, int h)
    {
      char[] buffer = new char[h];
      for (int i = 0; i < buffer.Length; i++)
      {
        buffer[i] = i < h - l ? '1' : '0';
      }
    
      int bitsInUInt = sizeof(uint) * 8;
      int numNeededUInts = (int)Math.Ceiling((decimal)buffer.Length /
                                             (decimal)bitsInUInt);
      uint[] uints = new uint[numNeededUInts];
      for (int j = uints.Length - 1, s = buffer.Length - bitsInUInt;
           j >= 0 && s >= 0;
           j--, s -= bitsInUInt)
      {
        uints[j] = Convert.ToUInt32(new string(buffer, s, bitsInUInt), 2);
      }
    
      int remainder = buffer.Length % bitsInUInt;
      if (remainder > 0)
      {
        uints[0] = Convert.ToUInt32(new string(buffer, 0, remainder), 2);
      }
    
      return uints;
    }
    

    【讨论】:

      【解决方案6】:

      试试这个:

      uint x1 = 3;
      uint x2 = 9;
      
      int cbToShift = x2 - x1; // 6
      int nResult = ((1 << cbToShift) - 1) << x1; 
      
      /*
        (1<<6)-1 gives you 63 = 111111, then you shift it on 3 bits left
      */
      

      【讨论】:

      • 范围 [3-9] 只是一个简化的示例。这需要使用数组在大于 32 位的值范围内放大。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-09
      • 2021-10-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多