利用random5 生成 random7

  摘要:今天看算法,看到一个有意思的题目:给定一个函数 rand(5) 能随机生成 [1, 5] 之间的正整数,你能实现 rand(7) 吗?

尝试

  如果我们用 rand(5) + rand(5) 呢? rand(5) + rand(5) 的结果是 [2, 10], 我们思考一下就知道,这些数肯定不是等概率的,比如 2 的概率要低于 5 的概率(生成 2 只有 1+1 这一种可能,而生成 5 可以有 1+4,2+ 3, 3+2,4+1)。我们再来看看,现在既然有了随机等概率的 1, 2, 3, 4, 5 ,那很容易就有随机等概率的 10, 20, 30, 40, 50。假设现在又有另外一个函数,能等概率的生成 0, 1, 2, 3, 4, 5, 6, 7, 8,9, 那么我们就能轻易的构造了等概率的 10, 11, 12, 13 ,... , 59(因为每一个数字只能有另外两个数字唯一相加得到),这就是我们的整体思路。

探索

  基于上面的分析,我们首先要让 rand(5) 产生等概率的间距数组(比如上面的 10, 20, 30, 40, 50), 然后让 rand(5) 产生连续的待插入的数字(比如上面的 0, 1, 2 ,..., 9)。问题是,要多大的间距才合适呢?其实也很简单,要让 0, 1, 2, 3, 4 刚好能插入到间距数组中。

step1: 用 rand(5) 产生等概率的 0, 1, 2, 3, 4, 准备插入到下一步的等间距数组中,使得插入刚好合适。

step2: 用 rand(5)产生等概率的 0, 1, 2, 3, 4, 为了被插入,将其散开成 0, 5, 10, 15, 20。

step3: 将第一步的结果插入到第二步中,于是,就形成了 0, 1, 2, 3, 4 ,..., 21, 22, 23, 24。然后就很容易等概率地生成 1, 2, 3, 4, 5, 6, 7 。

/*
* 输入:rand5 产生 [1, 5] 的随机数
* 输出:rand7 产生 [1, 7] 的随机数
*/
int rand7() {
    int x = 22;
    while (x > 21) {  //将区间控制到 [1, 21]
        x = rand5() + (rand5() - 1) * 5;  //产生 [1, 25] 的整数区间
    }
    return 1 + x % 7;  //将[1, 21] 映射到 [1, 7]
}

升华

  上面的方法是基于这种思想:rand() 产生 [0, N-1], 把 rand() 视为 N进制的一位数产生器,那么可以使用 rand()*N + rand() 来产生 2位的 N进制数,以此类推,可以产生 3位, 4位...N位。这种按构造 N进制数的方式生成的随机数,必定能保证随机。

  题目中 N为 5, 因此可以使用 rand5() * 5 + rand5() 来产生 2位的 5进制数,范围就是 1 到 25。再去掉 22-25, 剩余的除3, 映射到 [1, 7] 的区间上,以此作为 rand7() 的产生器。

思考:如果反过来呢?给你 rand7, 让我们实现 rand5, 这个该怎么实现呢?最直观的想法就是不断调用 rand7, 直到它产生 1到5 之间的数,然后返回。代码如下:

int rand5() {
    int x = 6;
    while (x > 5) {  //将区间控制到 [1, 5]
        x = rand7()
    }
    return x;
}

根据以上,我们可以得出一个一般性结论:如果 A > B, 那么一定可以用 randA 去实现 randB, 其中 randA 表示等概率生成[1, a] 的函数, randB 表示等概率生成[1, b] 的函数。

其它解法

  对于上述题目,我们也可以采用 预置数组 的方式来解决,该方法简单,容易理解,但是不具有兼容性,需要额外的存储空间。

/*
* 输入:rand5 产生 [1, 5] 的随机数
* 输出:rand7 产生 [1, 7] 的随机数
*/
int rand7() {
    int vals[5][5] = {
        {1, 2, 3, 4, 5},
        {6, 7, 1, 2, 3},
        {4, 5, 6, 7, 1},
        {2, 3, 4, 5, 6},
        {7, 0, 0, 0, 0},
    };
    int res = 0;
    while (res == 0) {
        int i = rand5();
        int j = rand5();
        res = vals[i][j];
    }
    return res;
}

 

参考资料:

https://my.oschina.net/u/2822116/blog/794617

posted on 2020-09-10 11:19  爱笑的张飞  阅读(2169)  评论(0编辑  收藏  举报

导航