LeetCode 470.用Rand7()實現Rand10()
阿新 • • 發佈:2021-08-25
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()的解法
-
就是我上面的解法,先生成均等概率的0和1,然後通過移位來產生1—Y
-
當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} \] -
當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; } } };