大数据算法-蓄水池抽样算法(Reservoir Sampling)

大数据算法-蓄水池抽样算法(Reservoir Sampling)_第1张图片

背景:

给定一个数据流,数据流长度N很大,且N直到处理完所有数据之前都不可知,从 N 个样本中随机选择 K 个样本,其中 N 非常大(以至于 N 个样本不能同时放入内存)或者 N 是一个未知数,请问如何在只遍历一遍数据(O(N))的情况下,能够随机选取出k个不重复的数据:

  1. 数据流长度N很大且不可知,所以不能一次性存入内存。
  2. 时间复杂度为O(N)。
  3. 随机选取k个数,每个数被选中的概率为k/N。

思路: 

假设数据序列的规模为 n,需要采样的数量的为 k。

首先构建一个可容纳 k 个元素的数组,将序列的前 k 个元素放入数组中。然后从第 k+1 个元素开始迭代直到数组结束,在输入数据S的第 i 个元素中,算法生成一个随机数 j∈[1,i] , 如果 j <= k, 那么蓄水池的第 j 个元素以 k/n 的概率被替换为 S中的第 i 个元素。当遍历完所有元素之后,数组中剩下的元素即为所需采取的样本。

step 0: 假设要抽取K个样本,准备一个K个元素的数组A

step 1: 把输入数据的前K个数放入数组A中;

step 2:遍历输入数据的第i=K+1, K+2,……个数:x

             生成一个随机数 r,r的取值范围 [0, K + i),如果 r

step 3:遍历所有数据后,得到A,返回结果。

JAVA代码如下: 

package cn.lens.test;

import java.util.Random;

/**
 * @Author: Lens
 * @Date: 2021/7/22 9:29
 */
public class ReservoirSampleTest {
    public static void main(String[] args) {
        int input_str[] = {11, 21, 23, 4, 15, 20, 83, 414, 75, 74, 82, 19, 50, 12, 13, 81, 22, 3, 44, 45, 56, 65, 44};
        int[] arr_res = sample(input_str, 5);
        for (int num : arr_res) {
            System.out.println(num);
        }
    }

    /**
     *
     * @param input 模拟原始数组
     * @param k 采样的个数
     * @return 返回采样的结果数据
     */
    public static int[] sample(int[] input, int k) {
        Random random = new Random();
        int[] res = new int[k];

        for (int i = 0; i < input.length; i++) {
            if (i < k) {
                res[i] = input[i]; //先取前k个数字放在数组里面
            } else {  //如果i > k,在 1 - i之间取一个随机数字,如果这个随机数字小于k,就替换数组,否则就继续遍历,直到结束
                int rand = random.nextInt(i);
                if (rand < k) {
                    res[rand] = input[i];
                }
            }
        }
        return res;
    }
}

应用场景:

蓄水池抽样算法的O(N)时间复杂度,O(m)空间复杂度令其适用于对流数据、大数据集的等概率抽样。如一个大文本数据,随机输出其中的几行。 

你可能感兴趣的:(java,大数据,算法,java)