返回

剖析水塘抽样算法:从未知规模数据集中随机采样

后端

水塘抽样算法,又称水库抽样算法,是一种从未知规模数据集中随机抽取单个元素的算法。其巧妙之处在于,它无需加载整个数据集,而是通过迭代遍历数据,保持固定大小的水塘,并根据一定概率将新元素加入或替换水塘中的元素,从而实现高效的随机采样。

水塘抽样算法的原理如下:

  1. 初始化一个固定大小的水塘,其大小等于要抽取的元素个数。
  2. 遍历数据集,依次处理每个元素。
  3. 对于每个元素,计算它被选中进入水塘的概率。
  4. 根据计算出的概率,随机决定是否将该元素加入水塘。
  5. 如果该元素被选中,则将其加入水塘,并随机替换掉水塘中已经存在的某个元素。
  6. 重复步骤3-5,直到遍历完整个数据集。
  7. 水塘中的元素即为从数据集中随机抽取的元素。

水塘抽样算法具有以下特点:

  • 算法简单易懂,实现方便。
  • 算法在数据量很大的情况下依然能够高效地工作。
  • 算法可以保证每个元素被选中的概率与数据集中其他元素被选中的概率相同。

水塘抽样算法的优缺点如下:

  • 优点:
    • 不需要预先知道数据集中元素的个数。
    • 不需要加载整个数据集到内存。
    • 算法时间复杂度为O(n),其中n是数据集中元素的个数。
  • 缺点:
    • 算法只能从数据集中随机抽取单个元素。
    • 算法不能保证每个元素被选中的概率与数据集中其他元素被选中的概率完全相同。

水塘抽样算法的应用场景包括:

  • 从大规模数据集中随机抽取样本。
  • 从流数据中随机抽取样本。
  • 从分布式数据集中随机抽取样本。

下面是一个使用Python实现的水塘抽样算法的简单Demo:

import random

def reservoir_sampling(stream, k):
    """
    从数据流中随机抽取k个元素。

    参数:
        stream:数据流。
        k:要抽取的元素个数。

    返回:
        从数据流中随机抽取的k个元素。
    """

    # 初始化水塘
    reservoir = []
    for i in range(k):
        reservoir.append(next(stream))

    # 遍历数据流
    for i, x in enumerate(stream):
        # 计算当前元素被选中进入水塘的概率
        p = k / (i + 1)

        # 根据计算出的概率,随机决定是否将该元素加入水塘
        if random.random() < p:
            # 将当前元素加入水塘,并随机替换掉水塘中已经存在的某个元素
            reservoir[random.randint(0, k - 1)] = x

    # 返回水塘中的元素
    return reservoir


# 使用水塘抽样算法从数据流中随机抽取10个元素
stream = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
k = 10
sample = reservoir_sampling(stream, k)
print(sample)

输出结果:

[1, 3, 5, 6, 8, 9, 10, 2, 7, 4]

从输出结果可以看出,水塘抽样算法成功地从数据流中随机抽取了10个元素。