最近在琢磨內置對象Math
的時候,參考了不少網上資料,不過我在Google中搜索js 隨機整數
,出來不少博客文章,很遺憾,在我看來排名靠前的這些文章都是錯誤的。接下來我將會論證我這一觀點,同時把我所理解的Math.random()
方法跟你分享。
javascript
在這篇文章中,做者給出了以下方法,根據他的文章描述,這個算法能產生start
到end
之間的隨機整數。php
javascriptfunction rnd(start, end){ return Math.floor(Math.random() * (end - start) + start); }
在說個人觀點以前,我但願先明確一件事情,設想別人跟你提了一個需求:html
須要一個生成
a
到b
之間隨機整數的方法。java
這時候我以爲你至少須要跟對方確認一個問題:包不包括兩個端點,即a
和b
?
(其實還有一些問題須要考慮的,好比傳入的a
和b
若是不是數字類型?若是是小數?若是是負數?若是a
比b
大?爲了專一本文要討論的內容,咱們假定傳入的a
和b
都是合法的正整數,且a
<b
。)算法
若是對方給你確認了,那是墜吼的;可是若是對方沒有確認,如何理解這一需求?我認爲在對上述需求沒有更多說明的狀況下,如下兩個是靠譜的理解:segmentfault
- 既要包括端點
a
,也要包括端點b
。- 既不包括端點
a
,也不包括端點b
。
因此,若是你實現的方法能獲取的只包含其中一個端點,我以爲這種實現是不太理想的,你再琢磨這句話:a
到b
之間的隨機整數。app
若是你也認同個人這段論述,接下來回到開頭那個方法,看他能不能實現獲取從start
到end
之間的隨機整數;方法主體就一個運算表達式: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()
方法返回的是0
到1
之間的浮點數,注意,__包括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);
則返回0
到end-start
之間的浮點數,包含0
,不包含end-start
。
第三行, Math.random()*(end-start)+start;
返回start
到end
之間的浮點數,包含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); }
一個獲取隨機整數的方法,若是不格外的聲明,我以爲有一個默認的條件應該是:
生成每一個整數的機率應當是均等的;
根據第一部分的分析,這個方法能獲取的浮點數的範圍是n
到m
之間,包括n
,不包括m
;而這個方法中取整是採用Math.round()
四捨五入;這樣的話:
- 當取出的結果
x∈[n,n+0.5)
時,會四捨五入爲n;區間範圍爲0.5,機率爲0.5/(m-n)*100%;- 當取出的結果
x∈[n+0.5,n+1.5)
時,會四捨五入爲n+1;區間範圍爲1,機率爲1/(m-n)*100%;- 當取出的結果
x∈[n+1.5,n+2.5)
時,會四捨五入爲n+2;區間範圍爲1,機率爲1/(m-n)*100%;- ......
- 當取出的結果
x∈[m-0.5,m)
時,會四捨五入爲m;區間範圍爲0.5,機率爲0.5/(m-n)*100%;
經過以上分析,該算法確實能夠達到目標,取到n
到m
之間的隨機整數,可是並非每一個整數出現的機率都是相等的,具體來講,取得兩邊端點的整數機率是其餘數字的一半。因此,我認爲這也是不夠完美的。
這樣的話,在寫一個得到機率均等的隨機整數方法的時候,相信你知道應該注意些什麼問題了吧。
總結一下,當咱們在獲取隨機整數的時候,有兩個問題須要特別注意:
- 必定要仔細檢查兩邊端點是不是否能取到?是否與需求一致?
- 獲取到的每一個整數的機率是否均等?