這是朋友去筆試的一道題,有點考智商,當時我還很自信的說 random5+random5/2 不就能夠了 他說不行,而後我就在網上搜了一下 有一道相似的題目html
題目:dom
已知一個函數rand7()可以生成1-7的隨機數,請給出一個函數,該函數可以生成1-10的隨機數。函數
思路:優化
假如已知一個函數可以生成1-49的隨機數,那麼如何以今生成1-10的隨機數呢?spa
解法:.net
該解法基於一種叫作拒絕採樣的方法。主要思想是隻要產生一個目標範圍內的隨機數,則直接返回。若是產生的隨機數不在目標範圍內,則丟棄該值,從新取樣。因爲目標範圍內的數字被選中的機率相等,這樣一個均勻的分佈生成了。code
顯然rand7至少須要執行2次,不然產生不了1-10的數字。經過運行rand7兩次,能夠生成1-49的整數,orm
1 2 3 4 5 6 7 1 1 2 3 4 5 6 7 2 8 9 10 1 2 3 4 3 5 6 7 8 9 10 1 4 2 3 4 5 6 7 8 5 9 10 1 2 3 4 5 6 6 7 8 9 10 * * 7 * * * * * * *
因爲49不是10的倍數,因此咱們須要丟棄一些值,咱們想要的數字範圍爲1-40,不在此範圍則丟棄並從新取樣。htm
代碼:blog
因爲row範圍爲1-7,col範圍爲1-7,這樣idx值範圍爲1-49。大於40的值被丟棄,這樣剩下1-40範圍內的數字,經過取模返回。下面計算一下獲得一個知足1-40範圍的數須要進行取樣的次數的指望值:
E(# calls to rand7) = 2 * (40/49) + 4 * (9/49) * (40/49) + 6 * (9/49)
2
* (40/49) + ... ∞ = ∑ 2k * (9/49)
k-1
* (40/49) k=1 = (80/49) / (1 - 9/49)
2
= 2.45
優化:
上面的方法大概須要2.45次調用rand7函數才能獲得1個1-10範圍的數,下面能夠進行再度優化。
對於大於40的數,咱們沒必要立刻丟棄,能夠對41-49的數減去40可獲得1-9的隨機數,而rand7可生成1-7的隨機數,這樣能夠生成1-63的隨機數。對於1-60咱們能夠直接返回,而61-63則丟棄,這樣須要丟棄的數只有3個,相比前面的9個,效率有所提升。而對於61-63的數,減去60後爲1-3,rand7產生1-7,這樣能夠再度利用產生1-21的數,對於1-20咱們則直接返回,對於21則丟棄。這時,丟棄的數就只有1個了,優化又進一步。固然這裏面對rand7的調用次數也是增長了的。代碼以下:
下面計算下優化後方法的調用rand7函數的指望次數:
E(# calls to rand7) = 2 * (40/49) + 3 * (9/49) * (60/63) + 4 * (9/49) * (3/63) * (20/21) + (9/49) * (3/63) * (1/21) * [ 6 * (40/49) + 7 * (9/49) * (60/63) + 8 * (9/49) * (3/63) * (20/21) ] + ((9/49) * (3/63) * (1/21))
2
* [ 10 * (40/49) + 11 * (9/49) * (60/63) + 12 * (9/49) * (3/63) * (20/21) ] + ... = 2.2123
這裏指望次數爲2.21,比起未優化的2.45次減小了大概10%。
http://www.cppblog.com/mysileng/archive/2013/05/20/200426.html
看第一遍,我仍是以爲都差很少,後來朋友指出,我這樣算的機率不一樣,不能算做隨機數,因而我又看了一遍,恍然大悟