[LeetCode] 470. Implement Rand10() Using Rand7() 使用Rand7()來實現Rand10()


 

Given a function rand7 which generates a uniform random integer in the range 1 to 7, write a function rand10 which generates a uniform random integer in the range 1 to 10.

Do NOT use system's Math.random().

 

Example 1:

Input: 1
Output: [7] 

Example 2:

Input: 2
Output: [8,4] 

Example 3:

Input: 3
Output: [8,1,10] 

 

Note:

  1. rand7 is predefined.
  2. Each testcase has one argument: n, the number of times that rand10 is called.

 

Follow up:

  1. What is the expected value for the number of calls to rand7() function?
  2. Could you minimize the number of calls to rand7()?

 

這道題給了我們一個隨機生成 [1, 7] 內數字的函數 rand7(),需要利用其來生成一個能隨機生成 [1, 10] 內數字的函數 rand10(),注意這里的隨機生成的意思是等概率生成范圍內的數字。這是一道很有意思的題目,由於 rand7() 只能生成1到7之間的數字,所以 8,9,10 這三個沒法生成,那么怎么辦?大多數人可能第一個想法就是,再用一個唄,然后把兩次的結果加起來,范圍不就擴大了么,擴大成了 [2, 14] 之間,然后如果再減去1,范圍不就是 [1, 13] 了么。想法不錯,但是有個問題,這個范圍內的每個數字生成的概率不是都相等的,為啥這么說呢,我們來舉個簡單的例子看下,就比如說 rand2(),我們知道其可以生成兩個數字1和2,且每個的概率都是 1/2。那么對於 (rand2() - 1) + rand2()呢,看一下:

rand2() - 1 + rand()2  =   ?
   1            1          1
   1            2          2
   2            1          2
   2            2          3

我們發現,生成數字范圍 [1, 3] 之間的數字並不是等概率大,其中2出現的概率為 1/2,1和3分別為 1/4。這就不隨機了。問題出在哪里了呢,如果直接相加,不同組合可能會產生相同的數字,比如 1+2 和 2+1 都是3。所以需要給第一個 rand2() 升一個維度,讓其乘上一個數字,再相加。比如對於 (rand2() - 1) * 2 + rand2(),如下:

(rand2() - 1) * 2 + rand()2  =   ?
     1                  1         1
     1                  2         2
     2                  1         3
     2                  2         4

這時右邊生成的 1,2,3,4 就是等概率出現的了。這樣就通過使用 rand2(),來生成 rand4()了。那么反過來想一下,可以通過 rand4() 來生成 rand2(),其實更加簡單,我們只需通過 rand4() % 2 + 1 即可,如下:

rand4() % 2 + 1 =  ?
   1               2
   2               1
   3               2
   4               1

同理,我們也可以通過 rand6() 來生成 rand2(),我們只需通過 rand6() % 2 + 1 即可,如下:

 rand6() % 2 + 1 =  ?
   1               2
   2               1
   3               2
   4               1
   5               2
   6               1

所以,回到這道題,我們可以先湊出 rand10*N(),然后再通過 rand10*N() % 10 + 1 來獲得 rand10()。那么,只需要將 rand7() 轉化為 rand10*N() 即可,根據前面的講解,我們轉化也必須要保持等概率,那么就可以變化為 (rand7() - 1) * 7 + rand7(),就轉為了 rand49()。但是 49 不是 10 的倍數,不過 49 包括好幾個 10 的倍數,比如 40,30,20,10 等。這里,我們需要把 rand49() 轉為 rand40(),需要用到 拒絕采樣 Rejection Sampling,總感覺名字很奇怪,之前都沒有聽說過這個采樣方法,刷題也是個不停學習新東西的過程呢。簡單來說,這種采樣方法就是隨機到需要的數字就接受,不是需要的就拒絕,並重新采樣,這樣還能保持等概率,具體的證明這里就不講解了,博主也不會,有興趣的童鞋們可以去 Google 一下~ 這里直接用結論就好啦,當用  rand49() 生成一個 [1, 49] 范圍內的隨機數,如果其在 [1, 40] 范圍內,我們就將其轉為 rand10() 范圍內的數字,直接對 10 去余並加1,返回即可。如果不是,則繼續循環即可,參見代碼如下:

 

解法一:

class Solution {
public:
    int rand10() {
        while (true) {
            int num = (rand7() - 1) * 7 + rand7();
            if (num <= 40) return num % 10 + 1;
        }
    }
};

 

我們可以不用 while 循環,而采用調用遞歸函數,從而兩行就搞定,叼不叼~

 

解法二:

class Solution {
public:
    int rand10() {
        int num = (rand7() - 1) * 7 + rand7();
        return (num <= 40) ? (num % 10 + 1) : rand10();
    }
};

 

我們還可以對上面的解法進行一下優化,因為說實話在 [1, 49] 的范圍內隨機到 [41, 49] 內的數字概率還是挺高的,我們可以做進一步的處理,就是當循環到這九個數字的時候,我們不重新采樣,而是做進一步的處理,將采樣到的數字減去 40,這樣就相當於有了個 rand9(),那么通過 (rand9() - 1) * 7 + rand7(),可以變成 rand63(),對 rand63() 進行拒絕采樣,得到 rand60(),從而又可以得到 rand10()了,此時還會多余出3個數字,[61, 63],通過減去 60,得到 rand3(),再通過變換 (rand3() - 1) * 7 + rand7() 得到 rand21(),此時再次調用拒絕采樣,得到 rand20(),進而得到 rand10(),此時就只多余出一個 21,重復整個循環的概率就變的很小了,參見代碼如下:

 

解法三:

class Solution {
public:
    int rand10() {
        while (true) {
            int a = rand7(), b = rand7();
            int num = (a - 1) * 7 + b;
            if (num <= 40) return num % 10 + 1;
            a = num - 40, b = rand7();
            num = (a - 1) * 7 + b;
            if (num <= 60) return num % 10 + 1;
            a = num - 60, b = rand7();
            num = (a - 1) * 7 + b;
            if (num <= 20) return num % 10 + 1;
        }
    }
};

 

Github 同步地址:

https://github.com/grandyang/leetcode/issues/470

 

類似題目:

Generate Random Point in a Circle

 

參考資料:

https://leetcode.com/problems/implement-rand10-using-rand7/

https://leetcode.com/problems/implement-rand10-using-rand7/discuss/152282/C%2B%2B-2-line

https://leetcode.com/problems/implement-rand10-using-rand7/discuss/175450/Java-Solution-explain-this-problem-with-2D-matrix

 

LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM