设 Rand(A,B) 随机返回[A,B]中的一个整数。
问题1:从N个数中随机抽取m个数。
解1(笨), 通过“改变样本空间”,抽取-排除的形式,先出抽一个,第二个抽碰到第一个值就忽略继续抽,依次类推知道抽满m个。
i=0
while(i<m){
x=Rand(1,N)
if(x not in S){
insert(x,S);
i++
}
}
缺点:循环次数可能比预计多很多,如果随机不充分,可能会死循环。
解2(good)递归,A先对前N-1个数随机抽取m-1个数,B再从1->N中随机选一个数x,如果x不在前面m-1个中,就插入x,否则插入N;
for( i = N-m+1 ,i <=N ;i++){
x=Rand(1,i)
if(x not in S) insert(x,S)
else insert(i,S)
}
方法结果证明:
假设前N-1个数已经随机抽去m-1个数,结果集合S
对于最后一个数x
x in S 插入N p1 = (m-1)/N
x not in S && x==N 插入N p2 = 1/N p1+p2 =m/N
x not in S && x!=N 插入x p = {(N-(m-1)) - 1 }/N =(N-m)/N
可知:最后一个数插入的概率 = m/N
这种方法可以增量随样!!!!
成功完成一次抽样的概率 p = m*(m-1)*(m-2)*...1 / N *(N-1)*(N-2)* ...(N-m+1) = 1 / C(N,m)
复杂度讨论:
外层循环 O(M)
主要在于内层的集合查找判断
#1 关于S数据结构1:bitmap, 当N不是很大的时候,把每一个数都看做1个bit位,很好判断x是否在s中 算法复杂度为常数O(M)
#2 关于S数据结构2:平衡树,当N很大时,查找树的复杂度为O(logM), 算法总复杂度为O(MlogM)