深入理解Math.random()的概率分布特性

直接上源碼

/**
 * Returns a {@code double} value with a positive sign, 
 * 返回一個(gè)帶符號(hào)的double類型的數(shù)字,說人話就是返回一個(gè)非負(fù)double類型的數(shù)字(包含0)
 *
 * greater than or equal to {@code 0.0} and less than {@code 1.0}.
 * 大于等于0
 * 
 * Returned values are chosen pseudorandomly with (approximately)
 * uniform distribution from that range.
 * 返回一個(gè)【偽隨機(jī)數(shù)】,并且返回的值(近似)【均勻分布】
 *
 * <p>When this method is first called, it creates a single new
 * pseudorandom-number generator, exactly as if by the expression
 *
 * <blockquote>{@code new java.util.Random()}</blockquote>
 *
 * This new pseudorandom-number generator is used thereafter for
 * all calls to this method and is used nowhere else.
 *
 * <p>This method is properly synchronized to allow correct use by
 * more than one thread. However, if many threads need to generate
 * pseudorandom numbers at a great rate, it may reduce contention
 * for each thread to have its own pseudorandom-number generator.
 *
 * @return  a pseudorandom {@code double} greater than or equal
 * to {@code 0.0} and less than {@code 1.0}.
 * @see Random#nextDouble()
 */
public static double random() {
    return RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble();
}

核心點(diǎn)
● 返回一個(gè)非負(fù)double類型數(shù)字,返回是[0-1)【包含0不包含1】
● 這是一個(gè)偽隨機(jī)數(shù),真正是隨機(jī)數(shù)應(yīng)該是沒有概率一說
● 產(chǎn)生的沒一個(gè)隨機(jī)數(shù)的概率近似相等

基礎(chǔ)驗(yàn)證:Math.random()隨機(jī)數(shù)等概率

/**
 * Math.random();
 * 返回一個(gè)范圍[0-1)的double類型數(shù)字(包含0,不包括1)
 * 每個(gè)數(shù)字的返回都是等概率的。
 */
public static void f1() {
    int randomCount = 1000_0000;

    int count = 0;
    double number = 0.1D;
//  double number = 0.2D;
//  double number = 0.3D;
//  double number = 0.4D;
//  double number = 0.5D;
//  double number = 0.6666D;
    for (int i = 0; i < randomCount; i++) {
        if (Math.random() < number) {
            count++;
        }
    }
    System.out.println("隨機(jī)小于" + number + "的概率:" + (double) count / (double) randomCount);
}

// 分別測試隨機(jī)的數(shù)字小于0.1、0.2、0.3、0.4、0.5、0.6666的概率結(jié)果如下:
// 隨機(jī)小于0.1的概率:0.1000645
// 隨機(jī)小于0.2的概率:0.2001116
// 隨機(jī)小于0.3的概率:0.2999759
// 隨機(jī)小于0.4的概率:0.3999943
// 隨機(jī)小于0.5的概率:0.5000803
// 隨機(jī)小于0.6666的概率:0.6665477

根據(jù)以上測試發(fā)現(xiàn),我們想要在[0-1)之間獲取一個(gè)小于x隨機(jī)數(shù)的概率為≈x%。

再次驗(yàn)證:[0-10)隨機(jī)數(shù)等概率

我們知道Math.random()返回一個(gè)[0-1)區(qū)間的一個(gè)隨機(jī)數(shù),那么把這個(gè)隨機(jī)數(shù)放大10倍,我們就能得到一個(gè)[0-10)區(qū)間的一個(gè)隨機(jī)數(shù)。

/**
 * Math.random() * 10;
 * 返回一個(gè)范圍[0-10)的double類型數(shù)字(包含0,不包含10)
 */
