• 水塘抽样算法


    水塘抽样算法

    水塘抽样算法是用于解决,对于一个未知长度的数据流进行随机采样的问题的。本文介绍几个算法及其变形。
    说明:伪代码中S代表未知长度数据流,n为S的实际长度,S.CURRENT表示S当前值,S.NEXT代表S指针下移,R代表结果,k表示结果需要保留的个数。

    简单抽样一个元素

    Sample(S[1..n],R[1]) //s是未知长度的数据流
      R[1]=S[1]          //取第一个值,初始化结果
      count := 1         //计数器,记录当前为数据流中的第几位
      while S has data
    	count++
        j := random(0, 1)   //根据当前位置生成随机数[0,1)
    	if j < 1/count
    		R[1] = S.CURRENT
    	S.NEXT
    

    证明:
    一个元素的时候:被抽中的概率为1
    两个元素的时候:第一个元素留下的概率为1-1/2,第二个元素留下的概率为 1/2
    三个元素的时候:第一个元素留下的概率为(1-1/2)*(1-1/3)=1/3,第二个元素留下的概率为1/2*(1-1/3)=1/3,第三个元素留下的概率为1/3
    以此类推,当判断到第i个元素时,第m(0<m<=i)个元素留下的概率为1/m*(1-1/(m+1))*(1-1/(m+2))...(1-1/(i))=1/i就可以保证所有元素的概率相等

    算法R

    ReservoirSample(S[1..n], R[1..k])
      //使用前l个值初始化结果
      for i = 1 to k
          R[i] := S[i]
    
      while S has next
    	i++
        j := random(1, i)   //取1~i的随机数
        if j <= k
            R[j] := S[i]	//如果位置落在k之内则替换掉对应的R[j]
    

    证明:
    第m轮时,R中的每个元素被替换的概率为1/k*k/m=1/m,i能替换掉R中值的概率为k/m,所以到第i轮,第m(0<m<=i)个元素是k/m*(1-1/(m+1))*(1-1/(m+2))...*(1-1/i)=k/i

    随机值排序水塘算法

    ReservoirSample(S[1..?], R[1..k])
      H = new max-priority-queue	//有序队列
      while S has data
        r = Random(0,1)
        if H.Count < k
          H.Insert(r, S.Current)
        else
          if H.Maximum > r
            H.Extract-Max()
            H.Insert(r, S.Current)
        S.Next
    

    该算法是对每个值进行随机值计算,并保留最大的k个随机值

    带权重的随机值排序水塘算法

    有些情况,采样的数据是有权重的,下面介绍几个带权重的水塘抽样算法

    A-Res算法

    ReservoirSample(S[1..?], R[1..k])
      H = new min-priority-queue
      while S has data
        r = Random(0,1) ^ (1/S.Weight)  // 随机值取0-1随机值的1/weight的次方
        if H.Count < k
          H.Insert(r, S.Current)
        else
          if H.Minimum < r		//如果比队列中最小值大,则替换最小值
            H.Extract-Min()	
            H.Insert(r, S.Current)
        S.Next
    

    该算法的随机值是使用了生成随机值的1/weight次方,可以增加高权重的随机值

    分布式随机抽样

    有些情况需要在分布式的情况下进行随机抽样,可以采用A-Res算法在分布式机器上运行,最后使用各机器上的值进行一起排序达到随机抽样的效果。

    洗牌算法Fisher-Yates算法

    一种需求是需要对所有的值进行乱序排序

    shuffle(S[1..?])
    	for(i:= 1 .. n-1)
    		r:= random(0,i)		//[0,i]的随机值
    		swap(a[r],a[i])
    

    JDK中的Collections.shuffle也是使用这个算法进行数组乱序的。

    参考:https://en.wikipedia.org/wiki/Reservoir_sampling

  • 相关阅读:
    【MySQL】学生成绩
    【MySQL】统计累计求和
    【MySQL】查询不在表中的数据
    【MySQL】排名函数
    【Python】数据处理分析,一些问题记录
    【Github】如何下载csv文件/win10如何修改txt文件为csv文件
    【Windows】github无法访问/hosts文件只能另存为txt
    【C/C++】拔河比赛/分组/招商银行
    【C/C++】金币
    【C/C++】旋转数组的最小数字/ 剑指offer
  • 原文地址:https://www.cnblogs.com/resentment/p/7435913.html
Copyright © 2020-2023  润新知