对于海量数据来讲,数据内存占用会变得很高. Probabilistic数据结构牺牲了一下准确率去换取更低内存占用。好比一个HyperLogLog的数据结构只须要花费12KB内存,就能够计算接近2^64个不一样元素的基数,而错误率在1.625%.算法
HyperLogLog一个经常使用的场景就是统计网站的UV。数据结构
简单来讲,基数(cardinality,也译做势),是指一个集合(这里的集合容许存在重复元素)中不一样元素的个数。例如看下面的集合:
{1,2,3,4,5,2,3,9,7}
这个集合有9个元素,可是2和3各出现了两次,所以不重复的元素为1,2,3,4,5,9,7,因此这个集合的基数是7。maven
<dependency> <groupId>net.agkn</groupId> <artifactId>hll</artifactId> <version>1.6.0</version> </dependency>
使用网站
@Test public void testSimpleUse(){ final int seed = 123456; HashFunction hash = Hashing.murmur3_128(seed); // data on which to calculate distinct count final Integer[] data = new Integer[]{1, 1, 2, 3, 4, 5, 6, 6, 6, 7, 7, 7, 7, 8, 10}; final HLL hll = new HLL(13, 5); //number of bucket and bits per bucket for (int item : data) { final long value = hash.newHasher().putInt(item).hash().asLong(); hll.addRaw(value); } System.out.println("Distinct count="+ hll.cardinality()); }
设想成一次不断投硬币的过程,非正面即反面(每一面的几率为0.5)。 在这个过程当中,投掷次数大于k的几率是0.5^k(连续投掷出k个反面),在一次过程当中,投掷次数小于k的几率是(1-0.5)^k。
所以,在n次投掷过程当中,投掷次数均小于k的几率是.net
P(x<=k)=(1-0.5^k)^n P(x>=k)=1-(1-0.5^k)^n
从以上公式,能够看出,当n<=k)的几率,接近为0。而当n>>k时,P(x<=k)的几率接近为0。因此,当n>>k时,没有一次投掷次数大于k的几率几乎为0。code
将一次过程,理解成一个比特子串,反面为0,正面为1, 投掷次数k对应第一个1出现的位置,当统计子串足够多时,其最大的第一个1的位置为j,那么当n>>2^j时,P(x<=k)接近为0,当n<<2^j时,P(x>=0)也趋向为0。也就是说,在获得x=k的前提下,咱们能够认为n=2^j。blog
再通俗点说明: 假设咱们为一个数据集合生成一个8位的哈希串,那么咱们获得00000111的几率是很低的,也就是说,咱们生成大量连续的0的几率是很低的。生成连续5个0的几率是1/32,那么咱们获得这个串时,能够估算,这个数据集的基数是32。内存