我一直在寻找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;
}
答案 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%。 其中的差异在于:
实现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;
}