1. 程式人生 > 其它 >LeetCode 470.用Rand7()實現Rand10()

LeetCode 470.用Rand7()實現Rand10()

470.用Rand7()實現Rand10()

已有方法 rand7 可生成 1 到 7 範圍內的均勻隨機整數,試寫一個方法 rand10 生成 1 到 10 範圍內的均勻隨機整數。不要使用系統的 Math.random() 方法。

示例 1:

輸入: 1
輸出: [7]

示例 2:

輸入: 2
輸出: [8,4]

示例 3:

輸入: 3
輸出: [8,1,10]

​ 看到這題我一開始的想法是先構造等概率的0和1,然後構建0001-1010(二進位制的1-10)這樣應該是等概率的。

class Solution {
public:
    int rand1()
    {
		int a = rand7();
        while(a==4)
        {
            a = rand7();
        }
        return a<4?0:1;
    }
    int rand10() {
        int res;
        while(true){
        	res = (rand1()<<3)+(rand1()<<2)+(rand1()<<1)+(rand1());
            if(res>0&&res<11)    return res;
        }
    }
};

然後發現速度太慢,開啟官方題解一臉懵逼,然後多看了幾篇別人的題解慢慢理解了思路。

總結下從RandX()——>RandY()的解法

  1. 就是我上面的解法,先生成均等概率的0和1,然後通過移位來產生1—Y

  2. 當X>Y的時候,比較簡單,可以不斷的呼叫RandX()直到產生1—Y。具體的概率證明在下:

    當第一次命中的時候概率為\(\frac{1}{X}\)​​​。

    如果第二次命中,那麼第一次必然沒有命中,那概率則為第一次未命中的概率乘上第二次命中的概率\(\frac{Y-X}{X}*\frac{1}{X}\)

    依次類推,

    \[\begin{align} & P=\frac{1}{X}+\frac{Y-X}{X}*\frac{1}{X}+(\frac{Y-X}{X})^2*\frac{1}{X}+\cdots+(\frac{Y-X}{X})^n*\frac{1}{X}\\ & =\frac{1}{X}*(1+\frac{Y-X}{X}+(\frac{Y-X}{X})^2+\cdots+(\frac{Y-X}{X})^n)\\ & =\frac{1}{X}*(1+\frac{\frac{Y-X}{X}*(1-(\frac{Y-X}{X})^n)}{1-\frac{Y-X}{X}})\\ & =\frac{1}{X}*(1+\frac{Y-X}{2X-Y})\\ & =\frac{1}{X}*(\frac{X}{2X-Y})\\ & =\frac{1}{2X-Y} \end{align} \]
  3. 當X<Y的時候,我們可以用到(RandX()-1)*X+RandX()生成\(1-X^2\)的隨機整數

    然後通過拒絕取樣(當生成的數>Y時重新生成)

    以此題為例程式碼如下

    class Solution {
    public:
        int rand10() {
            int a,b,res;
            do{
    			a = rand7();
                b = rand7();
                res = (a-1)*7+b;
            }while(res>40);
            return (res-1)%10+1;
        }
    };
    

    當然這樣我們捨棄的數字還是比較多,我們如何進一步的優化利用被捨棄的這些數,當生成的隨機數被拒絕的時候我們也同時產生了1-9的隨機數,如果在呼叫一次rand7(),我們就能生成1-63的隨機數,我們再拒絕61-63,再呼叫一次rand7()生成1-21的隨機數,拒絕21,此時只剩下隨機數1

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