【问题标题】:Generate random number from random bits从随机位生成随机数
【发布时间】:2014-03-03 03:07:06
【问题描述】:

所以我有一个函数可以给我随机位 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

【问题讨论】:

  • 为什么你认为得到的数字不统一?
  • 我不太确定,似乎范围是否偏爱更高的值,因为我必须调用 rand 以获得与范围一样多的位,所以范围越高 rand调用和我的随机数的平均值越高。只是我的直觉,我不知道它可能完全有缺陷。
  • rand(a,b) 是否返回整数 ab 之间的整数?
  • 我认为真正的问题在于任意范围。如果 a = 0 和 b = 2 ** n - 1,你很好。但是如何处理其他类型的范围似乎很棘手。
  • 是的,你得到的位数越多,平均值就越高。事实上,如果随机位生成器是真正随机的,那么在大量样本中,平均值将非常接近(b-a)/2。但是使用随机位生成器,您生成 00000 的可能性应该与生成 01101 或任何其他 5 位序列的可能性一样。

标签: algorithm random


【解决方案1】:

是的,它不会是统一的。

考虑 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

【讨论】:

  • 如果您想尽量减少丢弃值的数量,请参阅此答案:stackoverflow.com/a/20818831/5987
  • 为什么要扔掉而不重新缩放?
  • @AlexandruBarbarosie 重新缩放可以工作,但使其完全统一可能是一个问题/不可能,尽管对某些数字稍微有利可能是可以接受的。
  • 如果假设范围从01024,您将需要 10 位,其中任何高于 10000 00000 的值都应该被丢弃,因此大约有一半的计算,这是善良的坏的......
  • @AlexandruBarbarosie 是的,在最坏的情况下,你有大约 50% 的机会失败,但失败两次是 25%、3 次 12.5% 等等 - 进行特别长时间的概率是非常低,我当然不希望我们一直在处理最坏的情况。
【解决方案2】:

对于均匀分布,您需要拒绝抽样

假设您要生成 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

【讨论】:

    【解决方案3】:

    好吧,不仅结果不是随机的,而且您可能无法在 (a,b) 之间生成一个值,因为rand(0,1) 可能总是生成 0,因此产生的数字超出了您的范围(如果a&gt;0)。

    问题出现了,因为假设一个范围 (0,5)0 将只有 1 个表示为 00000,而 1 将有 5 个:10000,01000,00100,00010,00001。意识到这一点,直接的解决方案应该是 bijective 映射,因此最简单的解决方案是将您的 01 视为数字的位,因为任何数字在基数 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_BITSMAX_VAL,可以选择将输入到rand_range() 的数据类型的限制,这样您就可以保证每个输入都将被正确地重新调整。

    【讨论】:

    • 如果您重新调整值,这是否真的是统一的?
    • @lgglyboo 可能有轻微的偏差,它完全取决于MAX_VAL,它应该是&gt;=(b-a),但同时尽可能小,换句话说,2 的第一个幂大于b-a,在这种情况下偏差可以忽略不计。
    【解决方案4】:

    认为以下是更好的方法:-

    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]);
            }
        }
    
    }
    

    【讨论】:

    • 我看到的唯一问题是它不能保证真正完成。
    • 它没有完成的概率非常低,但平均会在 2 次迭代中完成。
    • @Igglyboo 它不会在 k 次迭代中完成的概率是 (0.5)^k,即使是 10 次迭代也小于 0.001
    【解决方案5】:

    在 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) 范围内的随机 int,该范围可以 be easily extended to define randint(a, b)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-09
      • 2013-05-05
      • 2014-03-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多