洗牌/采样/随机数
Reference
关于乱序(shuffle)与随机采样(sample)的一点探究 - xybaby - 博客园
Index
洗牌算法
Fisher–Yates shuffle - Wikipedia
Knuth-Durstenfeld Shuffle(Fisher–Yates Shuffle 改进版)
Knuth-Durstenfeld Shuffle 是一个“原地”(in-place)算法
伪代码
To shuffle an array a of n elements (indices 0..n-1):
Python 实现
正确性证明:
即证明:任何一个元素 shuffle 之后出现在任意位置的概率都是
1/N
证明:
Inside-Out Shuffle
Inside-Out Shuffle 算法会返回一个打乱的副本
伪代码
Python 实现
正确性证明 TODO
Inside-Out Shuffle 无穷版
所谓无限,指的是
n=len(src)
未知的情况Python 实现
采样(等概率)
问题描述
无放回思路
无放回会改变原始数据,如果不想修改原始数据,需要额外复制一份
思路 1:类似 Knuth-Durstenfeld Shuffle
跟洗牌一样,随机从序列中取出一个元素,同时从原序列中删除
该方法需要修改原数据
如果不能修改原数据,可以考虑复制一份数据(仅当 n 比较小时)
证明 TODO
思路 2:类似 Inside-Out Shuffle
Python
random.sample()
中当 `n <= 4ceil(log(3m, 4))` 时*的采用的方法类似 Inside-Out 算法的方式从原数组中抽取 m 个数
有放回思路
思路
Python
random.sample()
中当 `n > 4ceil(log(3m, 4))` 时*采用的策略使用一个
set
记录已经被抽到的位置,如果该位置已经存在,则继续如果
n < 4**ceil(log(3*m, 4))
时采用这个策略,可能会产生类似 Hash 冲突的问题,导致调用随机数方法的次数过多求解调用
rangd()
的次数是一个期望问题蓄水池抽样及实现 - handspeaker - 博客园
蓄水池采样
问题描述
基本思路 ->
等概率采样
蓄水池采样算法
伪代码
Python 实现
证明:该算法保证每个元素以
m/n
的概率被选中n <= m
时,每个元素被 100% 选中n > m
时,n=m+1
时,根据算法定义,第m+1
个元素被选中的概率=m/m+1
,而前m
个元素被选中的概率=1-被第m+1个元素替换的概率
=1-(m/m+1)*(1/m)=m/m+1
;说明前m+1
个元素被选中的概率相等。n=m+k
时,第m+k
个元素被选中的概率=m/m+k=m/n
,前(n-1)
个元素被选中的概率=1-被第n个元素替换的概率
=1-(m/n)*(1/m)=m/n
;说明所有n
个元素被选中的概率相等。蓄水池抽样及实现 - handspeaker - 博客园
采样(不等概率)
查表法(有放回)
图示
上面的线段按频度进行分割,下面为等距分割
往下方的线段随机打点(均匀分布),根据点所落在的区间对应到上方的线段,来确定采样的对象
Word2Vec 中负采样使用的方法
Python 实现
测试
如何采样,使 n-1
被采样 n 次?
n-1
被采样 n 次?0 被采样 1 次,1 被采样 2 次,2 被采样 3 次,...
法 1
类似查表法的思路
法 2
Python
无放回 TODO
随机数
用 rand_m()
生成 rand_n()
rand_m()
生成 rand_n()
面试中随机数"等概率"vs"不等概率"生成问题 - hellogiser - 博客园
说明:
rand_n()
能够等概率生成[0, n)
内的整数,rand_m()
同理
假设 rand_m()
由以下方式实现
m > n
时
m > n
时这种情况比较简单
因为
m >= n
,只要用rand_m()
生成的数在[0, n)
范围就返回,反之重新生成
m < n
时
m < n
时以下方法要求
n <= m*m
先看代码再说思路(暂时忽略注释的代码):
思路
rand_m(m) * m + rand_m(m)
的意思是先等概率生成0, m, 2m, .. (m-1)*m
,然后在加上rand_m(m)
;最终效果相当于等概率生成[0, m*m)
之间的数然后再按照
m > n
时的做法生成n
注意到,如果
n << m*m
时,这个方法会产生大量废操作一个优化方法就是等概率生成
t*n
中的数,然后对n
取模
利用不等概率方法等概率产生随机数
问题 1
non_rand_01()
测试代码Python
问题 2
计算 n 的二进制位数,记为
k
;然后在问题 1 的基础上,产生长度为 k 的随机 0/1 数列注意,产生的随机数可能大于
n
,当大于n
时,则丢弃并重新生成
最后更新于