【Algorithm】rand5生成rand7

前言

这是面试的时候遇到的一个问题,问题的关键就是如何利用 r a n d 5 rand5 rand5 生成均匀分布 r a n d 7 rand7 rand7,另外补充到, r a n d rand rand 只能产生整数。

思考

首先我们来看看另外一个问题,如何利用 r a n d [ 0 , 1 ] rand[0,1] rand[0,1] 生成 r a n d [ 0 , 2 k ) rand[0,2^k) rand[0,2k)?这个可以通过组成2进制数的方式完成这个任务,即将 r a n d [ 0 , 1 ] rand[0,1] rand[0,1] 执行 k k k 次。其中由于产生的是整数,因此在这里 r a n d [ 0 , 2 k ) rand[0,2^k) rand[0,2k) 亦可表示为 r a n d [ 0 , 2 k − 1 ] rand[0,2^k-1] rand[0,2k1]

再进一步,我们现在有了 r a n d [ 0 , 2 k − 1 ] rand[0,2^k-1] rand[0,2k1] 如何产生 r a n d [ a , b ] rand[a,b] rand[a,b]?我们来思考两种情况:

  1. 如果 2 k − 1 = b − a 2^k -1 = b-a 2k1=ba,只需要将数字整体偏移就可以了。例如 r a n d [ 3 , 6 ] = r a n d [ 0 , 2 2 − 1 ] + 3 rand[3,6] = rand[0,2^2-1] + 3 rand[3,6]=rand[0,221]+3
  2. 如果 2 k − 1 ≠ b − a 2^k -1 \neq b-a 2k1=ba,那先假设 b − a b-a ba 这个区间更小,那么我只需要截取 2 k − 1 2^k-1 2k1 的某个与之相同长度的区间偏移映射到 b − a b-a ba 上即可,当超出这个范围时,只需要重新执行 r a n d rand rand 即可;如果 b − a b-a ba 区间更大,按照同样的思路,我们可以通过扩大区间来回到前一种情况,事实上只要维持 2 k − 1 > = b − a 2^k -1 >= b-a 2k1>=ba 即可,所以 b − a b-a ba 区间更大的情况是可以排除的。

但是如果硬要通过小区间映射到更大的 b − a b-a ba 区间,我们可以截取一个区间,这个区间可以整除 b − a b-a ba 然后将区间分成 N 段,通过多级映射也可以完成,有点类似操作系统里面的多级索引。

int rand(int a, int b) { //generate rand[a,b]
    int tmp = 1, k = 0;
    int len = b - a;
    while (tmp - 1 < len) { //计算最小的正整数k,使2^k - 1 >= len
        tmp *= 2;
        k++;
    }
    tmp = 0;
    for (int i = 0; i < k; i++) { //产生 rand[0, 2^k-1]
        tmp <<= 1;
        tmp += rand01();
    }
    if (tmp > len) { //超出范围,截断
        return rand(a,b);
    }
    else {
        return tmp + a;
    }
}

实现

方法一

扩大区间,限制整数区间,映射区间

int rand7() {
    int i;
    do {
        i = 5 * (rand5() - 1) + rand5(); //产生[1,25]的整数区间
    } while (i > 21);  //将[1,25]整数区间控制于[1,21]
    return i % 7 + 1; //将[1,21]映射到[1,7]
}

参考

[1] 【算法题】rand5()产生rand7() https://www.cnblogs.com/dwdxdy/archive/2012/07/28/2613135.html

你可能感兴趣的:(Algorithm)