Rand()
1.拒绝采样
用rand7()实现rand10()
#include <bits/stdc++.h>
using namespace std;
int rand7(){
return rand() % 7 + 1;
}
int rand10(){
// (randX - 1) * Y + rand7() 可以实现randX_Y()
while (true){
int a = rand7();
int b = rand7();
int num = (a - 1) * 7 + b;
if (num <= 40) return num; // 此时拒绝采样的击中率是40/49
// 此时a相当于是rand9()
a = num - 40
b = rand7();
num = (a - 1) * 7 + b;
// 此时拒绝采样的击中率为60/63,算上上次未击中,此时到达当前这步的概率是9/49 * 60/63
if (num <= 60) return num;
// 此时a相当于rand3();
a = num - 60;
b = rand7();
num = (a - 1) * 7 + b;
// 当前击中率20/21,到达当前这步的概率9/49 * 3/63 * 20/21
if (num <= 20) return num;
}
// while循环的执行一次的概率是40/49 + 9/49 * 60/63 + 9/49 * 3/63 * 20/21
return -1;
}
int main(void){
// 通过rand()7实现rand()10并保证概率相等
int ans = rand10();
cout << ans << "\n";
return 0;
}
参考LeetCode
2.蓄水池抽样
能解决输入过大,导致无法一次将所有数据输入时,如何保证每个元素被取到的概率是一样的情况。
如果取一个元素:结论就是,对于当前第i个元素,以1/i概率将已经选取的值替换为当前值,以(1 - 1/i)的概率保留已经选到的值。
如果取k个元素:结论就是,对于当前第i个元素,以k/i概率将已经选取的值替换为当前值,以(1 - k/i)概率保留已经选取的值。
至于证明请参考蓄水池抽样证明
参考LeetCode
其他题
多数元素
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入:[3,2,3]
输出:3
示例 2:
输入:[2,2,1,1,1,2,2]
输出:2
进阶:
尝试设计时间复杂度为 O(n)、空间复杂度为 O(1) 的算法解决此问题。
注意:要求众数必须严格大于数组个数的一半。
对于多数元素这题,有两种解法让我觉得很棒。
- 随机化的方法 随机化的方法使用高等数学中级数趋向无穷的思想来证明最多取两次即可,那么满足线性时间复杂度。
- (Boyer-Moore 算法)摩尔投票法 摩尔投票法利用了众数必须过半的思想来解决该问题。