所以我有一个函数给我随机位rand(0,1)我想将它推广到rand(a,b),它给出了a到b范围内的随机数。
我的想法是只计算b - a中的位数,然后将它们一起追加。我认为这会奏效,但不会是统一的。我觉得它会支持更大的数字而不是更小的数字(数字更接近a)。不是真的要求直接回答只是一些帮助会很好。
编辑: 到目前为止,这是我的想法,只是在统一部分上不确定
pseudo code:
function rand_range(a, b):
n = b - a
sum = a
for i in range(n):
sum += rand(0,1)
return sum
答案 0 :(得分:2)
是的,它不会是统一的。
考虑3位的简单情况:
0+0+0 0
0+0+1 1
0+1+0 1
0+1+1 2
1+0+0 1
1+0+1 2
1+1+0 2
1+1+1 3
很明显,1和2比0或3更容易发生。
当你增加位数时会变得更加不均匀 - 0并且最大值永远不会出现多次,中间出现的次数最多。
对于随机分布,我能想到的最好的方法是抛弃一些生成的数字。
将b-a
舍入到最接近的2减1的幂,然后单独生成每个位,如果结果大于b-a
,请再试一次。
因此,如果b-a
为5,则向上舍入为7,并生成所涉及的3位以使最大数量为7:
000 0
001 1
010 2
011 3
100 4
101 5
110 6
111 7
现在,在6或7的情况下,扔掉它们再试一次。
这可以通过使用字符串并连接0或1,并在结尾转换为数字,或者在每一步,乘以2(将所有位移到一个位置)来完成加0或1。
最后,您仍会将结果添加到a
。
答案 1 :(得分:0)
要获得统一分布,您需要拒绝抽样:
假设您想要生成4,5,6(含)之间的数字,那么2位就足够了。映射00
- > 4
,01
- > 5
,10
- > 6
,11
- > reject
pseudo code:
function rand_range(a, b):
n = ceil(log2(b - a))
m = b-a
while(true)
sum = a
bits = []
for i in range(n):
bits.append(rand(0,1))
sum += ToBase10(bits)
if sum <= b:
break
return sum
答案 2 :(得分:0)
好吧,不仅结果不是随机的,而且你可能无法在(a,b)
之间生成一个值,因为rand(0,1)
总是会生成0,因此会产生一个超出的数字你的范围(如果是a>0
)。
存在的问题是因为我们假设范围为(0,5)
,0
只有1个表示为00000
,而1
则为5 10000,01000,00100,00010,00001
。实现这一点,直接解决方案应该是bijective映射,因此最简单的解决方案是将您的0
和1
视为数字的位,因为任何数字都具有唯一的表示形式基地2。
因此伪代码:
const MAX_BITS = 9;
const MAX_VAL = 1023;
fun rand_range(a,b){
sum = 0;
for i<MAX_BITS
sum += pow(2,i)*rand(1,0)
// rescale
return (b-a) * sum/MAX_VAL + a
}
可以选择输入到MAX_BITS
的{{1}}和MAX_VAL
数据类型限制,以便保证每个输入都能正确重新调整。
答案 3 :(得分:0)
认为以下是更好的方法: -
1. find log2(b-a) (number of bits to represent b-a)
2. generate log2(b-a) bits at random and construct decimal number from it.
3. if number is greater than (b-a) reject it and repeat 2.
4. else evaluate a + rand(b-a).
时间复杂度: - 如果随机数生成器是真正随机的,那么您将进行两次迭代以获得范围b-a中的随机数,因此T(a,b) = log(b-a)
以下是java实现: -
public class RNG {
public static int rand(int a,int b) {
int bits = 0;
int diff = b-a;
Random r = new Random();
while(diff>0) {
bits++;
diff = diff/2;
}
while(true) {
int acc = 0;
for(int i=0;i<bits;i++) {
acc = 2*acc + r.nextInt(2);
}
if(acc<=b-a) {
return(a+acc);
}
}
}
public static void main(String[] args) {
int a = 150;
int b = 300;
int freq[] = new int[b+1];
for(int i=0;i<1000;i++) {
int k = rand(a,b);
freq[k]++;
}
System.out.println("freq:");
for(int j=a;j<=b;j++) {
System.out.println(j+" : "+freq[j]);
}
}
}
答案 4 :(得分:0)
在Python中,您可以继承random.Random
class以获得包括randint(a, b)
在内的完整界面。
import random
class Random(random.Random):
def random(self):
"""Get the next random number in the range [0.0, 1.0)."""
return self.getrandbits(53) * 2**-53
def getrandbits(self, k):
"""getrandbits(k) -> x. Generates an int with k random bits."""
return sum(rand(0, 1) << r for r in range(k))
def seed(self, *args, **kwargs): # unused methods
return None
def getstate(self, *args, **kwargs):
raise NotImplementedError
def setstate(self, *args, **kwargs):
raise NotImplementedError
x = rand(a,b)
可以表示为r = Random(); x = r.randint(a, b)
优点是,如果您正确定义random()
,getrandbits()
方法,那么其余代码已经过测试并且正常工作。 _randbelow()
method显示了如何使用这些基元返回[0,n)
范围{{1}}中的随机int。