七、Redis三种高级数据结构-HyperLogLog

Redis HyperLogLog是用来做基数统计的算法,HyperLogLog在优点是,在输入的元素的数量或者体积非常大时,计算基数占用的空间总是固定的、并且非常小。在Redis里每个HyperLogLog键只需花费12KB内存,就可以计算接近 264 个元素的基数。因为HyperLogLog只是计算输入元素的基数,而不会储存元素的本身,因此HyperLogLog不会返回输入的元素。

1、什么是基数

比如数据集{1,3,5,7,9,3,5,7},那么这个数据集的基数集为{1,3,5,7,9},基数为5(不重复的元素)。基数估计就是在误差可接受的范围内,快速计算基数。

2、常用命令

  • pfadd key element[element…]:将数据添加到HyperLogLog数据结构中。
  • pfcount key [key…]:返回给定HyperLogLog的基数估计值。
  • pfmerge destKey sourceKey[sourceKey]:将多个HyperLogLog合并为一个HyperLogLog

3、HyperLogLog特点

1、占用内存小,12KB的内存大小,可以统计将近264 个元素。
2、计数存在一定的误差,但整体误差率较低。标准误差为0.81%。

*在Java中,我们知道long类型占用8字节,1byte=8位,即long类型能表示的最大数值为263 -1。对应上面的 264个数。假设现在有264个数。那么按照long,以及1KB=1024字节。那么264个数占用的内存为(264 8)/1024。其占用的内存远远高于12KB。

4、HyperLogLog原理

4.1、伯努利实验

在了解为什么HyperLogLog能在占用内存那么小的情况下,来统计那么大的数据。需要先了解下伯努利实验。

伯努利试验是数学中概率论的一部分内容。它的典故来源于抛硬币。

硬币只有正反两面(正好对应计算机中的0和1),最终出现正反面的概率都是50%。假设,我们一直抛硬币,只要出现一次正面,就记为一次完整实验。中间可能一次就出现正面,也可能10次才出现正面。不论抛了多少次,只要出现一次正面,就记为一次完整的实验。这个实验就是伯努利实验。
那么对于n次伯努利实验。意味着出现了**n**次正面。每一次的结果都有可能不同。假设第一次伯努利实验抛掷次数为**k1**,以此类推,第**n**此为**kn**
其中,在n次实验中,必然会存在一次是抛掷次数最大的。假设有一次伯努利实验抛掷了12次才出现正面,是最多的一次,我们将这个数记为**k_max**
在伯努利实验中,很容易得出以下结论:
1、n次伯努利实验中的抛掷次数都不大于k_max。
2、n次伯努利实验过程中最少有一次抛掷次数等于k_max。

其中n和k_max有一个估算关联关系**n=2^k_max**(我也不知道怎么推导的)。

假如有一轮伯努利实验的例子如下:

**第一次试验: 抛了3次才出现正面,此时 k=3,n=1 **
**第二次试验: 抛了2次才出现正面,此时 k=2,n=2 **
**第三次试验: 抛了6次才出现正面,此时 k=6,n=3 **
第n 次试验:抛了12次才出现正面,此时我们估算, n = 2^12

假设在上面的例子中,我们总共实验了3组,那么最大实验次数k_max就是6,那么由公式**n=2^k_max**可得n=2^6!=3。由此可知,在实验次数很少时,这种估算结果误差很大。
image.png

4.2、估算的优化

在上面的3组例子中,我们称为一轮实验。如果只进行一轮的话,当n足够大时,误差也会变小,但不是足够小。
因此是否可以进行多轮实验,然后取每轮实验的**k_max**平均值。例如进行100轮实验,然后再取平均数k_max/100。最终在估算出n。下面是**LogLog**的估算公式:

其中,DVLL 对应的就是n;m对应实验的轮次;头上有一横的R就是平均数(k_max1+k_max2+…+k_maxm)/m;constant是修正因子,可以通过修改这个数值还改变误差率。

通过这种增加实验轮次,然后取平均数是LogLog的做法。而HyperLogLog采用的不是平均数而是调和平均数。调和平均数和算术平均数的最大的区别在于调和平均数不容易受大的值影响。

求平均工资:
A的是1000/月,B的30000/月。采用平均数的方式就是: (1000 + 30000) / 2 = 15500
采用调和平均数的方式就是: 2/(1/1000 + 1/30000) ≈ 1935.484

**调和平均数公式为,**∑ 是累加符号:

HypeLogLog结合调和平均数的计算公式为:

4.3、HyperLogLog的做法

4.3.1、bit串

