国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

[轉][JS]Math.random()隨機數的二三事

2019-11-06 06:18:14
字體:
來源:轉載
供稿:網友

原文鏈接:http://www.soulteary.com/2014/07/05/js-math-random-trick.html

看到題目,如果大家平時被問到:如何生成一個怎么樣怎么樣的整數隨機數,估計大家都會不屑,但是當你淡定的回答獲取一個范圍應該是隨機數seeds和區間數值差的乘機與最小數相加然后再怎么怎么的時候…有沒有發現你的思維已經固化了呢。

這個知識點應該是玩JS肯定會碰到的之一吧。文末有Markdown,可以直接下載閱讀,清爽一點。

先來掉書袋,看看MDN的文檔。

打開Node,進入終端命令行模式,輸入Math.random():

javaScript
123>Math.random() 0.436846193857491

結果是不是依舊如同往常一樣稀松平常的小于1的一個偽隨機數跳了出來呢。 這個時候,如果別人問你,還有什么其他方案可以生成隨機數么,你會想到神馬呢。

逝者如斯夫,不舍昼夜。

如果你繼續在終端里輸入new Date()-0:

123>newDate()-0 1404488829907

我想你可以得到一個自增的數字,對,就是“秒”,如果你說這貨哪里隨機了,請別著急:

Javascript
123>(newDate()-0)%10086 8657

這里的取模%的數值可以是大于2且最好小于當前時間的數值,則可以得到你取模數值概率分之一的概率的隨機數。

如果你取模的數值是隨機數呢,那么產生這個隨機數的可見的兩個變量都是隨機的,那么是不是近似真的“隨機數”了呢?

當然,如果使用這招,還要考慮到硬件以及語言執行過程的耗時,因為我們知道計算機執行的時候,有一個時間的精度的范疇,所以需要使用一點點的延時抑制。

扯了一些沒用的,你可能著急了,那么請保持好奇心,我們繼續說點無聊的事情。

Math.random會提供給我們一個[0,1)之間的隨機數,但是如果我們要[1,10]范圍隨機整數的話,可以使用以下三個函數:

Math.roundMath.ceilMath.floor

我們先來生成一個隨機數:

JavaScript
123>Math.random()*(10-1)+1 8.26644050120376

接著我們來使用這三個Math內建函數:

JavaScript
1234567891011>Math.round(8.26644050120376) 8 >Math.ceil(8.26644050120376) 9 >Math.floor(8.26644050120376) 8

把數值換成8.56644050120376后,再來看看:

1234567891011>Math.round(8.56644050120376) 9 >Math.ceil(8.56644050120376) 9 >Math.floor(8.56644050120376) 8

所以區別一目了然,對于浮點數,round會遵守四舍五入規則,ceil無論如何貪心進位+1,floor無論如何都小心翼翼的自斷一臂-1,至于整數,自己試試看咯。

說到這里,接下來可以正常的描述內容了:

問:如何快速生成一段隨機文本,比如驗證碼或者我們訪問網站常見的隨機數token。

答案很多,我說一個經典的,其實思路很簡單,把剛剛生成隨機數的方法隨便選擇一個.toString():

JavaScript
1Math.random().toString(36).substring(7);

//當然也可以寫成這樣

JavaScript
1Math.random().toString(36).slice(2);

//或者利用時間

JavaScript
1(newDate()-0).toString(36)

隨便輸出一些,我們可以看到這貨輸出的字符串長短參次不齊的:

JavaScript
12345678910111213141516171819202122232425262728293031323334353637383940414243mptzulnb3xr 87jx7vkuik9 761qsolayvi amqx2mx6r ce5uyvkuik9 5ioufim5cdi dirp4hiwwmi ioe597ldi ohn9izfr sPRsakk2o6r 5g3ruo6flxr ... //單純時間來做隨機是不是生成的慘不忍睹 //而且不做隨機延時抑制,重復太明顯 hx7pom3y hx7pom3z hx7pom40 hx7pom41 hx7pom42 hx7pom43 hx7pom44 hx7pom45

我們先來看看為什么用toString()可以生成隨機數: 首先前面的家伙不管是隨機數seeds生成的,還是時間遞增的長整型,它們都是Number構造器構造出來的[object Number],而在ecma.js中,Number的這個方法是這樣的:

