JS語言裏常見的隨機函數示例,實驗結果分佈規律分析

  在JavaScript語言裏有個 Math.random() 隨機函數,用於生成指定範圍內的隨機數。

Math.random()函數

  根據官方的定義: Math.random() 函數返回一個浮點數, 僞隨機數在範圍[0,1),也就是說,從0(包括0)往上,但是不包括1(排除1),然後您可以縮放到所需的範圍。實現將初始種子選擇到隨機數生成算法;它不能被用戶選擇或重置。

也就是說 random() 方法可返回介於 0 ~ 1 之間的一個隨機數。比如:0.93228703630515190.081824481973134850.6718927278167157,每次調用它會返回一個含16位小數的隨機浮點數。

  我們日常代碼過程中實際上是需要生成整數,或是指定區間的隨機數,這就需要對它封裝改造一下了。常見的封裝改造有以下幾種。

隨機整數:floor + random

示例:生成隨機整數,傳入參數是最大值(不包括),即返回值是 [0, max)

function getRandomInt(max) {
  return Math.floor(Math.random() * Math.floor(max));
}

console.log(getRandomInt(3));
// expected output: 0, 1 or 2

console.log(getRandomInt(1));
// expected output: 0

指定區間的隨機數(一)

示例:這個例子返回了一個在指定值之間的隨機數 [min, max)。這個值不小於 min(有可能等於),並且小於(不等於)max。

function getRandomArbitrary(min, max) {
  return Math.random() * (max - min) + min; 
}

console.log(getRandomArbitrary(1,3));
// expected output: 1.0501238302537486,1.9956248025522734,2.7839421306375227 ……

指定區間的隨機數(二)

示例:這個例子返回了一個在指定值之間的隨機整數 [min, max)。這個值不小於 min (如果 min 不是整數,則不小於 min 的向上取整數),且小於(不等於)max。

function getRandomInt(min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min)) + min; //不含最大值,含最小值
}

console.log(getRandomInt(1,3));
// expected output: 1 or 2

也許很容易想到用 Math.round() 來實現,但是這會導致你的隨機數處於一個不均勻的分佈,這可能不符合你的需求。

指定區間的隨機數(三)

上一個例子提到的函數 getRandomInt() 結果範圍包含了最小值,但不含最大值。如果你的隨機結果需要同時包含最小值和最大值,怎麼辦呢? getRoundRandom() 函數可以實現得到一個兩數之間的隨機整數,包括兩個數在內。

示例:這個例子返回了一個在指定值之間的隨機整數 [min, max]。這個值不小於 min (如果 min 不是整數,則不小於 min 的向上取整數),且不大於max(如果 max 不是整數,則不小於 max 的向下取整數)。

function getRoundRandom(min, max){
    return Math.round(Math.random()*(max-min))+min;
}

console.log(myRoundRandom(1,3));
// expected output: 1, 2 or 3

  需要注意:Math.round() 函數返回一個數字四捨五入後最接近的整數。它取整的結果分佈不均,大概成正泰分佈。驗證:var num = getRoundRandom(0, 5); 生成5輪,每輪10000個結果分佈如下:

指定區間的隨機數(四)

得到一個兩數之間的隨機整數,包括兩個數在內。如果想概率分佈均勻一點可以使用 Math.trunc()
Math.trunc() 方法會將數字的小數部分去掉,只保留整數部分。Math.trunc() 的執行邏輯很簡單,僅僅是刪除掉數字的小數部分和小數點,不管參數是正數還是負數。傳入該方法的參數會被隱式轉換成數字類型。

示例:這個例子返回了一個在指定值之間的隨機整數 [min, max]。這個值不小於 min,且不大於max。

function getTruncRandom(min, max){
    return Math.trunc(Math.random()*(max-min+1))+min;
}

console.log(getTruncRandom(1,3));
// expected output: 1, 2 or 3

可以簡寫:const getTruncRandom=(min,max)=>Math.trunc(Math.random()*(max-min+1)+min);

