编程语言
首页 > 编程语言> > 随机——蓄水池抽样算法 &等概率值

随机——蓄水池抽样算法 &等概率值

作者:互联网

package ReservoirSampling

import (
	"math/rand"
	"testing"
	"time"
)

/*
蓄水池抽样算法

假设有一个机器(以流的形式输出),它可以源源不断的吐出球,
从1号球开始吐,吐完1号球一定吐2号球,吐完2号球一定吐3号球...吐完n-1号球吐n号球,
你有一个可以装下10个球的袋子。
当前球可以进入袋子或扔掉,扔掉的球永远扔掉,就找不回来了。
当机器吐出100个球的时候,怎么保证吐出的每个球被选中的概率都是 10 /100?
1732个球被选中的概率是10/1732,3426个球被选中的概率是10/3426?

如何保证之前吐出过的所有球被选进袋子的球全均等? 不能用大容器,收集完再随机。

流程:

假设只有10个球的空间的袋子
1~10 号球,不需要决策过程,直接进袋子
往后的每个球出来的时候,以10/k的概率决定要不要他,不要他,永远都丢掉
要他,袋子中10个球等概率扔掉1个,把选中的放进来

引入一个随机函数 ,f(i)  传入i 返回 1 ~ i 等概率的数字
可以决定,吐出第k号球,1~10之外的,
我用10/k的概率,决定要或不要他 f(k)
决定要入袋子,扔谁, 袋子中10个球等概率扔一个



假如吐到17号球,3号球在袋子中的概率多少

11号球没来的时候  3号球在袋子中的概率 100%  = 1
11号球来了, 11号球点儿很正 以10/11 的概率入袋子 , 3号球非常倒霉,以1/10的概率被选中,扔掉

  10/11  * 1/10   = 1/11
  11号球到来,3号球 仍在袋子中的概率是 1 * (1 - 1/11) = 10 / 11
  12号球到来,3号球 仍在袋子中的概率是 1 * (1 - 1/12) = 11 / 12
  那么 1 * 10/ 11 * 11/12 * 12 / 13 ....* 16/17 = 10 / 17



扩展
 运营10亿+的游戏公司,服务器非常多,某一天全球服务器大抽奖,当天登录过的就能参与抽奖,登录次数跟结果无关,
 你想选出100个幸运者,就可以用蓄水池抽样算法
 需要两个参数 首次登录方法,     全球第几个登录

 从一本很厚的电话簿中抽取 1000 人进行姓氏统计。

 从 Google 搜索 "Ken Thompson",从中抽取 100 个结果查看哪些是今年的。


研究者只关注研究本身
 */




func GetRandNumByLimit(n int) int { // return [1,n]
	rand.Seed(time.Now().UnixNano())
	return rand.Int() % n + 1
}

func TestGetRandNumByLimit(t *testing.T)  {
	mp := map[int]int{}
	for i := 1; i <= 10000; i++ {
		mp[GetRandNumByLimit(10)]++
	}
	t.Log(mp)
}

func ReservoirSampling(N int) []int  {
	result := make([]int, N)

	for k := 1; k <= 1000; k++ {
		if  k <= N {
			result[k - 1] = k
			continue
		}
		if GetRandNumByLimit(k) < N { // 天选之子   N / k
			result[GetRandNumByLimit(N) - 1] = k // 倒霉蛋儿
		}
	}
	return result
}



func TestReservoirSampling(t *testing.T)  {
	t.Log(ReservoirSampling(10))
}


//给定一个等概率生成1~7的函数,如何等概率生成1~10
// f() one of [1,7]
// g()  -> f()  one of [1,10]
// 方法,最简洁的,把可以凭借的随机元,生成二进制,返回7 重做, 返回1~7 认为返回0, 返回 4~6 认为返回1

// 1~10  搞定0~9 等概率就可以
// 看看0~9需要几个二进制位,   4个二进制位就够了
// 01二进制函数,调用四次,就可以拼成 四个bit 代表的值  > 9 的重做



// f  7 ~ 13   7,8,9,10,11,12,13    制作 17 ~ 56 的等概率随机数
//    7,8,9    -> 0
//    10,11,12 -> 1
//    13       -> 重做

//  17 ~ 56 等同于    0 ~ 56 - 17 (39)  几个二进制位拼去

// _ _ _ _ _ _  > 39扔掉 重做

// 假设一个函数f, 0 , 1 返回的概率不等   0: p概率   1: 1-p概率
// 如何 0 1 等概率返回

// f 调用两次, 得到二进制状态
// 00  01  10  11   00 和 11 忽略,重做
// 得到 01  和 10 代表 0 和1 时返回
// 一生二,二生三,三生万物



标签:11,10,抽样,概率,号球,蓄水池,算法,袋子,12
来源: https://blog.csdn.net/dawnto/article/details/121177493