应用场景:大数据流中的随机抽样问题。 即:当内存无法加载全部数据时,如何从包含未知大小的数据流中随机选取k个数据,并且要保证每个数据被抽取到的概率相等。
数学证明
采样策略策略
- 对于前k个数,我们全部保留。
- 对于第i(i>k)个数,我们以
的概率保留第i个数,并以
的概率与前面已选择的k个数中的任意一个替换。
vector<int> ReservoirSampling(vector<int>& results, vector<int>& nums, int k)
{
vector<int> results(k);
int n = nums.size();
// 对于前k个数,我们全部保留
for (int i = 0; i < k; ++i) {
results[i] = nums[i];
}
// 后面的数据,进行替换
for (int i = k; i < n; ++i) {
int random = rand() % i;
if (random < k) {
results[random] = nums[i];
}
}
return results;
}
// 时间复杂度O(n);
随机数索引
给定一个可能含有重复元素的整数数组,要求随机输出给定的数字的索引。 您可以假设给定的数字一定存在于数组中。
注意:数组大小可能非常大。 使用太多额外空间的解决方案将不会通过测试。
class Solution {
vector<int> &nums;
public:
Solution(vector<int> &nums) : nums(nums) {}
int pick(int target) {
int ans;
for (int i = 0, cnt = 0; i < nums.size(); ++i) {
if (nums[i] == target) {
++cnt; // 第 cnt 次遇到 target
if (rand() % cnt == 0) {
ans = i;
}
}
}
return ans;
}
};
// pick时间复杂度O(n)