標籤:
引子:
在寫程式間突然需要用到隨機數,於是用到了js的Math.random隨機函數,產生指定範圍內的隨機數,根據網上流傳的寫法產生指定範圍內的隨機數如下
function random(min, max){ return Math.round(Math.random()*(max-min))+min;}var zero = one = two = three = 0;for(var i=0;i<10000;i++){ var num = random(0,3); if(num==0){ zero++; }else if(num==1){ one++; }else if(num==2){ two++; }else if(num==3){ three++; }else{ console.log("assert false"); }}console.log(zero,one,two,three);
經過10000次計算,在0-3範圍內產生的結果資料如下,0=1670次 1=3348次 2=3266次 3=1717次
發現這個演算法略坑,min以及max的邊界命中率簡直低的可憐,根本不是均勻分布,反而有點像常態分佈,只能改進重寫了
Math.random()函數
根據官方的定義:random() 方法可返回介於 0 ~ 1 之間的一個隨機數。那麼猜想在0~1之間,如果對產生的隨機數進行四捨五入,那麼該機率在(0,1)是呈均勻分布狀態,也就是說出現0或者是1的機率是相等的,接下來驗證猜想
function guess(){ var zero = 0; var one = 0; for(var i=0;i<100000;i++){ Math.round(Math.random()) ==1 ? one++ : zero++; } return zero+" "+one;}for(var i=0;i<10;i++){ var g = guess(); console.log(g);}
運行結果如下
50093 49907
49787 50213
49862 50138
49948 50052
49820 50180
50158 49842
50179 49821
49967 50033
49920 50080
50063 49937
從資料結果中可以看出,對隨機數四捨五入之後,出現0和出現1的機率呈均勻分部狀態,測試次數無窮大的情況下,那麼出現1或0的機率的極限都是1/2,根據這個特性,由二進位開始聯想,那麼在指定位元的情況下,在這個區間內,每一個數出現的機率也是均勻分布的,機率各為1/2^n,於是就有了下面的驗證程式如下
var Helpers = { getbit: function(num){ var bit = 0; while(num>0){ num = num>>1; bit++; } return bit; }, randomBin : function(times){ var num = Math.round(Math.random()); while(times>1){ num = (num<<1) + Math.round(Math.random()); times--; } return num; }, random : function(min,max){ var num = max-min; var times = this.getbit(num); do{ var binNum = this.randomBin(times); }while(!(binNum<=num)); return binNum+min; }}var zero = one = two = three = 0;for(var i=0; i<=10000; i++){ var n = Helpers.random(0,3); if(n == 0){ zero++; }else if(n == 1){ one++; }else if(n == 2){ two++; }else if(n == 3){ three++; }}console.log(zero,one,two,three);
運行10000次的結果:2523 2548 2416 2514 ,根據資料可以看出,這種產生隨機數的方式比之第一種要更加的均勻
尾語:
雖然複用代碼是一個好習慣,但是不要盲目的去複用他人的代碼,靠巧合編程是無法長久的,惟有不斷的提升自己,才會覺得數學與電腦真的是非常的有趣。
js隨機數