在Redis的HyperLogLog中,其首先将要保存的元素通过hash函数计算得到其64位的hash值。并将这个hash值转换成bit串。例如hash值是5,那么bit串就是“101”。
那么为什么要转换成bit串呢?是为了和伯努利实验的正反面对应上。bit串中的0和1就对应了伯努利实验的正面和反面。假如一个数据的hash值的bit串是“100010000”,那么从低往高,从右往左数,第一个1的位置就是出现正面的次数。
那么基于上面的估算结论:我们就可以根据最大的抛掷次数来估算出大概进行了多少次实现。同样的,也就可以根据存入的数据,转化后出现1的位置k_max来估算出,大致存了多少数据。

4.3.2、分桶

HyperLogLog中的分桶其实就是应伯努利实现估算优化中的多轮。每一个桶对应一轮的伯努利实验。转换成计算机存储就是:存储的是一个单位是bit,长度为L的大数组S,将S分为m组,其中m就是多轮实验。每组占用的bit个数是平均记为P,那么存在以下关系:
1、L=S.length
2、L=m*P
3、以K为单位,数组S占用大小为L/8/1024

在Redis中,HyperLogLog中设置m为16834(16384个桶),P为6(每个桶占6位,只占用6位原因:bit串是64位的其中低14位用来计算桶的位置,也就是说还有50位用来得到第一个1出现的问题,如果1在最左边,那么1的位置最大也就是50,2^6 =64,是第一个大于50的数,因此6位就可以完全存下1的位置),那么L=168346(bit)。占用内存为168346/8/1024=12K。

** 第0组 第1组 … 第16833组 **
[000 000] [000 000] [000 000] [000 000] … [000 000]

4.3.3、做法

在Redis中,HyperLogLog首先,将保存的元素转换成对应的hash值bit串(64位)。然后根据bit串来计算应该落在桶的位置。按照从右往左的顺序,取14位(因为Redis只是将桶分为16384个=2^14 ,14位正好可以将桶完全使用上)。根据低14位计算到桶的位置之后,然后再根据剩下的50位,从右往左,找到第一个1的位置。如果1出现的位置index>oldIndex,那么index会替换掉oldIndex。否则是跳过。
image.png

参考文章
HyperLogLog在线观察** **

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/10008.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

#04 构建您的第一个神经网络:PyTorch入门指南

文章目录 前言理论基础神经网络层的组成前向传播与反向传播 神经网络设计步骤1:准备数据集步骤2:构建模型步骤3:定义损失函数和优化器步骤4:训练模型步骤5:评估模型结论 前言 在过去的几天里,我们深入了解了…

头歌实践教学平台:CG1-v2.0-直线绘制

第1关&#xff1a;直线光栅化-DDA画线算法 一.任务描述 1.本关任务 (1)根据直线DDA算法补全line函数&#xff0c;其中直线斜率0<k<1&#xff1b; (2)当直线方程恰好经过P(x,y)和T(x,y1)的中点M时&#xff0c;统一选取直线上方的T点为显示的像素点。 2.输入 (1)直线两…

使用com.google.common.collect依赖包中的Lists.transform()方法转换集合对象之后,修改集合中的对象属性,发现不生效

目录 1.1、错误描述 &#xff08;1&#xff09;引入依赖 &#xff08;2&#xff09;模拟代码 &#xff08;3&#xff09;运行结果 1.2、解决方案 1.1、错误描述 最近在开发过程中&#xff0c;使用到了com.google.common.collect依赖包&#xff0c;通过这个依赖包中提供的…

Vue踩坑,less与less-loader安装,版本不一致

无脑通过npm i less -D安装less之后&#xff0c;继续无脑通过npm i less-loader -D安装less-loader出现如下错误&#xff1a; 解决方法&#xff1a; 1) npm uninstall less与 npm uninstall less-loader 2) 直接对其版本&#xff1a; npm i less3.0.4 -D npm i less-loader…

conda删除虚拟环境命令

conda删除虚拟环境命令 删除虚拟环境的命令可以使用conda命令的remove或者env remove子命令&#xff0c;具体的实现方法如下所示&#xff1a; 使用conda remove命令删除虚拟环境&#xff1a; conda remove --name <环境名称> --all 这将删除指定名称的虚拟环境…

Python进阶之-inspect模块详解

✨前言&#xff1a; Python中的inspect模块是用于获取对象的信息的模块&#xff0c;比如查看源代码、列出成员、检查类和函数的参数等&#xff0c;非常适用于调试和反射&#xff08;introspection&#xff09;。接下来&#xff0c;我会详细解释inspect模块&#xff0c;并结合例…

es关闭开启除了系统索引以外的所有索引

1、es 开启 “删除或关闭时索引名称支持通配符” 功能 2、kibanan平台执行 POST *,-.*/_close 关闭索引POST *,-.*/_open 打开索引3、其他命令 DELETE index_* // 按通配符删除以index_开头的索引 DELETE _all // 删除全部索引 DELETE *,-.* 删除全…

