详细介绍:Count-min Sketch 算法
介绍
Count-min Sketch 是一个概率数据结构,用于在数据流中快速估算元素的出现频率。它的核心优势是速度快、占用内存极少,但代价是计算出的频率是一个近似值(总是大于等于真实值)。
工作原理
数据结构
CMS可以想象成一个二维的表格(数组):
- 宽度为
w - 深度为
d - 配合
d个不同的哈希函数`(h1,h2,…, hn)
初始时,这个二维数组中的所有计数器都被初始化为0。
两种基本操作
更新操作
update(element, count):
当来了一个元素及其计数时:- 对于每一行
i(从1到d); - 计算哈希值:position=hi(element)%wposition = h_i(element) \% wposition=hi(element)%w
- 将第
i行、第position列的计数器值增加count
- 对于每一行
查询操作estimate(element):
当要查询某个元素的估算频率时:- 对于每一行
i(从1到d) - 计算哈希值:position=hi(element)%wposition = h_i(element) \% wposition=hi(element)%w
- 读取第
i行、第position列的计数器值 - 在所有
d个读取到的值中,取最小值作为该元素频率的估算值
- 对于每一行
为什么取最小值?
由于哈希冲突的存在,不同的元素可能会被映射到同一个计数器上,导致该计数器的值比其中任何一个元素的真实频率都大。因此,真实的频率不可能超过任何一个计数器值。取所有计数器中的最小值,是所有这些“偏大估计”中最接近真实值的一个。
一个简单的例子
假设我们有一个宽度 w = 5,深度 d = 3 的 Count-min Sketch,使用 3 个不同的哈希函数。
场景: 我们想要统计单词 “apple”, “banana”, “apple”, “cherry”, “apple”, “banana” 的出现频率。
步骤 1:初始状态
我们的二维数组全是 0:
行1: [0, 0, 0, 0, 0]
行2: [0, 0, 0, 0, 0]
行3: [0, 0, 0, 0, 0]
步骤 2:处理第一个 “apple”
假设哈希结果:
- h₁(“apple”) % 5 = 1
- h₂(“apple”) % 5 = 3
- h₃(“apple”) % 5 = 0
更新后数组变为:
行1: [0, 1, 0, 0, 0] ← 在第1列加1
行2: [0, 0, 0, 1, 0] ← 在第3列加1
行3: [1, 0, 0, 0, 0] ← 在第0列加1
步骤 3:处理 “banana”
假设哈希结果:
- h₁(“banana”) % 5 = 3
- h₂(“banana”) % 5 = 1
- h₃(“banana”) % 5 = 2
更新后数组变为:
行1: [0, 1, 0, 1, 0] ← 在第3列加1
行2: [0, 1, 0, 1, 0] ← 在第1列加1
行3: [1, 0, 1, 0, 0] ← 在第2列加1
步骤 4:处理第二个 “apple”
使用相同的哈希函数,我们再次在位置 (1, 3, 0) 上加1:
行1: [0, 2, 0, 1, 0]
行2: [0, 1, 0, 2, 0]
行3: [2, 0, 1, 0, 0]
步骤 5:查询 “apple” 的频率
我们查看位置 (1, 3, 0) 的值:
- 行1 位置1: 2
- 行2 位置3: 2
- 行3 位置0: 2
取最小值 2,这是正确的(此时 “apple” 确实出现了 2 次)。
步骤 6:演示哈希冲突
假设我们查询 “orange”(它从未出现过),但它的哈希结果是:
- h₁(“orange”) % 5 = 1
- h₂(“orange”) % 5 = 3
- h₃(“orange”) % 5 = 2
我们查看位置 (1, 3, 2) 的值:
- 行1 位置1: 2
- 行2 位置3: 2
- 行3 位置2: 1
取最小值 1,这是错误的(“orange” 的真实频率是 0)。这就是 CMS 的特性:永远不会低估,但可能高估。
这个例子清晰地展示了 Count-min Sketch 的工作原理、优势(节省空间)和局限性(可能高估频率)。
浙公网安备 33010602011771号