給定能隨機生成整數1到5的函數,寫出能隨機生成整數1到7的函數(均勻概率問題)


google面試題:給定能隨機生成整數1到5的函數,寫出能隨機生成整數1到7的函數。

問題分析:現在給了一個能隨機生成1~5的隨機函數,怎樣利用這個已知條件生成一個1~7的隨機函數呢?既然要生成的是隨機數那么生成1,2,3,4,5,6,7的概率就應該是一樣的。顯然現在光生成1~5之間的數就不夠了,我們想到應該要加大生成數的范圍,並且加大范圍的同時還要保證每個數產生的概率一樣,於是有這樣一種方法用這個表達式來擴大生成數范圍:rand5()*5+rand5(),新的數據范圍變成:6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30.並且可以看出來這個25個數出現的可能性是一樣的,於是我們可以只用6~26之間的21個數變成1~7這7個數,於是就是要每3個數對應一個數,即:

6,7,8對應1

9,10,11對應2

…………

24,25,26對應7

這種變化對應的方式是(6 - 3)/ 3 = 1,(7 - 3) / 3 = 1,(8-3) / 3 = 1.

int rand7()
{
    int i;
    //直到產生6~26之間的數跳出循環
    while((i=rand5()*5+rand5()) > 26) ;
    return (i-3)/3;
}

總感覺這種方式不好,雖然1-7概率均勻,但相加不等於1。現在深入分析:

假設我們要等概率生成一個3位的10進制數(000 - 999),我們可以在 隨機生成整數0到9的函數 基礎上,隨機生成3個數字組成三位數就得到了結果。

這里類似,我們首先必須認識到:任何一個數都可以用5進制的數來表示,如12 = 5進制(22) = 2*5 + 2。因此假設我們要隨機生成[0,444]范圍的數,我們只要隨機生成3個5進制的數字組合就可以。

這里的主要問題是:7不是5的冪次方。

但是我們可以將某一個5的冪次方均分成 7 段(分別為0 - 6,等概率的落到每一段),利用5進制隨機成一個數,看這個數在哪一個段,就代表我們要生成哪一個數字,這樣就保證了等概率的生成0 - 6.

有一個小的問題就是:5的冪次方不能整除7,會遺漏最高的幾個數。但是我們這里只要數字足夠大,則遺漏的概率相當的小

下面是簡單的代碼:

const int K = 10;//[0, 5^K - 1]
int Base[K],Rnd[K],Step; //Step表每個區間的長度
int Rand15() //已有的隨機生成1 - 5的隨機函數
{
    return rand()%5 + 1;
}
void Init()
{
    Base[0] = 1;
    for(int i=1 ;i<K; ++i)
        Base[i] = Base[i-1]*5;
    Step = Base[K-1]*5/7;
    //cout<<"Step = "<<Step<<"\tStep*7 = "<<Step*7<<endl;
    srand(time(0));
}
int Rand17() //我們需要寫的隨機生成1 - 7的隨機函數
{
    int Sum = 0;
    for(int i=0 ;i<K; ++i) //隨機生成K個1到5的隨機數
    {
        Rnd[i] = Rand15();
        Sum += (Rnd[i]-1)*Base[i];
    }
    return Sum/Step + 1;
}

這里K = 10的時候,保證遺漏了2個數,即落到余數里面的概率是:2/9765625.

利用rand[1,5】得到rand[1,7]可以抽象成利用rand[0,1]得到rand[0,n],看下面的就清楚了。

-----------------------------------------

題目:已知rand7() 可以產生 1~7 的7個數(均勻概率),利用rand7() 產生rand10() 1~10(均勻概率)

記住這道題重點是:均勻概率。

rand7 產生的數概率是一樣的,即1~7出現概率一樣,由於我們對結果做了一定的篩選只能通過 1~5,而1~5出現的概率也是一樣的,又由於范圍為1~5 所以 temp1 出現 1~5的概率 為1/5 ,同理 后面的 出現 temp2 的概率為 1/2。

首先temp1出現在1~5的概率為1/5,而temp2出現 1~2 的概率為1/2,也就是說 5*(temp2-1) 出現5或0的概率為1/2,所以假如你要得到1~5的數的話 那么 5*(temp2-1) 必須0,所以因為你要保證 5*(temp2-1)=0,這個概率只有1/2,再加上 你前面指定1~5 的概率 為1/5 ,所以結果為 1/5*1/2=1/10

int rand10() 
02
{ 
03
    int temp1; 
04
    int temp2; 
05
    do 
06
    { 
07
        temp1 = rand7(); 
08
    }while(temp1>5); 
09
    do 
10
    { 
11
        temp2 = rand7(); 
12
    }while(temp2>2); 
13
    return temp1+5*(temp2-1); 
14
} 

已知一個函數f可以得到1-5間的隨機數,問怎么得到1-7的隨機數

對不對?

這個其實和算法導論上的一個題很像么:已知random等概率返回0或者1,那么試寫一個函數等概率返回[a,b]之間的整數。思路就是2進制表示[0, b-a]之間的數,先計算出至少需要多少位,按位生成一個二進制數,一旦大於b-a就重新生成。放到這里的話,表示成5進制就可以了~

推廣一下:對於等概率可以生成k個連續整數函數的函數randomk,設計生成[a,b]之間的整數的算法:
令n = b - a;則等概率生成[0,n]上的一個整數即可。於是用k進制表示生成的整數,設m=ceiling(logk(n)),(這個不對)

k^m-1=n; 求解m。m=ceil(logk(n+1)); 向上取整,如4,應該用3個二進制表示. log2(5) 向上取整. (m還可以寫成:

m= 1 +log2n)

  1. int randN() {
  2.   while (res > n) {
  3.      for (i = 0; i < m; i++)  
  4.          gen bit i for res with randomk
  5.   }
  6.   return res;
  7. }
  8. 期望的運行時間為t*m* i * (1 - (n+1)/k^m)^(i-1) * ((n+1)/k^m),i從1加到無窮,t表示randomk的運行時間,那么計算這個級數的值為t*m*k^m/(n+1).
    帶入到這個題目,期望運行時間為50*t/7,還是很快的。
 利用rand()得到rand[0,n]的代碼:
void set(int &a,int i) { a |= (1<<i);}//一定要是引用
int randN(int n)
{
  
    int res=n+1;//讓第一次進入while循環,res只要》n即可.
    //c++只有log(默認以e為底)和log10,如果自己設定底數用換底公式
    float floatN=n;
    float tmp=log(floatN+1)/log(2.0);
    int m=ceil(tmp);
    //cout<<"m:"<<m<<endl;
    while(res>n)
    {
        res=0;
        for(int i=0;i<m;i++)
        {
            Sleep(20);
            srand(time(NULL));
            
            int bit=rand()%(2-0)+0;//[0,1]
            if(bit==1)
            {
                set(res,i);
            }
        }
    }
    return res;
}

參考:http://blog.csdn.net/pkueecser/article/details/7425994

 


免責聲明!

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



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