有人能解释一下这个GetCardinality方法在做什么吗?

时间:2009-11-18 08:54:24

标签: c# lucene.net faceted-search

我一直在寻找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;
    }

2 个答案:

答案 0 :(得分:11)

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

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

这样做的原因是预先计算这些值并存储它们要快得多,而不是每次(或按需)必须解决它们。毕竟,这并不像13的二进制表示中1的数量会发生变化:)

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

因此给定BitArray,此函数会深入uint[] m_array成员,然后返回其中uint的二进制表示中设置的总位数。

答案 1 :(得分:5)

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

这是一个很好的探索方法,以获取整数位的基数(这是上述代码示例所做的大部分内容)。

通过我的分面搜索以及其他一些简单的更改,文章中的方法变得非常简单,我能够将计算所需的时间减少约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;
    }