JavaScript
123456789/** @param {Number} [radix] @return {string} */ Number.prototype.toString=function(radix){};

作為一個好人,我給你指條明路,MDN的文檔

看過之后是不是想到了parseInt函數的第二個參數?我們發現,這個原生函數支持2~36(如果超出36,那么26個字母就不夠用了親)進制的轉換,所以如果在生成的時候,隨機切換進制,取結果的隨機位置,效果會不會更好呢,你可以試一試。

如果你想獲得字母多一點且平均一點,那么只有使用36進制了,但是不管怎么躲,都有可能出現一串數字。

1xjuk0zxofen1xlxr

有沒有好方法來解決這個問題呢,答:

1Math.random().toString(36).replace(/[^a-z]+/g,'')

123//或者這樣 Math.random().toString(36).slice(2).replace(//d/g,'')

這個時候,你或許會說,文章該就此結束了吧,這個方法看起來很爽很簡潔。 不過,你有思考過一個問題么,回顧前文,隨機數可以是0,1,…這些整數…

當隨機數是這些數值的時候,很抱歉,返回值是原來的數值,即0,1,…,我們得到的最后的結果就會是一個空字符串,而如果是0.5這類某些以5結尾的浮點數的時候,結果依然如此。還有當數值是某些時候,生成的隨機數位數會比較短…

解決這個問題,你當然可以重新生成這個隨機數,直到它輸出一個你心滿意足的隨機數再放過他,但是,我們剛剛了解到的生成一個大的隨機數的方法是依賴時間,小學還是初中學過的用一個比較小的數值除以一個比較大的數值,得到的結果是一個更小的浮點數來解決這個問題呢。

