题目描述:随机的从大小为n的数组中选取m个整数,要求每个元素被选中的概率相等。
分析与解答:首先从有N个元素的数组中随机选出一个元素,然后把这个选中的数字与数组中第一个元素交换,接着从数组后面N-1个数字中选出1个元素与数组中第二个元素交换,以此类推,直到选出m个数字为止,数组前m个数字就是随机选出来的m个数字,且他们被选中的概率相同。
————————————————
版权声明:本文为优快云博主「蜡笔小新灬」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/qq_42013574/article/details/89103823
import random
def getRandomM(a, n, m):
if a == None or n <= 0 or n < m:
print("参数不合理")
return
i = 0
while i < m:
j = random.randint(i, n - 1) # 获取[i到n-1]间的随机数
# 随机选出的元素放到数组的前面
tmp = a[i]
a[i] = a[j]
a[j] = tmp
i += 1
if __name__ == "__main__":
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
n = 10
m = 6
getRandomM(a, n, m)
i = 0
while i < m:
print(a[i], end=" ")
i += 1
上题中如果n的大小不确定(可以认为是⼀个数据流),如何做?
链接:https://www.nowcoder.com/questionTerminal/12796031452e4ced8a16255bb02c4168
来源:牛客网
蓄水池抽样
具体方法:我们先选取前m个数放入池中,然后我们每次以m/k的概率选择第k(k>m)个数a[k],然后再在蓄水池中随机选取一个元素a[j],交换a[k]和a[j];
代码:
[cpp] view plain copy
- Init : a reservoir with the size: m
- for i= m+1 to N
- j=random(1, i);
- if( j < m)
- SWAP the jth value and ith value
- end for
接下来证明:
假设当前是i+1, 按照我们的规定,i+1这个元素被选中的概率是m/i+1,也即第 i+1 这个元素在蓄水池中出现的概率是m/i+1
此时考虑前i个元素,如果前i个元素出现在蓄水池中的概率都是m/i+1的话,说明我们的算法是没有问题的。
对这个问题可以用归纳法来证明:m < i <=N
1.当i=m+1的时候,蓄水池的容量为m,第m+1个元素被选择的概率明显为m/(m+1), 此时前k个元素出现在蓄水池的概率为 m/(m+1),
很明显结论成立。
2.假设当 j=i 的时候结论成立,此时以 m/i 的概率来选择第i个元素,前i-1个元素出现在蓄水池的概率都为m/i。
证明当j=i+1的情况:
即需要证明当以 m/i+1 的概率来选择第i+1个元素的时候,此时任一前i个元素出现在蓄水池的概率都为m/(i+1).
前i个元素出现在蓄水池的概率有2部分组成, ①在第i+1次选择前得出现在蓄水池中; ②得保证第i+1次选择的时候不被替换掉
①.由2知道在第i+1次选择前,任一前i个元素出现在蓄水池的概率都为m/i
②.考虑被替换的概率:
首先要被替换得第 i+1 个元素被选中(不然不用替换了)概率为 m/(i+1),其次是因为随机替换的池子中m个元素中任意一个,所以不幸被替换的概率是 1/m,故 前i个元素(池中元素)中任一被替换的概率 = m/(i+1) * 1/m = 1/(i+1),则(池中元素中)没有被替换的概率为: 1 - 1/(i+1) = i/(i+1)
综合① ②,通过乘法规则
得到前i个元素出现在蓄水池的概率为 m/i * i/(i+1) = m/(i+1)
故证明成立