public static void f2() {
    int randomCount = 1000_0000;

    // 初始化一個(gè)長度為10的數(shù)組,每個(gè)元素分別記錄不同數(shù)字產(chǎn)生的個(gè)數(shù)
    // numbers[0] 存儲(chǔ)的是 0 的個(gè)數(shù)
    // numbers[1] 存儲(chǔ)的是 1 的個(gè)數(shù)
    // ...
    // numbers[9] 存儲(chǔ)的是 9 的個(gè)數(shù)
    int[] numbers = new int[10];
    for (int i = 0; i < randomCount; i++) {
        // 生成的隨機(jī)數(shù)放大10倍,并轉(zhuǎn)換成int類型,這個(gè)值一定是[0-10)之間的正整數(shù)(包含0,不包含10)
        numbers[(int) (Math.random() * 10)]++;
    }

    for (int i = 0; i < numbers.length; i++) {
        System.out.println("隨機(jī)小于" + (i + 1) + "的次數(shù):" + numbers[i] + ", 概率是:" + (double) numbers[i] / (double) randomCount);
    }
}

// 測試輸出結(jié)果如下:
// 隨機(jī)小于1的次數(shù):999811, 概率是:0.0999811
// 隨機(jī)小于2的次數(shù):1001029, 概率是:0.1001029
// 隨機(jī)小于3的次數(shù):1000648, 概率是:0.1000648
// 隨機(jī)小于4的次數(shù):999331, 概率是:0.0999331
// 隨機(jī)小于5的次數(shù):1000135, 概率是:0.1000135
// 隨機(jī)小于6的次數(shù):999298, 概率是:0.0999298
// 隨機(jī)小于7的次數(shù):998976, 概率是:0.0998976
// 隨機(jī)小于8的次數(shù):999258, 概率是:0.0999258
// 隨機(jī)小于9的次數(shù):1001133, 概率是:0.1001133
// 隨機(jī)小于10的次數(shù):1000381, 概率是:0.1000381

根據(jù)以上測試,我們同樣能驗(yàn)證想要在[0-10)之間獲取一個(gè)小于x隨機(jī)數(shù)的概率為≈x%。

問題進(jìn)階1:獲取一個(gè)小于x隨機(jī)數(shù)的概率變成x2%

還是先上代碼

public static void f3() {
    int randomCount = 1000_0000;

    int count = 0;
    double number = 0.456D;
    for (int i = 0; i < randomCount; i++) {
        /**
         * 這里執(zhí)行了2次隨機(jī)函數(shù),第一次隨機(jī)小于number的概率為number%,第二次隨機(jī)小于nunber的概率也為number%
         * 那么執(zhí)行兩次都小于number的概率就是number*number%,即number2%。
         * 注意:這里取max是為了保證兩次獲取的數(shù)字都是小于指定的數(shù)字。換句話說隨機(jī)多次中取最大的值都不大于指定的數(shù)字,那么其他的數(shù)字肯定也不大于指定的數(shù)字
         */
        if (Math.max(Math.random(), Math.random()) < number) {
            count++;
        }
    }
    System.out.println("隨機(jī)小于" + number + "原來的概率:" + number + ", 平方后的概率為:" + Math.pow(number, 2));
    System.out.println("隨機(jī)小于" + number + "的概率:" + (double) count / (double) randomCount);
}

// 分別測試隨機(jī)的數(shù)字小于0.1、0.2、0.3、0.4、0.5、0.6666的概率結(jié)果如下:
// 隨機(jī)小于0.1原來的概率:0.1, 平方后的概率為:0.010000000000000002
// 隨機(jī)小于0.1的概率:0.0099795
// 隨機(jī)小于0.2原來的概率:0.2, 平方后的概率為:0.04000000000000001
// 隨機(jī)小于0.2的概率:0.040024
// 隨機(jī)小于0.3原來的概率:0.3, 平方后的概率為:0.09
// 隨機(jī)小于0.3的概率:0.0900515
// 隨機(jī)小于0.4原來的概率:0.4, 平方后的概率為:0.16000000000000003
// 隨機(jī)小于0.4的概率:0.1600157
// 隨機(jī)小于0.5原來的概率:0.5, 平方后的概率為:0.25
// 隨機(jī)小于0.5的概率:0.2499815
// 隨機(jī)小于0.6666原來的概率:0.6666, 平方后的概率為:0.44435556
// 隨機(jī)小于0.6666的概率:0.4446067

代碼中我們執(zhí)行了兩次獲取隨機(jī)數(shù),并取最大值。
我們知道執(zhí)行一次獲取到不大于x的概率為x%,那么連續(xù)執(zhí)行兩次都小于x的概率就是x% * x%,即為x2%。
那么我們這里為什么要取兩次隨機(jī)數(shù)的最大值呢?
如果最大值都比x小,那么另外的值肯定也比x小,所以這里表達(dá)的意思就是連續(xù)兩次隨機(jī)的數(shù)字都小于x的概率。