1(Math.random()/+newDate()).toString(36).replace(//d/g,'').slice(1)

這樣就得到了一個比較長,且比較公平的隨機數。可以用node驗證一下:

1varc={},r;for(vari=0,j=10000000;i<j;i++){r=(Math.random()/+newDate()).toString(36).replace(//d/g,'').slice(1);c[r]?c[r]+=1:c[r]=1;}for(variinc){if(c[i]===1){deletec[i];}}console.log(c);

運行結果是沒有任何沖突,當然這可能是小概率事件。如果你覺得你點很背,你可以試一試下面這段,手動執行幾次,看看,有沒有不是空數組這個結果的結果。

1for(vari=0,j=100;i<j;i++){(function(){varc={},r;for(vari=0,j=1000;i<j;i++){r=(Math.random()/+newDate()).toString(36).replace(//d/g,'').slice(1);c[r]?c[r]+=1:c[r]=1;}for(variinc){if(c[i]===1){deletec[i];}}console.log(c);}())}

當然,你也可以不用這兩個測試例子上面的那段代碼,改用下面這種方式,多輸出幾次隨機字符串,然后拼合在一起。

1for(varc='';c.length<5;)c+=Math.random().toString(36).substr(2)

這個把戲估計你看膩了,我們來看下面這個系列的示例,從固定的字典中抽取字符構成隨機字符串:

依賴Array的map方法,要注意兼容性,當然,你從MDN那邊copy一段hacks也可以無縫兼容,是不是看起來高大上一點:

123456789Array.apply(0,Array(5)).map(function(){   return(function(charset){     returncharset.charAt(Math.floor(Math.random()*charset.length))   }('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789')); }).join('');

不過或許你更容易接受這種多一點:

1234567functionrand(length,current){   current=current?current:'';   returnlength?rand(--length,"0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz".charAt(Math.floor(Math.random()*60))+current):current; }

或者更傳統的:

1234567891011functionrand(){   vartext="";   varpossible="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";   for(vari=0;i<5;i++)text+=possible.charAt(Math.floor(Math.random()*possible.length));   returntext; }

或許你以為這樣把戲就玩完了,too young too simple,之前玩過了Number的原生函數,我們還可以動手腳的是String的原生函數,比如我們知道,字母的ASCII碼是[65,97],那么當這個區間是字典,我們隨機抽取這個區間是不是會有一些好玩的事情發生呢。

123456789101112131415functionrand(x){   vars="";   while(s.length<x&&x>0){     varr=Math.random();     s+=String.fromCharCode(Math.floor(r *26)+(r>0.5?97:65));   }   returns; }

隨便輸入一個電話號碼(推薦輸入短一點的數),然后等等看,是不是會出現下面的結果:

1JrxKnxrHwJzGCKoKCzrpFxMxDBFqBrpAEGvpvMtCIHIHFCxtMxrHtpEGDyxzxwMBBqDvEBwxprwHqDMCErIzLwuyFApnxpxJoxBAFEoHsAEqvIxBIJvpFBoLnvurHJsvDFwGtFvDsELMLwzowvqBJtCwrGCsCHGvsxCLunGxtrtnyvvwyFqEsstotxnsrqLHAIyCxzLxDqtuzsoFJAGHyxrwxJusJrtpuvIyIILoJrGLKnptqHLBAKwGEpnIwzCtFAnrHIqLHynGwuyuPSDpLJFGxBuJBouwCDGKsKFCLKnAzoupsxFqIynKFCoBApyBsJKzJpKwEFCyGywrBoFpvorMzBrBAFowrKxvuJLoKtzpEoDsEsBExyGBMssnADnBwrvJDGunJsMyFGCxIFApEnoLyGxBrHroBsLAICGLDIwvqp

這個話題,好像說的有點麻木了,換個需求吧。有的網站會玩一些隨機背景色的小花樣。有沒有優雅的解決方案呢:

遵守隨機數函數的定義,獲取顏色數值之間的數值就好,看過了上面的代碼,這句,很好懂了伐:

1'#'+(0x1000000+(Math.random())*0xffffff).toString(16).substr(1,6)

當然,你可以寫一個更加直觀的方案:

1234567891011//用十進制數據來代替16進制數據運算,請注意因為右邊是開區間,要比上面的0xffffff +1,最后將結果轉換回十六進制,然后修剪一下字符串,輸出 Math.floor(Math.random()*16777216).toString(16); '#000000'.slice(0,-color.length)+color;   //或者更簡短的樣子如下 "#"+("000000"+Math.floor(Math.random()*16777216).toString(16)).substr(-6);

還有一類花樣,如下:

1234567functionrandomColor(){   varr=function(){returnMath.floor(Math.random()*256)};   return"rgb("+r()+","+r()+","+r()+")"; }

輸出如下:

1rgb(29,236,191)

時間不早了,最后說一下隨機排序數組吧。

關于洗牌算法,網上流傳很多,隨便選擇一種模擬一下就好,比如隨便寫的全重排:

12345678910111213vari=0,data=[],r; for(;i<10;data[i++]=i); while(--i){   r=Math.round(Math.random()*9+1)-1;   data[i]=data[i]+data[r],data[r]=data[i]-data[r],data[i]=data[i]-data[r]; } console.log(data)

或者利用Array.prototype.sort()函數,這里可以不把里面的數值帶進來運算。

首先Math.random()會生成一個[0,1)之間的數值,用0.5這個比較公平的數值減去它,概率得到小于0,等于0,大于0三種狀況,而Array.prototype.sort()期待的數值恰好是[-1,0,1],是不是很省事。

123456789vari=0,data=[],r; for(;i<10;data[i++]=i); data.sort(function(){   return.5-Math.random(); });

時間不早了,明天還得早起,先寫到這里,想到什么,再補充什么吧。

多謝瀏覽,歡迎反饋。

資料參考: Google && StackOverflow

曉白于2014.7.5夜

-EOF-


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 东辽县| 泸溪县| 玉环县| 正蓝旗| 沭阳县| 大同市| 疏勒县| 阜阳市| 安徽省| 江北区| 林周县| 仪陇县| 雷山县| 中方县| 周宁县| 阿拉善左旗| 株洲市| 汪清县| 沙田区| 湖州市| 额济纳旗| 武清区| 利辛县| 同心县| 苍南县| 分宜县| 万安县| 新乡县| 五指山市| 青川县| 若尔盖县| 三穗县| 阜南县| 沿河| 保山市| 台南市| 常熟市| 承德市| 海宁市| 友谊县| 淮滨县|