隨機數的組合問題在面試時是常常考的,好比以前我就被問到:「有一個能夠生成1-5的隨機數函數,怎樣把它擴大到1-7?」面試
在解決這個問題以前,先來看看另一個比較簡單的問題:「有一個能夠生成1-7的函數,怎樣把它縮小到1-5?」下面是一個生成1-7函數random7:dom
function random7() { return Math.floor(Math.random() * 7 + 1); }
如何把它轉成生成1-5的函數呢?這很簡單:在一個循環裏面調用random7,直到它的值小於等於5就結束循環並返回該隨機數便可,以下:函數
function random5() { var r = random7(); while(r > 5) { r = random7(); } return r; }
上面的思路就是:若是生成的隨機數大於5,就繼續調用random7,直到它小於等於5爲止。好吧,迴歸正題,再來看一下1-5如何轉成1-7吧。下面是一個隨機生成1-5的函數:spa
function random5() { return Math.floor(Math.random() * 5 + 1); }
咱們如今的目的是要把它擴大到1-7。有一種很天然的想法可能就是:一個random5()產生的隨機數範圍是1-5,那麼兩個random5()相加的範圍就是2-10了,再減去1就是1-9了,因此,能夠按照上面的思路,在random7裏來個循環,若是小於等於7就結束循環而且返回。以下:code
function random7() { var r = random5() + random5() - 1; while(r > 7) { r = random5() + random5() - 1; } return r; }
這樣確實能夠把1-5的範圍擴大到1-7,可是問題來了:所謂隨機函數,產生的每一個值的機率是相等的,可是上面的方法產生的值機率相等嗎?咱們可使用機率論的組合知識算岀來:生成1有一種組合,就是random5() + random5() - 1;中的兩個random5()均是1,生成2有兩種組合,第一個random5()是1第二個是2,或者相反。顯然,它們的機率是不等的。因此這種方法是不行的。blog
爲了實現生成的每一個值的機率是相等的,就是使得每一個值的組合數相等。一種可行的方法是使得每一個值的組合只有一種,以下:it
function random7() { var r = (random5() - 1) * 5 + random5(); while(r > 7) { r = (random5() - 1) * 5 + random5(); } return r; }
爲何這樣就會使得各個值的機率相等呢?首先來看一下(random5() - 1) * 5,容易算岀這個表達式生成的可選值是0,5,10,15,20,用它去跟random5()相加,由於random5()的可選值是1, 2,3,4,5,因此二者相加以後就會獲得1-25之間的隨機數,並且產生的每一個值的組合均只有一種,因此它們的機率也是相等的。io
也許有人會問,(random5() - 1) * 5,這裏爲何是乘以5而不是其餘呢?這是由於乘以5以後和random5()相加,獲得的數是連續的而且是等機率的。function
上面討論的都是特殊情形1-5和1-7之間的轉換,對於其餘的通常情形,你們能夠本身試試哈。class