關於JavaScript中的隨機數方法

最近在琢磨內置對象Math的時候,參考了不少網上資料,不過我在Google中搜索js 隨機整數,出來不少博客文章,很遺憾,在我看來排名靠前的這些文章都是錯誤的。接下來我將會論證我這一觀點,同時把我所理解的Math.random()方法跟你分享。
圖片描述javascript

獲取端點問題;

先看這一篇,Javascript實現隨機整數

在這篇文章中,做者給出了以下方法,根據他的文章描述,這個算法能產生startend之間的隨機整數。php

javascriptfunction rnd(start, end){
    return Math.floor(Math.random() * (end - start) + start);
}

一個須要確認的問題

在說個人觀點以前,我但願先明確一件事情,設想別人跟你提了一個需求:html

須要一個生成ab之間隨機整數的方法。java

這時候我以爲你至少須要跟對方確認一個問題:包不包括兩個端點,即ab ?
(其實還有一些問題須要考慮的,好比傳入的ab若是不是數字類型?若是是小數?若是是負數?若是ab大?爲了專一本文要討論的內容,咱們假定傳入的ab都是合法的正整數,且a<b。)算法

若是對方給你確認了,那是墜吼的;可是若是對方沒有確認,如何理解這一需求?我認爲在對上述需求沒有更多說明的狀況下,如下兩個是靠譜的理解:segmentfault

  1. 既要包括端點a,也要包括端點b
  2. 既不包括端點a,也不包括端點b

因此,若是你實現的方法能獲取的只包含其中一個端點,我以爲這種實現是不太理想的,你再琢磨這句話:ab之間的隨機整數app

若是你也認同個人這段論述,接下來回到開頭那個方法,看他能不能實現獲取從startend之間的隨機整數;方法主體就一個運算表達式:less

javascriptreturn Math.floor(Math.random() * (end - start) + start);

逐步分析:dom

javascriptMath.random();                               //[0,1)
Math.random()*(end-start);                   //[0,end-start);
Math.random()*(end-start)+start;             //[start,end);
Math.floor(Math.random()*(end-start)+start); //{ x | x>=start,x<end,x∈N}

第一行,Math.random()方法返回的是01之間的浮點數,注意,__包括0,不包括1!__ECMA語言標準裏明確規定了:spa

Returns a Number value with positive sign, greater than or equal to 0 but less than 1

因此,這篇博客,和這篇博客是不正確的;因爲這兩篇佔據結果第一頁的博文比較古老,我甚至在想是否是ECMA標準一開始不是這樣子的,而後查了一下,ECMA標準從第一個版本對random方法的規定就是如今這個樣子。

第二行,Math.random()*(end-start);則返回0end-start之間的浮點數,包含0,不包含end-start

第三行, Math.random()*(end-start)+start;返回startend之間的浮點數,包含start,不包含end

第四行,Math.floor(Math.random()*(end-start)+start);。這是最關鍵的一行,對上一行產生的浮點數進行向下取整,既然是向下取整,結果可能取到start,卻永遠取不到end

根據前述理解,這樣的實現我認爲是不合要求的。因此,這篇文章也是不正確的。
另外,這幾篇幾個不錯的JavaScript隨機...js生成隨機數採用parseInt對獲取的浮點數進行取整操做,也是一樣的問題,能取到左端點,卻沒法取到右端點。parseInt操做浮點數的效果至關於Math.floor()

勘誤:

以前寫的上面這句話「parseInt操做浮點數的效果至關於Math.floor()」,我在看了這篇文章以後發現這句話是不對的,抱歉。但個人結論是不變的:Math.random()的結果範圍包括0,所以parseInt(Math.random())也可能取到0

而這一篇JavaScript random方法獲得隨機整數,採用的是Math.ceil()方法進行向上取整,

javascriptMath.ceil(Math.random()*3);//獲得1-3的整數

這樣確實既能取到右端點,也能取到左端點,可是做者的左端點標錯了,這個是有可能能取到0的,儘管取到0的機率是無限趨近於0;

這樣引申出另外一個問題:取得的隨機整數範圍裏,各整數出現的機率是否一致?

獲取機率問題

一個機率不均等的方法

在以前有位老師給出的獲取隨機數方法是下面這樣(要求了必須包括端點);

javascriptfunction getRandom(n,m){
  //省略特殊情形下的處理過程,好比n>m,或者n、m之一沒法轉化爲有效數字;
  return Math.round(Math.random()*(m-n)+n);
}

一個獲取隨機整數的方法,若是不格外的聲明,我以爲有一個默認的條件應該是:

生成每一個整數的機率應當是均等的;

根據第一部分的分析,這個方法能獲取的浮點數的範圍是nm之間,包括n,不包括m;而這個方法中取整是採用Math.round()四捨五入;這樣的話:

  1. 當取出的結果x∈[n,n+0.5)時,會四捨五入爲n;區間範圍爲0.5,機率爲0.5/(m-n)*100%;
  2. 當取出的結果x∈[n+0.5,n+1.5)時,會四捨五入爲n+1;區間範圍爲1,機率爲1/(m-n)*100%;
  3. 當取出的結果x∈[n+1.5,n+2.5)時,會四捨五入爲n+2;區間範圍爲1,機率爲1/(m-n)*100%;
  4. ......
  5. 當取出的結果x∈[m-0.5,m)時,會四捨五入爲m;區間範圍爲0.5,機率爲0.5/(m-n)*100%;

經過以上分析,該算法確實能夠達到目標,取到nm之間的隨機整數,可是並非每一個整數出現的機率都是相等的,具體來講,取得兩邊端點的整數機率是其餘數字的一半。因此,我認爲這也是不夠完美的。

這樣的話,在寫一個得到機率均等的隨機整數方法的時候,相信你知道應該注意些什麼問題了吧。

總結

總結一下,當咱們在獲取隨機整數的時候,有兩個問題須要特別注意:

  1. 必定要仔細檢查兩邊端點是不是否能取到?是否與需求一致?
  2. 獲取到的每一個整數的機率是否均等?
相關文章
相關標籤/搜索