代码之家  ›  专栏  ›  技术社区  ›  John_

有人能告诉我这个getcardinality方法在做什么吗?

  •  6
  • John_  · 技术社区  · 15 年前

    我一直在用lucene.net研究平面搜索,我发现了一个很好的例子。 here 这解释了一个公平的数量,除了它完全忽略了检查位数组中项目基数的函数。

    有人能告诉我它在做什么吗?我不明白的主要事情是为什么bitsetarray是按原样创建的,它用于什么,以及所有if语句如何在for循环中工作。

    这可能是一个很大的问题,但在我想到在自己的代码中使用它之前,我必须了解它是如何工作的。

    谢谢

    public static int GetCardinality(BitArray bitArray)
        {
            var _bitsSetArray256 = 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};
            var array = (uint[])bitArray.GetType().GetField("m_array", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(bitArray);
            int count = 0;
    
            for (int index = 0; index < array.Length; index ++)
                count += _bitsSetArray256[array[index] & 0xFF] + _bitsSetArray256[(array[index] >> 8) & 0xFF] + _bitsSetArray256[(array[index] >> 16) & 0xFF] + _bitsSetArray256[(array[index] >> 24) & 0xFF];
    
            return count;
        }
    
    2 回复  |  直到 12 年前
        1
  •  11
  •   AakashM    15 年前

    这个 _bitsSetArray256 数组是用值初始化的,以便 _bitsSetArray256[n] 包含二进制表示形式中设置的位数 n ,为了 n 在里面 0..255 .

    例如, _bitsSetArray256[13] 等于3,因为二进制中的13是 1101 其中包含3个 1 S.

    这样做的原因是,预先计算并存储这些值要快得多,而不必每次都计算(或按需计算)。它不像 在二进制表示的13中,s永远不会改变,毕竟:)

    for 循环,我们循环通过一个数组 uint S.A.C.公司# 无符号整型 是一个32位的数量,即由4个字节组成。我们的查找表告诉我们一个字节中设置了多少位,所以我们必须处理四个字节中的每一个。钻头的操作 count += 行提取四个字节中的每一个,然后从查找数组中获取其位计数。将所有四个字节的位计数相加得到 无符号整型 作为一个整体。

    所以给出了一个 BitArray ,此函数深入到 uint[] m_array 成员,然后返回以二进制表示的 无符号整型 在那里。

        2
  •  5
  •   PHeiberg    12 年前

    我只是想为我们中那些正在开发自己的lucene.net方面的版本的人发表一篇关于位数组的有用文章。见: http://dotnetperls.com/precomputed-bitcount

    这是一个很好的解释,说明了获取整数中on位的基数的最快方法(这是上面代码示例所做的大部分工作)。

    在我的方面搜索和其他一些简单的更改中,我发现了本文中的方法,我可以将获取计数所用的时间缩短约65%。 区别在于:

    1. 声明_bitcount global(因此它不是每次调用创建的)
    2. 将for改为foreach(ant profiler在这里显示了25%的增益)
    3. 实现65535表与256表,每次移动16位,而不是8位。

      private static int[] _bitcounts = InitializeBitcounts();
      
      private static int GetCardinality(BitArray bitArray)
      {
          uint[] array = (uint[])bitArray.GetType().GetField("m_array", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(bitArray);
      
          int count = 0;
          foreach (uint value in array)
          {
              count += _bitcounts[value & 65535] + _bitcounts[(value >> 16) & 65535];           
          }
          return count;
      }
      
      private static int[] InitializeBitcounts()
      {
          int[] bitcounts = new int[65536];
          int position1 = -1;
          int position2 = -1;
          //
          // Loop through all the elements and assign them.
          //
          for (int i = 1; i < 65536; i++, position1++)
          {
              //
              // Adjust the positions we read from.
              //
              if (position1 == position2)
              {
                  position1 = 0;
                  position2 = i;
              }
              bitcounts[i] = bitcounts[position1] + 1;
          }
          return bitcounts;
      }