一、线性同余法生成随机数
线性同余法是一种使用很广泛的伪随机数生成器算法:
R(n+1)=(A * R(n) + C) mod M; // 一般选取互质的A、C、M,而且M一般比较大
简而言之,线性同余法就是将当前的伪随机数值乘以A再加上C,然后将除以M得到的余数作为下一个伪随机数。
二、洗牌算法
FisherYates洗牌算法:
1. 初始化原始数组和新数组,原始数组长度为n(已知);
2. 从还没处理的数组(假如还剩k个)中,随机产生一个[0, k)之间的数字p(假设数组从0开始);
3. 把第p个数取出与第k个数交换;
4. 重复步骤2和3直到数字全部取完;
5. 从步骤3取出的数字序列便是一个打乱了的数列。
下面证明其随机性,即每个元素被放置在新数组中的第i个位置是1/n(假设数组大小是n)。一个元素m被放入第i个位置的概率P = 前i-1个位置选择元素时没有选中m的概率 * 第i个位置选中m的概率,即:
void Fisher_Yates_Shuffle(deque<int> &arr,vector<int> &res)
{
srand((unsigned)time(NULL));
for(int k = len - 1; k > 0; k--)
{
int p = rand()%(i + 1);
swap(arr[k], arr[p]);
}
}
三、蓄水池抽样
蓄水池抽样(Reservoir Sampling )是一个很有趣的问题,它能够在o(n)时间内对n个数据进行等概率随机抽取,例如:从1000个数据中等概率随机抽取出100个。另外,如果数据集合的量特别大或者还在增长(相当于未知数据集合总量),该算法依然可以等概率抽样。
1、如何随机从n个对象中选择一个对象?
【解法】:每次以1/m的概率选择是否将第m个数放入蓄水池。
【证明】:第m个数最终被选择的概率 = 选择第m个数*后面的数都不被选择,即
p
=
1
m
∗
[
(
1
−
1
m
+
1
)
∗
(
1
−
1
m
+
2
)
∗
.
.
.
∗
(
1
−
1
n
)
]
=
1
n
p=\frac 1m *[(1-\frac 1{m+1})*(1-\frac 1{m+2})*...*(1-\frac 1{n})]=\frac 1{n}
p=m1∗[(1−m+11)∗(1−m+21)∗...∗(1−n1)]=n1
2、如何随机从n个对象中选择k个对象?
【解法】:每次以k/m的概率选择是否将第m个数放入蓄水池。
【证明】:第m个数最终被选择的概率 = 选择第m个数*(后面的数都不被选择+后面的数被选择但是不替换第m个数),即
p
=
k
m
∗
[
(
1
−
k
m
+
1
+
k
m
+
1
∗
(
1
−
1
k
)
)
∗
(
1
−
k
m
+
2
+
k
m
+
2
∗
(
1
−
1
k
)
)
∗
.
.
.
∗
(
1
−
k
n
+
k
n
∗
(
1
−
1
k
)
)
]
=
k
n
p=\frac km *[(1-\frac k{m+1} +\frac k{m+1}*(1-\frac 1{k}))*(1-\frac k{m+2} +\frac k{m+2}*(1-\frac 1{k}))*...*(1-\frac k{n} +\frac k{n}*(1-\frac 1{k}))]=\frac k{n}
p=mk∗[(1−m+1k+m+1k∗(1−k1))∗(1−m+2k+m+2k∗(1−k1))∗...∗(1−nk+nk∗(1−k1))]=nk
参考:https://blog.csdn.net/chengqiuming/article/details/83037469
https://blog.csdn.net/qq_26399665/article/details/79831490
https://blog.csdn.net/huagong_adu/article/details/7619665