問題進(jìn)階2:獲取一個(gè)小于x隨機(jī)數(shù)的概率變成x3%

上代碼

public static void f4() {
    int randomCount = 1000_0000;

    int count = 0;
//        double number = 0.1D;
//        double number = 0.2D;
//        double number = 0.3D;
//        double number = 0.4D;
//        double number = 0.5D;
        double number = 0.6666D;
    for (int i = 0; i < randomCount; i++) {
        /**
         * 這里執(zhí)行了2次隨機(jī)函數(shù),第一次隨機(jī)小于number的概率為number%,第二次隨機(jī)小于nunber的概率也為number%,
         * 第三次隨機(jī)小于nunber的概率也為number%,
         * 那么執(zhí)行兩三次都小于number的概率就是number*number%*number%,即number3%。
         * 注意:這里取max是為了保證三次獲取的數(shù)字都是小于指定的數(shù)字。換句話說隨機(jī)多次中取最大的值都不大于指定的數(shù)字,那么其他的數(shù)字肯定也不大于指定的數(shù)字
         */
        if (Math.max(Math.random(), Math.max(Math.random(), Math.random())) < number) {
            count++;
        }
    }
    System.out.println("隨機(jī)小于" + number + "原來的概率:" + number + ", 立方后的概率為:" + Math.pow(number, 3));
    System.out.println("隨機(jī)小于" + number + "的概率:" + (double) count / (double) randomCount);
}
// 分別測試隨機(jī)的數(shù)字小于0.1、0.2、0.3、0.4、0.5、0.6666的概率結(jié)果如下:
// 隨機(jī)小于0.1原來的概率:0.1, 立方后的概率為:0.0010000000000000002
// 隨機(jī)小于0.1的概率:0.001004
// 隨機(jī)小于0.2原來的概率:0.2, 立方后的概率為:0.008000000000000002
// 隨機(jī)小于0.2的概率:0.0079951
// 隨機(jī)小于0.3原來的概率:0.3, 立方后的概率為:0.026999999999999996
// 隨機(jī)小于0.3的概率:0.0269906
// 隨機(jī)小于0.4原來的概率:0.4, 立方后的概率為:0.06400000000000002
// 隨機(jī)小于0.4的概率:0.0641036
// 隨機(jī)小于0.5原來的概率:0.5, 立方后的概率為:0.125
// 隨機(jī)小于0.5的概率:0.1250869
// 隨機(jī)小于0.6666原來的概率:0.6666, 立方后的概率為:0.29620741629599995
// 隨機(jī)小于0.6666的概率:0.2962848

舉一反三:獲取一個(gè)小于x隨機(jī)數(shù)的概率變成x四次方%

public static void f5() {
    int randomCount = 1000_0000;

    int count = 0;
    double number = 0.456D;
    for (int i = 0; i < randomCount; i++) {
        /**
         * 這里執(zhí)行了4次隨機(jī)函數(shù),第一次隨機(jī)小于number的概率為number%,第二次隨機(jī)小于nunber的概率也為number%
         * 第3次隨機(jī)小于number的概率為number%,第4次隨機(jī)小于nunber的概率也為number%
         * 那么執(zhí)行四次都小于number的概率就是number*number%*number%*number%,即number四次方%。
         * 注意:這里取max是為了保證多次獲取的數(shù)字都是小于指定的數(shù)字。換句話說隨機(jī)多次中取最大的值都不大于指定的數(shù)字,那么其他的數(shù)字肯定也不大于指定的數(shù)字
         */
        if (Math.max(Math.random(), Math.max(Math.random(), Math.max(Math.random(), Math.random()))) < number) {
            count++;
        }
    }
    System.out.println("隨機(jī)小于" + number + "原來的概率:" + number + ", 四次方后的概率為:" + Math.pow(number, 4));
    System.out.println("隨機(jī)小于" + number + "的概率:" + (double) count / (double) randomCount);
}

以上內(nèi)容感謝左神。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容