reservoir sampling 在无限数据流中需单次遍历、o(k)空间,确保每个元素被选概率严格为k/n;必须用std::uniform_int_distribution避免rand()偏差,且两次随机调用须独立,seed固定方可重现结果。

无限数据流下 Reservoir Sampling 的核心约束
它不能“等数据来完再处理”,必须边读边决定是否保留——这是所有无限流采样算法的硬边界。Reservoir Sampling 正是为此设计的:只用 O(k) 空间、单次遍历、每个元素被选中的概率严格为 k / n(n 是当前已读元素总数)。
为什么不能用 vector::size() 或提前预知 n
无限流意味着你永远不知道 n 有多大,也永远无法调用 size()。常见错误是写成:
for (int i = 0; i <p>这在流式场景中根本不可行。正确做法是用计数器 <code>count</code> 替代 <code>n</code>,每读一个新元素就递增一次:</p>
- 前
k个元素无条件进蓄水池 - 第
count个元素(count > k)以概率k / count被选中 - 若选中,就随机替换蓄水池中某个已有元素(用
rand() % k)
C++ 实现时 rand() 的坑必须避开
rand() 在 C++11 后已被视为过时,且其低比特位周期短、分布不均,尤其在取模 % k 时容易引入偏差。更严重的是:rand() % count 并不等于均匀采样 [0, count)。
正确做法是用 std::uniform_int_distribution:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
std::random_device rd; std::mt19937 g(rd()); std::uniform_int_distribution<int> dist(0, count - 1); int r = dist(g);</int>
注意:分布对象 dist 必须按需重建(上限 count - 1 每轮都变),不能复用旧实例。
实际流接口怎么对接
你不会拿到一个 std::vector,而是类似这样的输入模式:
-
std::istream&(逐行/逐整数读) - 回调函数(如
on_next(int value)) - 迭代器风格的
next()+has_next()
无论哪种,主循环结构都一样:
int count = 0;
std::vector<int> reservoir(k);
while (auto val = read_next()) {
if (count dist(0, count);
if (dist(g) <p>最后一句容易错:两次 <code>dist(g)</code> 调用必须独立,不能复用同一个随机数去同时判断“是否入选”和“替换谁”。</p>
<p>最易被忽略的一点:蓄水池算法本身不维护顺序,也不保证输出可重现;如果你需要确定性结果(比如测试或调试),必须固定 <code>std::mt19937</code> 的 seed,并确保每次流输入完全一致——因为哪怕多读一个空行,<code>count</code> 就变,整个采样路径就不同。</p></int>C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










