【问题标题】:Can someone explain to me what this GetCardinality method is doing?有人可以向我解释这个 GetCardinality 方法在做什么吗?
【发布时间】:2010-12-17 19:04:24
【问题描述】:

我一直在研究使用 Lucene.NET 进行分面搜索,我发现了一个出色的示例 here,它解释了相当多的内容,除了它完全忽略了检查位数组。

谁能告诉我它在做什么?我不明白的主要事情是为什么按原样创建 bitsSetArray,它的用途以及所有 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;
    }

【问题讨论】:

    标签: c# lucene.net faceted-search


    【解决方案1】:

    我只是想为我们这些正在使用 Lucene.net 开发自己的 Faceting 版本的人发布一篇关于 bitArrays 的有用文章。见:http://dotnetperls.com/precomputed-bitcount

    这是对获取整数中 on 位基数的最快方法的一个很好的解释(这是上述代码示例的大部分内容)。

    在我的分面搜索和其他一些简单的更改中实施文章中的方法,我能够将获取计数的时间缩短约 65%。 区别在哪里:

    1. 声明 _bitcount 全局(因此不是每次调用都创建)
    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;
      }
      

    【讨论】:

      【解决方案2】:

      _bitsSetArray256 数组的初始化值使得_bitsSetArray256[n] 包含在n 的二进制表示中设置的位数,对于n 中的0..255

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

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

      for 循环中,我们正在循环通过uints 的数组。 C#uint 是一个 32 位的数量,即由 4 个字节组成。我们的查找表告诉我们在一个字节中设置了多少位,因此我们必须处理这四个字节中的每一个。 count += 行中的位操作提取四个字节中的每一个,然后从查找数组中获取其位计数。将所有四个字节的位计数相加得出uint 的位计数作为一个整体。

      所以给定一个BitArray,这个函数挖掘uint[] m_array成员,然后返回其中uints的二进制表示中设置的总位数。

      【讨论】:

      • 太棒了,感谢 AakashM。其中一些仍然在我脑海中浮现,但至少我了解该方法的概念以及它正在做什么。
      猜你喜欢
      • 2014-12-06
      • 1970-01-01
      • 2011-03-19
      • 1970-01-01
      • 2019-04-18
      • 1970-01-01
      • 2018-04-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多