@[TOC](算法–随机算法之水塘抽样(Reservoir sampling)以及算法证明)
假设有这么一个问题,在人流穿梭的上海南京路的劲头,有电视台在采访,要求随机在一个小时内抽取一个人来进行采访.
首先分析这个问题:
1.你不能要求所有人都在这里等着,主要是电视台给的备用凳子不够只能保持一个人在凳子上.
2.不知道总数,人流是随机的不知道总数,所以不能得到总数N,然后通过1/N来得到相应的人.
3.怎么办?
最开始这个问题是在高纳德的<<计算机程序设计艺术>>一书中提出来的原本的问题是:
可否在一未知大小的集合中,随机取出一元素?
这个问题可以转换成上述问题.
当然这个问题还有很多种问法:
在不知道文件总行数的情况下,如何从文件中随机的抽取一行?
当内存无法加载全部数据时,如何从包含未知大小的数据流中随机选取k个数据,并且要保证每个数据被抽取到的概率相等?
等等.
然后既然存在问题那么我们就来分析问题.众所周知取得随机元素的最直观的方法就是算总数,打标签.利用rand(原理线性同余方法,不做过多介绍,非本节重点)系列函数取其中的一个数,命中则取之.
代码实现起来也非常简单.
//go源码实例-随机取10个字符中的一个
package mainimport ("fmt""math/rand""time"
)func main() {str := "qwertyuiop"rand.Seed(time.Now().Unix())key := rand.Int31()%10fmt.Printf(string(str[key]))
}
现在我们来说下位置情况下的算法
首先在再取到第一个元素的时候只有一个元素就是这个元素,概率为1/1.
再去到第二个元素的时候,随机产生1-2中间的一个数如果产生的数是2则取2,舍掉1.
再取第三个元素,随机产生1-3之间的一个数如果得到3则舍去之前的元素留下3.
…
取第n个元素的时候在1-n之间取随机数如果取到n则舍去之前的保留n.
直到所有的元素取尽.
算法证明:
数学归纳法
1.当n=1的时候
元素被取得的概率为1.符合条件
2.当n=2的时候.
2被取的概率为1/2
.
1被取的概率为1*1/2 = 1/2
.
3.当n=3时.
3被取的概率为1/3
1或2被取得概率为1/2(上一步算的的概率)*2/3(这一步随机到1或者2的概率)=1/3
4.假设当n=m时每个元素被取的概率是1/m
5.当n=m+1时
第m+1个元素取得概率是1/(m+1)
前m个元素被取的概率则是1/m*(m/(m+1))=1/(m+1)
6.证明完毕,命题成立