从最基础的讲起如何做到均匀的生成随机数
題目描述:470. 用 Rand7() 實(shí)現(xiàn) Rand10()
因?yàn)槭堑谝淮谓佑|到這樣的題目,毫無思緒,對(duì)官方題解也是“不知道為什么要這么做”。看過一些題解之后才逐漸明白,現(xiàn)在讓我自己來寫題解,我打算先從簡(jiǎn)單的開始講起。
Part 1
假設(shè)已知rand2()可以均勻的生成[1,2]的隨機(jī)數(shù),現(xiàn)在想均勻的生成[1,4]的隨機(jī)數(shù),該如何考慮?
我想如果你也像我一樣第一次接觸這個(gè)問題,那么很可能會(huì)這么考慮——令兩個(gè)rand2()相加,再做一些必要的邊角處理。如下:
rand2() + rand2() = ? ==> [2,4]1 ? ?+ ? 1 ? ? = 21 ? ?+ ? 2 ? ? = 32 ? ?+ ? 1 ? ? = 32 ? ?+ ? 2 ? ? = 4// 為了把生成隨機(jī)數(shù)的范圍規(guī)約成[1,n],于是在上一步的結(jié)果后減1
(rand2()-1) + rand2() = ? ==> [1,3]0 ? ? ? + ? 1 ? ? = 10 ? ? ? + ? 2 ? ? = 21 ? ? ? + ? 1 ? ? = 21 ? ? ? + ? 2 ? ? = 3
可以看到,使用這種方法處理的結(jié)果,最致命的點(diǎn)在于——其生成的結(jié)果不是等概率的。在這個(gè)簡(jiǎn)單的例子中,產(chǎn)生2的概率是50%,而產(chǎn)生1和3的概率則分別是25%。原因當(dāng)然也很好理解,由于某些值會(huì)有多種組合,因此僅靠簡(jiǎn)單的相加處理會(huì)導(dǎo)致結(jié)果不是等概率的。
因此,我們需要考慮其他的方法了。
仔細(xì)觀察上面的例子,我們嘗試對(duì) (rand2()-1) 這部分乘以 2,改動(dòng)后如下:
(rand2()-1) × 2 + rand2() = ? ==> [1,3]0 ? ? ? ? ? ?+ ? 1 ? ? = 10 ? ? ? ? ? ?+ ? 2 ? ? = 22 ? ? ? ? ? ?+ ? 1 ? ? = 32 ? ? ? ? ? ?+ ? 2 ? ? = 4神奇的事情發(fā)生了,奇怪的知識(shí)增加了。通過這樣的處理,得到的結(jié)果恰是[1,4]的范圍,并且每個(gè)數(shù)都是等概率取到的。因此,使用這種方法,可以通過rand2()實(shí)現(xiàn)rand4()。
也許這么處理只是我運(yùn)氣好,而不具有普適性?那就多來嘗試幾個(gè)例子。比如:
(rand9()-1) × 7 + rand7() = resulta ? ? ? ? ? ? ? b為了表示方便,現(xiàn)將rand9()-1表示為a,將rand7()表示為b。計(jì)算過程表示成二維矩陣,如下:
可以看到,這個(gè)例子可以等概率的生成[1,63]范圍的隨機(jī)數(shù)。再提煉一下,可以得到這樣一個(gè)規(guī)律:
已知 rand_N() 可以等概率的生成[1, N]范圍的隨機(jī)數(shù)
那么:
(rand_X() - 1) × Y + rand_Y() ==> 可以等概率的生成[1, X * Y]范圍的隨機(jī)數(shù)
即實(shí)現(xiàn)了 rand_XY()
?
part2
那么想到通過rand4()來實(shí)現(xiàn)rand2()呢?這個(gè)就很簡(jiǎn)單了,已知rand4()會(huì)均勻產(chǎn)生[1,4]的隨機(jī)數(shù),通過取余,再加1就可以了。如下所示,結(jié)果也是等概率的。
rand4() % 2 + 1 = ?1 % 2 ? ?+ 1 = 22 % 2 ? ?+ 1 = 13 % 2 ? ?+ 1 = 24 % 2 ? ?+ 1 = 1事實(shí)上,只要rand_N()中N是2的倍數(shù),就都可以用來實(shí)現(xiàn)rand2(),反之,若N不是2的倍數(shù),則產(chǎn)生的結(jié)果不是等概率的。比如:
rand6() % 2 + 1 = ?1 % 2 ? ?+ 1 = 22 % 2 ? ?+ 1 = 13 % 2 ? ?+ 1 = 24 % 2 ? ?+ 1 = 15 % 2 ? ?+ 1 = 26 % 2 ? ?+ 1 = 1 rand5() % 2 + 1 = ?1 % 2 ? ?+ 1 = 22 % 2 ? ?+ 1 = 13 % 2 ? ?+ 1 = 24 % 2 ? ?+ 1 = 15 % 2 ? ?+ 1 = 2
Part 3
ok,現(xiàn)在回到本題中。已知rand7(),要求通過rand7()來實(shí)現(xiàn)rand10()。
有了前面的分析,要實(shí)現(xiàn)rand10(),就需要先實(shí)現(xiàn)rand_N(),并且保證N大于10且是10的倍數(shù)。這樣再通過rand_N() % 10 + 1 就可以得到[1,10]范圍的隨機(jī)數(shù)了。
而實(shí)現(xiàn)rand_N(),我們可以通過part 1中所講的方法對(duì)rand7()進(jìn)行改造,如下:
(rand7()-1) × 7 + rand7() ?==> rand49()
但是這樣實(shí)現(xiàn)的N不是10的倍數(shù)啊!這該怎么處理?這里就涉及到了“拒絕采樣”的知識(shí)了,也就是說,如果某個(gè)采樣結(jié)果不在要求的范圍內(nèi),則丟棄它。基于上面的這些分析,再回頭看下面的代碼,想必是不難理解了。
Part 4: 優(yōu)化
這部分具體的代碼是參考官方題解的,不過是我自己在理解了part 1和part 2之后才看懂的,一開始看真不知道為什么(/(ㄒoㄒ)/~~...
根據(jù)part 1的分析,我們已經(jīng)知道(rand7() - 1) * 7 + rand7() 等概率生成[1,49]范圍的隨機(jī)數(shù)。而由于我們需要的是10的倍數(shù),因此,不得不舍棄掉[41, 49]這9個(gè)數(shù)。優(yōu)化的點(diǎn)就始于——我們能否利用這些范圍外的數(shù)字,以減少丟棄的值,提高命中率總而提高隨機(jī)數(shù)生成效率。
class Solution extends SolBase {public int rand10() {while(true) {int a = rand7();int b = rand7();int num = (a-1)*7 + b; // rand 49if(num <= 40) return num % 10 + 1; // 拒絕采樣a = num - 40; // rand 9b = rand7();num = (a-1)*7 + b; // rand 63if(num <= 60) return num % 10 + 1;a = num - 60; // rand 3b = rand7();num = (a-1)*7 + b; // rand 21if(num <= 20) return num % 10 + 1;}} }作者:kkbill
鏈接:https://leetcode-cn.com/problems/implement-rand10-using-rand7/solution/cong-zui-ji-chu-de-jiang-qi-ru-he-zuo-dao-jun-yun-/
來源:力扣(LeetCode)
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。
總結(jié)
以上是生活随笔為你收集整理的从最基础的讲起如何做到均匀的生成随机数的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一个有趣的python排序模块:bise
- 下一篇: 经典算法题 -- 判断单链表是否成环及寻