蓄水池算法的设计和实现
蓄水池算法的设计和实现
作者: Grey
原文地址:
要解决的问题#
假设有一个源源吐出不同球的机器, 只有装下10个球的袋子,每一个吐出的球,要么放入袋子,要么永远扔掉,如何做到机器吐出每一个球之后,所有吐出的球都等概率被放进袋子里
规则#
吐出1到10号球,完全入袋, 引入随机函数f(i),提供一个值i,等概率返回1-i的一个数字, 当K号球吐出的时候(K>10) ,我们通过以下决策决定是否要入袋
-
引入随机函数:f(K) , 如果返回10以内的数,则入袋,如果返回10以外的数,则扔掉, 即:10/K的概率决定球是否入袋。
-
第一步中如果决定入袋,那么袋子中已经存在的球以等概率丢弃一个。
证明#
情况1#
当K为1~10号的时候,根据我们的规则,入袋概率100%,每个球等概率
情况2#
当K为任意一个大于10的数,我们假设K为927,即:当927这个编号的球吐出来的时候,我们考虑:
A. 1 ~ 10 号球的入袋概率
B. 大于10号小于等于927号球的入袋概率
如果A和B都可以做到等概率
那么可以推广到普遍情况都是等概率的。
A情况,927号球到来的时候,我们可以考虑一下5号球的入袋概率是多少?
5号球需要存活到927号球到来,必须满足:
-
11号球来的时候,5号球活下来
-
12号球来的时候,5号球活下来
-
...
-
926号球来的时候,5号球活下来
11号球来的时候,5号球怎么活下来呢?先看一下,11号球到来,5号球如果没有活下来的概率q
,那么5号球活下来的概率就是 1 - q
首先,根据我们的规则,11号球要以10/11
概率被选中入袋,且5号球要以非常倒霉的情况1/10
概率被选中要替换,那么,11号球到来,5号球被替换的概率为:
(10/11 * 1/10) = 1/11
那么5号球活下来的概率就是
1 - 1/11 = 10/11
12号球到来的时候,5号球活下来的概率同理,可以计算出为11/12
13号球到来的时候,5号球活下来的概率同理,可以计算出为12/13
....
927号球到来的时候,5号球活来的概率同理:可以计算出为926/927
所以,5号球活下来的概率为:
10/11 * 11/12 * 12/13 ... * 925/926 * 926/927 = 10/927
同理,1 ~ 10 号球任意一号都可以按照5号球的计算方式计算,概率均为:10/927
A情况是等概率的。
再看B情况,我们可以假设一个大于10但是小于927的球,比如,15号球,考虑入袋概率
15号球要在927号球到来的时候,还在袋子中,则需要保证:
15号球在当时被吐出的时候,以10/15
概率被选中,且在
16号球到来的时候,15号球活下来,概率根据A的计算逻辑,为15/16
17号球到来的时候,15号球活下来, 同理,为16/17
....
926号球到来的时候,15号球活下来,概率为925/926
927号球到来的时候,15号球活下来,概率为926/927
所以,927号球到来的时候,15号球活下来的概率是:
10/15 * 15/16 * 16/17 .... * 925/926 * 926/927 = 10/927
同理,任意大于10小于927号球的概率都可以根据15号球的计算逻辑推算出来,均为10/927
A情况和B情况概率都是10/927
所以规则满足了题目的要求。
代码#
public class Code_0058_ReservoirSampling {
public static class RandomBox {
private int[] bag;
// 袋子容量
private int capacity;
// 第几号球
private int count;
public RandomBox(int capacity) {
bag = new int[capacity];
this.capacity = capacity;
count = 0;
}
// 随机函数,等概率生成1-max之间随机的一个数字
// Math.random() -> 生成[0,1)范围内的数
// (int)i 是对i进行向下取整
private int rand(int max) {
return (int) (Math.random() * max) + 1;
}
public void add(int num) {
// 球个数增加
count++;
// 如果球的个数没有超过容量
if (count <= capacity) {
// 则入袋
bag[count - 1] = num;
} else if (rand(count) <= capacity) {
// 否则以N/count的概率入袋
bag[rand(capacity) - 1] = num;
}
}
// 返回袋子中最终选中的球
public int[] choices() {
int[] res = new int[capacity];
System.arraycopy(bag, 0, res, 0, capacity);
return res;
}
}
}
更多#
参考资料#
作者:GreyZeng
出处:https://www.cnblogs.com/greyzeng/p/15311295.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
你可以在这里自定义其他内容
本文来自博客园,作者:Grey Zeng,转载请注明原文链接:https://www.cnblogs.com/greyzeng/p/15311295.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· Vue3状态管理终极指南:Pinia保姆级教程