写在前面
博客参考:https://blog.csdn.net/qq_26399665/article/details/79831490
洗牌问题背景
从n个不同数中随机不重复不回放的取出m个数,n>=m。
洗牌算法的本质是:将原始的数组各个元素打散,是原数组各个数在打散数组中等概率出现。
洗牌算法
Fisher-Yates Shuffle算法
基本思路:从原始数组中随机取出一个之前没有取过的数放到新的数组中,具体的是随机产生一个索引rand % arr_size
,然后将这个位置的数添加到结果数组中,然后将原始数组中的这个数删除掉,循环往复直至原始数组没有数。
vector<int> arr(n); //原始数组
vector<int> res; //结果数组
while (!arr.empty()) {
int idx = rand() % arr.size();
res.push_back(arr[idx]);
arr.erase(arr.begin() + idx);
}
另外两个洗牌算法Knuth-Durstenfeld Shuffle,Inside-Out Algorithm就暂不介绍了。
蓄水池抽样算法
这个算法之前在博客中专门介绍过,蓄水池抽样算法步骤:
在n个数中等概率不放回取m个数,怎么取?
我们维护m大小的池子,
- 当接收第i个数时,i小于m,则直接放到池子中;
- 当i>=m时,则在[0,i]产生一个随机数d,若d落在[0,m-1]范围内,则用接收到的第i个数替换蓄水池中第d个数
- 反复2
蓄水池算法的本质是,若数字索引落在池子范围内,则直接放入池子,若不在池子范围内,则随机产生一个落在池子内的索引,然后将这个数字重映射到这个索引处(即替换)。