驗證:var num = getTruncRandom(0, 5); 生成5輪,每輪10000個結果分佈如下:

指定區間的隨機數(五)

前面例子提到的函數 getRandomInt() 結果範圍包含了最小值,但不含最大值。如果你的隨機結果需要同時包含最小值和最大值,怎麼辦呢? getRandomIntInclusive() 函數可以實現得到一個兩數之間的隨機整數,包括兩個數在內。

示例:這個例子返回了一個在指定值之間的隨機整數 [min, max]。這個值不小於 min (如果 min 不是整數,則不小於 min 的向上取整數),且不大於max(如果 max 不是整數,則不小於 max 的向下取整數)。

function getRandomIntInclusive(min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random()*(max-min+1))+min; //含最大值,含最小值 
}

console.log(getRandomIntInclusive(1,3));
// expected output: 1, 2 or 3

驗證:var num = getRandomIntInclusive(0, 5); 生成5輪,每輪10000個結果分佈如下:

總結

  1,實驗三採用round + random方式最終驗證隨機結果分佈不均衡,不予採用;
  2,實驗四、實驗五最終驗證隨機結果分佈接近均衡,均可以採用;
  3,實驗四利用 trunc + random 實現方式簡潔,運算次數更少,推薦採用;


附件代碼:生成5輪,每輪10000個結果分佈

<script>
    // round + random()函數
    function getRoundRandom(min, max){
        return Math.round(Math.random()*(max-min))+min;
        /* 實驗結果
        遊戲開始:zero = 0 one =0 two =0 three =0 four =0 five=0
        遊戲結束:zero = 1034 one =2011 two =1965 three =2004 four =1978 five=1008
        遊戲結束:zero = 978 one =2097 two =1967 three =1947 four =2062 five=949
        遊戲結束:zero = 1083 one =2066 two =1933 three =1908 four =1986 five=1024
        遊戲結束:zero = 994 one =1974 two =2092 three =1918 four =1967 five=1055
        遊戲結束:zero = 1010 one =1979 two =2086 three =1970 four =1944 five=1011
        */
    }
    // trunc + random()函數
    function getTruncRandom(min, max){
        return Math.trunc(Math.random()*(max-min+1))+min;
        /* 實驗結果
        遊戲開始:zero = 0 one =0 two =0 three =0 four =0 five=0
        遊戲結束:zero = 1752 one =1607 two =1636 three =1631 four =1650 five=1724
        遊戲結束:zero = 1667 one =1660 two =1696 three =1654 four =1646 five=1677
        遊戲結束:zero = 1708 one =1658 two =1658 three =1724 four =1619 five=1633
        遊戲結束:zero = 1688 one =1620 two =1689 three =1664 four =1664 five=1675
        遊戲結束:zero = 1737 one =1644 two =1655 three =1656 four =1694 five=1614
        */
    }
    // ceil + floor + random()函數
    function getRandomIntInclusive(min, max) {
        min = Math.ceil(min);
        max = Math.floor(max);
        return Math.floor(Math.random() * (max - min + 1)) + min; //含最大值,含最小值 
    }

    function guess(){
        var zero = one = two = three = four = five = 0; // 記錄每個數出現次數

        console.log('遊戲開始:zero = '+zero+' one ='+one+' two ='+two+' three ='+three+' four ='+four+' five='+five);
        for(var i=0; i<10000; i++)
        {
            // 自定義生成隨機數
            // var num = getRoundRandom(0, 5); 
            // var num = getTruncRandom(0, 5); 
            var num = getRandomIntInclusive(0, 5);
            
            // 對結果類型計數
            switch(num){
                case 0: zero++; break;
                case 1: one++; break;
                case 2: two++; break;
                case 3: three++; break;
                case 4: four++; break;
                case 5: five++; break;
                default: console.error('生成數字錯誤 num='+num);
            }
        }

        // 該輪結束輸出結果類型計數
        console.log('遊戲結束:zero = '+zero+' one ='+one+' two ='+two+' three ='+three+' four ='+four+' five='+five);
    }

    guess();
</script>

【完】

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章