鸿蒙OpenHarmony开发板解析:【系统能力配置规则】

如何按需配置部件的系统能力 SysCap&#xff08;SystemCapability&#xff0c;系统能力&#xff09;是部件向开发者提供的接口的集合。 开发前请熟悉鸿蒙开发指导文档&#xff1a;gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到。 部件配置系统…

Java入门——类和对象(上)

经读者反映与笔者考虑&#xff0c;近期以及往后内容更新将主要以java为主&#xff0c;望读者周知、见谅。 类与对象是什么&#xff1f; C语言是面向过程的&#xff0c;关注的是过程&#xff0c;分析出求解问题的步骤&#xff0c;通过函数调用逐步解决问题。 JAVA是基于面向对…

c#文心接口应用举例

"文心接口" 通常不是 C# 语言或生态系统中直接的一个术语&#xff0c;但我猜你可能是在提及百度的一个AI平台或API&#xff0c;比如百度的文心一言&#xff08;ERNIE Bot&#xff09;或其他与AI和NLP&#xff08;自然语言处理&#xff09;相关的服务。由于具体的文心…

DDOS攻击实战演示,一次DDOS的成本有多低?

DDoS攻击成本概览 分布式拒绝服务&#xff08;DDoS&#xff09;攻击以其低廉的启动成本和惊人的破坏力著称。攻击者通过黑市轻松获取服务&#xff0c;成本从几十元人民币的小额支出到针对大型目标的数千乃至数万元不等。为了具体理解这一成本结构&#xff0c;我们将通过一个简…

机器学习【如何学习】

如何学习机器学习 机器学习是一门快速发展的学科&#xff0c;学习机器学习需要掌握一定的数据基础和编程能力。但是很多人一听到数学就头大&#xff0c;就退缩&#xff0c;导致想要学习的新已经有两三年了&#xff0c; 但是学习的行动一直在心里&#xff0c;迟迟没有落地&…

常见的TypeScript类型注解符号

1、: 用途: 用于指定类型。放在变量名或函数参数后面&#xff0c;紧接着是类型。 示例: let name: string "Alice";2、string, number, boolean, void, null, undefined 用途: 基本数据类型。分别表示字符串、数字、布尔值、无返回值、空值和未定义。 示例: func…

每日两题 / 226. 翻转二叉树 98. 验证二叉搜索树(LeetCode热题100)

226. 翻转二叉树 - 力扣&#xff08;LeetCode&#xff09; 以后续遍历的方式交换当前节点的左右指针 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), ri…

机器学习-12-sklearn案例03-flask对外提供服务

整体思路 训练一个模型&#xff0c;把模型保存 写一个基于flask的web服务&#xff0c;在web运行时加载模型&#xff0c;并在对应的接口调用模型进行预测并返回 使用curl进行测试&#xff0c;测试通过 再创建一个html页面&#xff0c;接受参数输入&#xff0c;并返回。 目录结…

Java使用equals和==的区别

对于在范围 -128 到 127 之间的数值&#xff0c;Java 会使用缓存的 Integer 对象。因此&#xff0c;这些数值的 Integer 对象可以使用 进行比较。 对于不在这个范围的数值&#xff0c;Java 每次可能创建一个新的 Integer 对象。这样&#xff0c;即使两个 Integer 对象代表相同…

CSS悬浮动画

<button class"btn">悬浮动画</button>.btn {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);padding: 10px 20px;width: 200px;height: 50px;background-color: transparent;border-radius: 5px;border: 2px solid powderblu…

C++继承深入解析

目录标题 1. 继承的基本概念1.1 为什么使用继承1.2 继承的语法 2. 继承的类型2.1 公共继承&#xff08;Public Inheritance&#xff09;公共继承的特性公共继承的语法公共继承的实际应用 2.2 保护继承&#xff08;Protected Inheritance&#xff09;保护继承的特性保护继承的语…

R2S+ZeroTier+Trilium

软路由使用ZeroTier搭建远程笔记 软路由使用ZeroTier搭建远程笔记 环境部署 安装ZeroTier安装trilium 环境 软路由硬件&#xff1a;友善 Nanopo R2S软路由系统&#xff1a;OpenWrt&#xff0c;使用第三方固件nanopi-openwrt。内网穿透&#xff1a;ZeroTier。远程笔记&…

银河麒麟操作系统 v10 离线安装 Docker v20.10.9

1查看系统版本 [rootweb-0001 ~]# cat /etc/os-release NAME"Kylin Linux Advanced Server" VERSION"V10 (Tercel)" ID"kylin" VERSION_ID"V10" PRETTY_NAME"Kylin Linux Advanced Server V10 (Tercel)" ANSI_COLOR"…