rand 函数的一些妙用
2022-09-10 17:11:38 1.1k 字 · 约 5 分钟 # algorithm

最近看到一些有趣的rand()函数,在这里记录一下。

由rand5()返回rand7()

rand5()函数表示等概率返回1~5之间的整数,要求我们在不使用其他函数的前提下,使用rand5()函数生成rand7()函数。

我们可以发现rand5()可以等概率返回1、2、3、4、5中的数,那么我们试图通过rand5()函数构造一个等概率返回0、1的函数bit()。

再由bit()函数即可等概率构造任意一个[0, x]区间内的等概率返回函数。

因为每一次bit()调用都为独立事件,再令res > x的再重试即可。

JAVA
public class Rand7 {

    /**
     * @return 返回1~5之间的某个整数
     * @description 等概率返回1~5之间的所有整数
     */
    public static int rand5() {
        // Math.random()函数将等概率返回[0, 1)的浮点数
        return (int) (Math.random() * 5) + 1;
    }

    /**
     * @return 返回0或1
     * @description 等概率返回0、1
     */
    public static int bit() {
        int res = 0;
        do {
            res = rand5();
        } while (res == 3);
        return res < 3 ? 0 : 1;
    }

    /**
     * 等概率返回0~2^bits-1之间的所有数
     *
     * @param bits 二进制位数
     * @return 返回0~2^bits-1之间的某个数
     */
    public static int generate(int bits) {
        int res = 0;
        for (int i = 0; i < bits; i++) {
            res += bit() << i;
        }
        return res;
    }


    /**
     * @return 返回1~7之间的某个整数
     * @description 等概率返回1~7之间的所有整数
     */
    public static int rand7() {
        // 可以发现[1, 7]可以由[0, 6] + 1构成,于是考虑如何等概率返回[0, 6]
        int res = 0;
        do {
            res = generate(3);
        } while (res == 7);
        return res + 1;
    }


    public static void main(String[] args) {
        int[] counts = new int[10];
        int N = 1000000;
        for (int i = 0; i < N; i++) {
            counts[rand7()]++;
        }
        for (int i = 1; i <= 7; i++) {
            System.out.printf("%d出现%d次\n", i, counts[i]);
        }
    }
}

测试次的结果:

image-20220910173239248

由rand(a, b)返回rand(c, d)

其中rand(a, b)函数可以等概率返回[a, b]之间的所有数,需要使用rand(a, b)生成rand(c, d)其中d - c != b - a。

JAVA
public class RandRange {
    public static int randAB(int a, int b) {
        int diff = b - a + 1;
        return (int) (Math.random() * diff) + a;
    }

    public static int bit(int a, int b) {
        // 使用randAB(a, b)等概率返回0、1
        int res = 0;
        // 注意如果[a, b]中有奇数个数字则舍弃中间数字
        // 如果[a, b]中有偶数个数字则直接划分即可
        int count = b - a + 1;
        int mid = a + (b - a) / 2;
        if (count % 2 == 1) {
            do {
                res = randAB(a, b);
            } while (res == mid);
            return res < mid ? 0 : 1;
        } else {
            res = randAB(a, b);
            return res <= mid ? 0 : 1;
        }
    }


    public static int randCD(int a, int b, int c, int d) {
        // 和rand7()思路一致, 先生成[0, x], 而后 + c即可
        int x = d - c;
        int bits = 0;
        // 先求得x的二进制位数
        int tmp = x;
        do {
            tmp >>= 1;
            bits++;
        } while (tmp != 0);
        int res = 0;
        do {
            res = 0;
            for (int i = 0; i < bits; i++) {
                res += (bit(a, b) << i);
            }
        } while (res > x);
        return res + c;
    }


    public static void main(String[] args) {
        int N = 1000000;
        int a = 10, b = 20;
        int c = 40, d = 100;
        int[] counts = new int[b + 1];
        for (int i = 0; i < N; i++) {
            counts[randAB(a, b)]++;
        }
        for (int i = a; i <= b; i++) {
            System.out.printf("%d出现%d次\n", i, counts[i]);
        }
        counts = new int[d + 1];
        for (int i = 0; i < N; i++) {
            counts[randCD(a, b, c, d)]++;
        }
        for (int i = c; i <= d; i++) {
            System.out.printf("%d出现%d次\n", i, counts[i]);
        }
    }
}

image-20220910180448410

可以发现总体上是满足等概率出现的。

由不等概率(0, 1)返回等概率(0, 1)

已知有f()函数将以概率返回,概率返回,且满足。

那么我们进行两次独立事件,若两次都返回,显然概率为,若两次都返回,显然概率为,那么我们舍弃这两种情况,若一次一次且不区分先后,那么概率就为,记先后则返回,先后则返回。

JAVA
public class DifferentPossibility {

    public static int f() {
        // 假定以3/4概率返回0, 1/4概率返回1
        int res = 0;
        res = (int) (Math.random() * 4) + 1;
        return res <= 3 ? 0 : 1;
    }


    public static int g() {
        int res = 0;
        do {
            res = f();
        } while (res == f());
        return res;
    }

    public static void main(String[] args) {
        int N = 100000;
        int[] counts = new int[2];
        for (int i = 0; i < N; i++) {
            counts[g()]++;
        }
        for (int i = 0; i <= 1; i++) {
            System.out.printf("%d出现了%d次\n", i, counts[i]);
        }
    }
}

image-20220910181557263

文章作者: CZQ
文章链接: https://www.codenote.xyz/posts/a74f2eba.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 CZQ的codenote!