问题:假设您有一个随机数生成器randn(),它返回0到n-1之间的均匀分布的随机数。给定任意数m,写一个随机数生成器,返回0到m-1之间均匀分布的随机数。
我的回答:
-(int)randm() {
int k=1;
while (k*n < m) {
++k;
}
int x = 0;
for (int i=0; i<k; ++i) {
x += randn();
}
if (x < m) {
return x;
} else {
return randm();
}
}
这是对的吗?
答案 0 :(得分:9)
你很接近,但你的答案的问题在于,有一种方法可以将一个数字写成另外两个数字的总和。
如果m<n
,那么这是有效的,因为数字0,1,...,m-1
每个都有相同的概率,算法几乎肯定会终止。
这个答案一般不起作用,因为将一个数字写成两个其他数字之和的方法不止一种。例如,只有一种方法可以获得0
,但有许多方法可以获得m/2
,因此概率不会相等。
示例:n = 2
和m=3
0 = 0+0
1 = 1+0 or 0+1
2 = 1+1
所以你方法的概率分布是
P(0)=1/4
P(1)=1/2
P(2)=1/4
这不统一。
要解决此问题,您可以使用唯一分解。在基础m
中写下n
,跟踪所需的最大指数,例如e
。然后,找到小于m
的{{1}}的最大倍数,将其称为n^e
。最后,使用k
生成e
个数字,将其作为某个数字randn()
的基础n
扩展,如果x
,则返回x < k*m
,否则再试一次。
假设x
,那么
m < n^2
答案 1 :(得分:4)
这不正确。
您正在添加统一的随机数,这不会产生一致的随机结果。假设n = 2且m = 3,则x的可能值为0 + 0,0 + 1,1 + 0,1 + 1。所以得到1的可能性是获得0或2的两倍。
您需要做的是在基数n中写入m,然后生成随机数的基数n表示的“数字”。如果您有完整的号码,则必须检查它是否小于m。如果是,那么你就完成了。如果不是,那么你需要重新开始。
答案 2 :(得分:3)
两个均匀随机数发生器的总和不是均匀生成的。例如,两个骰子的总和更可能是7比12,因为得到12你需要投掷两个六,而你可以得到7作为1 + 6或6 + 1或2 + 5或5 + 2或...
假设randn()返回0到n - 1之间的整数,n * randn()+ randn()均匀分布在0和n * n - 1之间,因此可以增加其范围。如果randn()返回一个介于0和k * m + j - 1之间的整数,则重复调用它直到得到一个数字&lt; = k * m - 1,然后将结果除以k得到一个均匀分布的数字0和m -1。
答案 3 :(得分:0)
假设n和m都是正整数,那么缩放的标准算法是否会起作用?
return (int)((float)randn() * m / n);