水塘抽样算法(Spark RangePartitioner的抽样算法)

1、水塘抽样解决的问题

        对一个数量未知的样本,希望只经过一次遍历就完成随机抽样,即时间复杂度O(n)。因为样本数量未知,因此就不能通过random函数直接随机抽样。

2、水塘抽样的逻辑

        希望在n个样本中随机抽取k个数据。即每个数据被抽取的概率为k/n。

        首先我们创建一个k个成员的数组out,用来存放预期抽出的样本。

        (1)当k>=n时,每一个样本被抽取的概率都为100%。因此都可以直接放入数组out。

        (2)当k<n时,对于第n个样本,我们希望其被抽取的概率为k/n,因此我们需要设计出k/n的概率将其放入数组out(创建一个0-n之间的随机数,判断其大于k的概率就是k/n)。同时out中会有一个数据会被踢出。

        (3)当k<n时,对于数组out中已存的数据,其在上一轮被留下的概率为k/(n-1) ,这一轮发生不抽样的概率(n-k)/n,发生抽样的概率为k/n。发生抽样不被抽走的概率为(k-1)/k。因此其被抽取的概率为:

        上一轮抽中的概率 * (不发生抽样的概率 + 发生抽样的概率 * 不被抽走的概率)

        k/(n-1) * ((n-k)/n + k/n * (k-1)/k) = k/n

        概率也为k/n

3、代码实现

public class PoolSimple {
    public static void main(String[] args) {
        int[] data = new int[]{1, 3, 4, 5, 2, 12, 53, 54, 2, 4, 325, 46, 7, 2, 2, 423, 563, 23, 24, 6, 1};
        int[] result = new Simple(5).run(data);
        for (int item : result) {
            System.out.println(item);
        }
    }
}

class Simple {

    private final int[] out;
    private final int num;
    private final Random random;

    public Simple(int num) {
        this.num = num;
        this.out = new int[num];
        this.random = new Random();
    }

    
    public int[] run(int[] in) {
        for (int i = 0; i < in.length; i++) {
            if (i < num) {
                out[i] = in[i];
            } else {
                int t1 = random.nextInt(i + 1);
                if (t1 >= num) {
                    int t2 = random.nextInt(num);
                    out[t2] = in[i];
                }
            }
        }
        return out;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值