详细介绍:Count-min Sketch 算法

介绍

Count-min Sketch 是一个概率数据结构,用于在数据流中快速估算元素的出现频率。它的核心优势是速度快、占用内存极少,但代价是计算出的频率是一个近似值(总是大于等于真实值)

工作原理

数据结构

CMS可以想象成一个二维的表格(数组):

  • 宽度为w
  • 深度为d
  • 配合d个不同的哈希函数`(h1,h2,…, hn)

初始时,这个二维数组中的所有计数器都被初始化为0。

两种基本操作

  • 更新操作update(element, count)
    当来了一个元素及其计数时:

    1. 对于每一行i(从1到d);
    2. 计算哈希值:position=hi(element)%wposition = h_i(element) \% wposition=hi(element)%w
    3. 将第i行、第position列的计数器值增加count
  • 查询操作estimate(element):
    当要查询某个元素的估算频率时:

    1. 对于每一行i(从1到d
    2. 计算哈希值:position=hi(element)%wposition = h_i(element) \% wposition=hi(element)%w
    3. 读取第i行、第position列的计数器值
    4. 在所有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 的工作原理、优势(节省空间)和局限性(可能高估频率)。

posted @ 2025-12-26 16:56  clnchanpin  阅读(37)  评论(0)    收藏  举报