你有一枚硬币。您可以根据需要翻转它多次。
要生成的随机数,使得,其中。
数字的分配应统一。
如果很容易:
r = a + binary2dec(flip n times write 0 for heads and 1 for tails)
如果怎么办?
你有一枚硬币。您可以根据需要翻转它多次。
要生成的随机数,使得,其中。
数字的分配应统一。
如果很容易:
r = a + binary2dec(flip n times write 0 for heads and 1 for tails)
如果怎么办?
Answers:
您要查找的内容基于拒绝采样或accept-reject方法(请注意,Wiki页面有点技术性)。
此方法在以下情况下很有用:您想从集合中选择一些随机对象(在您的情况下为集合中的随机整数),但是您不知道该怎么做,但是您可以选择一些随机对象由含有第一组中的较大的一组(在你的情况下,[ 一,2 ķ + 一]对于一些ķ使得2 ķ + 一≥ b ;这对应于ķ硬币翻转)。
在这种情况下,您只需从较大的集合中选择元素,直到您随机选择较小的集合中的元素。如果较小的集合与较大的集合相比足够大(在您的情况下包含的整数最多是[ a ,b ]的两倍,这已经足够好了),那么这是有效的。
一个替代示例:假设您要在半径为1的圆内选择一个随机点。现在,为此找到直接方法并不是一件容易的事。我们转向accept-reject方法:我们在围绕圆的1x1正方形中对点进行采样,并测试绘制的数字是否位于圆内。
(技术问题:答案适合数目的选择)
既然你被允许翻转你的硬币多次如你所愿,你可以关闭,随你,希望统一通过挑选一小部分得到您的概率你翻转:(使用二进制基数硬币的点之后的每个数位)和乘法- [R由BA得到0和BA-1](四舍五入至的整数)之间的数。加入此号码一个,你就大功告成了。
例如:说。二进制中的1/3是0.0101010101 ....然后,如果翻转在0到0.010101之间...则选择b。如果它介于0.010101 ..和0.10101010 ...之间,则您的选择为+ 1,否则为+ 2。
如果您将硬币翻转次,则将选择a和b之间的每个数字,概率为1。
没有人提到这一点,因此让我正式证明,如果是一个大小不是2的幂的域,那么有限的多次抛硬币不足以生成D的均匀随机成员。假设您使用k个硬币生成D的成员。对于每个d ∈ d,生成你的算法的概率d是形式的甲/ 2 ķ对于某个整数甲。算术基本定理表明A / 2 k ≠ 1 / | D | 。
如果要生成独立的D均匀样本,则所需的硬币抛弃次数(在最佳算法下)约为n log 2 | n 。D | 。更一般而言,如果要从熵H的分布生成,则期望中大约需要n个H随机位。实现此目的的算法是算术解码,应用于(表面上)无限的随机位序列。
如果ba不是2的幂,那么您可能必须翻转许多硬币才能得到结果。您甚至可能永远都不会获得结果,但这在极端情况下是不可能的。
方法
最简单的方法是在[a,a + 2 ^ n)中生成一个数字,其中2 ^ n> = ba,直到一个碰巧出现在[a,b)中。使用这种方法会浪费很多熵。
一种更昂贵的方法可以保留所有熵,但是随着硬币翻转/掷骰子数量的增加,计算量变得非常昂贵。从直觉上讲,这就像将硬币翻转视为小数点右边的二进制数字的数字,然后将数字从2转换为ab,然后在变成“卡住”时返回该数字的数字一样。
例
以下代码将n面公平的模具的卷转换为m面公平的模具的卷(在您的情况下,n = 2,m = ab),随着卷数的增加,边际成本也随之增加。注意需要具有任意精度的有理数类型。一个不错的特性是,从n面转换为m面然后再转换为n面将返回原始数据流,尽管由于数字必须卡住而可能会延迟几次滚动。
public static IEnumerable<BigInteger> DigitConversion(this IEnumerable<BigInteger> inputStream, BigInteger modIn, BigInteger modOut) {
//note: values are implicitly scaled so the first unfixed digit of the output ranges from 0 to 1
Rational b = 0; //offset of the chosen range
Rational d = 1; //size of the chosen range
foreach (var r in inputStream) {
//narrow the chosen range towards the real value represented by the input
d /= modIn;
b += d * r;
//check for output digits that have become fixed
while (true) {
var i1 = (b * modOut).Floor();
var i2 = ((b + d) * modOut).Floor(); //note: ideally b+d-epsilon, but another iteration makes that correction unnecessary
if (i1 != i2) break; //digit became fixed?
//fix the next output digit (rescale the range to make next digit range from 0 to 1)
d *= modOut;
b *= modOut;
b -= i1;
yield return i1;
}
}
}
生成二进制十进制。无需明确存储它,只需跟踪最小和最大可能值。一旦这些值位于同一整数内,则返回该整数。代码草图如下。
(编辑)更为详尽的解释:假设您要生成一个1到3之间(含1/3概率)的随机整数。为此,我们生成一个随机的二进制十进制实数,x在(0,1)范围内。如果x <1/3,则返回1,否则,如果x <2/3,则返回2,否则,返回3。我们没有跟踪x的位数,而是跟踪x的最小和最大值。最初,x的最小值为0,最大值为1。如果先翻转头部,则x的小数点后的第一位(二进制)为1。则x的最小可能值(二进制)为0.100000 = 1/2,最大值为0.111111111 =1。现在,如果您的下一个翻转是尾巴,则x以0.10开始。最小可能值为0.1000000 = 1/2,最大为0.1011111 = 3/4。x的最小可能值为1/2,所以你知道 s没有机会返回1,因为这需要x <1/3。如果x最终为1/2 <x <2/3,或者2/3 <x <3/4,则返回3,仍然可以返回2。现在假设第三次翻转是尾巴。然后x必须以0.100开头。最小值= 0.10000000 = 1/2,最大值= 0.100111111 = 5/8。现在既然1/3 <1/2 <5/8 <2/3,我们知道x必须落在间隔(1/3,2/3)中,所以我们可以停止生成x的数字而只返回2。
该代码基本上执行此操作,除了在a和b之间生成x而不是在0和1之间生成x之外,但是原理是相同的。
def gen(a, b):
min_possible = a
max_possible = b
while True:
floor_min_possible = floor(min_possible)
floor_max_possible = floor(max_possible)
if max_possible.is_integer():
floor_max_possible -= 1
if floor_max_possible == floor_min_possible:
return floor_max_possible
mid = (min_possible + max_possible)/2
if coin_flip():
min_possible = mid
else:
max_possible = mid
备注:我针对接受/拒绝方法测试了此代码,并且均产生了均匀分布。此代码所需的硬币翻转次数比接受拒绝次数少,除非b-a接近下一个2的幂。例如,如果要生成a = 0,b = 62,则接受/拒绝效果更好。我能够证明,此代码平均可以比接受/拒绝多使用2次硬币翻转。根据我的阅读,看来Knuth和Yao(1976)提供了一种解决此问题的方法,并证明了他们的方法在预期的硬币翻转次数中是最佳的。他们进一步证明,预期的翻转次数必须大于分布的香农熵。但是我找不到本文的副本,因此很想知道他们的方法是什么。(更新:刚刚在这里找到了Knuth Yao 1976的展览:http://www.nrbook.com/devroye/Devroye_files/chapter_fifteen_1.pdf, 但我尚未阅读)。有人在此主题中还提到了Han Hoshi,这似乎更为笼统,并使用偏向硬币来解决。另请参阅Pae(2009)撰写的http://paper.ijcsns.org/07_book/200909/20090930.pdf,以获得对文献的良好讨论。
简单的答案?
,请检查它是否在范围内,如果不是,请再次生成它。
您最可能需要重新生成的时间 那时候 对于一些整数 ,但即使那样,每一代都将有超过50%的机会落入范围内。
对于b-a不等于2 ^ k的情况,这是一种建议的解决方案。它应该以固定的步骤数运行(无需丢弃超出您预期范围的候选者)。
但是,我不确定这是否正确。请批评并帮助描述此随机数发生器(如果有)中的确切不均匀性,以及如何对其进行测量/量化。
首先转换为生成范围为[0,z-1]的均匀分布随机数的等效问题,其中z = b-a。
同样,令m = 2 ^ k是2> = z的最小幂。
根据上述解决方案,我们已经有一个范围为[0,m-1]的均匀分布的随机数生成器R(m)(可以通过扔k个硬币(每个比特一个)来完成)。
Keep a random seed s and initialize with s = R(m).
function random [0, z-1] :
x = R(m) + s
while x >= z:
x -= z
s = x
return x
while循环最多运行3次,以固定的步数给出下一个随机数(最佳情况=最坏情况)。
在此处查看数字[0,2]的测试程序:http : //pastebin.com/zuDD2V6H
理论上最优算法
这是我发布的其他答案的改进。另一个答案的优点是,它更容易扩展到从另一个生成一个离散分布的更一般的情况。实际上,由于Han和Hoshi,另一个答案是该算法的特殊情况。
我将在此处描述的算法基于Knuth和Yao(1976)。在他们的论文中,他们还证明了该算法实现了最小的硬币翻转预期次数。
为了说明这一点,请考虑其他答案描述的拒绝采样方法。例如,假设您要统一生成5个数字之一[0,4]。下一个2的幂是8,因此您掷硬币3次并生成最多8的随机数。如果数字是0到4,则将其返回。否则,将其丢弃,并生成最多8个数字,然后重试,直到成功。但是当您丢掉数字时,您只是浪费了一些熵。取而代之的是,您可以限制所扔出的号码的数量,以减少将来所需的抛硬币次数。具体而言,一旦生成数字[0,7],则返回[0,4]。否则,它是5、6或7,并且每种情况下您都会做不同的事情。如果是5,则再次翻转硬币,然后根据翻转返回0或1。如果是6 掷硬币并返回2或3。如果是7,则掷硬币;如果是头,则返回4;如果是头,则返回4。
我们最初失败尝试的剩余熵给了我们3种情况(5、6或7)。如果我们仅把它扔掉,就扔掉log2(3)硬币翻转。我们取而代之,将其与另一次翻转的结果结合起来以生成6种可能的情况(5H,5T,6H,6T,7H,7T),这让我们立即再次尝试生成具有5/6成功概率的最终答案。
这是代码:
# returns an int from [0, b)
def __gen(b):
rand_num = 0
num_choices = 1
while True:
num_choices *= 2
rand_num *= 2
if coin.flip():
rand_num += 1
if num_choices >= b:
if rand_num < b:
return rand_num
num_choices -= b
rand_num -= b
# returns an int from [a, b)
def gen(a, b):
return a + __gen(b - a)