天天看點

程式設計珠玑第12章

正文

如何生成0~n-1内的m個随機整數

1、方法一

比如要從5個數裡選出2個數,第一個數的機率是2/5,第二個數的機率是1/4,然後是0/3

那麼現在已經很清楚了。

可以寫代碼如下:

for (int i = 0; i < n; ++i)
     if (rand() %(n-i) < m)
      {    cout << i << endl; 
            --m;
        }
           

2、方法二

可以用一個set,每生成一個随機,就去set裡面檢視一下有沒有在set裡面,如果沒有就插入進去,直到set大小為m.

其實用散列應該更快。

3、方法三

首先把生n個随機數(注意範圍),用第一章裡面講到的随機數唯一生成算法生成之後,取前面m個就可以了。

4、方法四

for (int j = n - m; j < n; ++j)
{
      int t = rand() % (j  + 1);  //注意取模的對象:生成[0,j]之間的随機數
      if (set.find(t) == set.end())
           set.insert(t); 
      else set.insert(j);   //注意放進去的數值。
}
           

習題

1、

參考答案

int bigrand()
{
      return RAND_MAX*rand() + rand();
}
int region(int l, int u)  //[l, u]
{
     ++u;
      return l + rand() % (u - l);
}
           

2、

算法導論中提到過這個問題,可以這樣設計一下,随機選擇一個數,随其後的m個數就被選中(假設這些數排列成圓形)。

3、當m < n/2時,

總共試了k次,則前面k-1次找到的數都是在集合中,那麼隻有第k次不在裡面,那麼機率

p = (m/n)^(k-1) * (n-m)/n

那麼期望是 連加 k = 1至無窮大,根據二項式分布可知,期望等于

n/(n-m) < 2

進而可知得證

4、這裡可以看一下算法導論裡面的中文版64頁的題目,把n個球投到m個框中,每個框中至少有一個球時,至少要投mlgm次。

     如果是n則是nlogn.

5、6、

7、非遞歸的代碼在正文中,是按照升序進行輸出。

      第二個問題,生成n個元素的m元子集的所有情況,應該可以使用DFS來進行處理。

8、先生成這些數,然後再按第一章的處理,生成亂序的情況。

     調用m次随機數函數生成器。輸出即可。

9、使用floyd的生成算法。

10、答案可能比較難想到。

問題:如何随機從n個對象中選擇一個對象,這n個對象是按序排列的,但是在此之前你并不知道n的值?具體些說,在事先并不知道行數的情況下,如何讀一個文本檔案,随機選擇并輸出一行?

證明: 解答:我們總是選擇第一行,并使用二分之一的機率選擇第二行,使用三分之一的機率選擇第三行,以此類推。在該過程結束的時候,每一行具有相同的選中機率(1/n,其中n是檔案的總行數):

i = 0

while more input lines

with probability 1.0/++i

choice = this input line  //如果前面做了選擇,并不會break,而是直到最後一個為止。

print choice

證明:

當做第i步選擇(選擇第i行)時,選擇該行的機率為1/i,則不選擇的機率為(i-1)/i

對于一篇有n行的文檔,現需證明最終標明第i行的機率為1/n。

當最終選擇第i行,前(i-1)步的選擇對最終結果不會産生影響,第i步選擇的機率為1/i,即選擇第i行,第(i+1~n)步中均采取不選擇的動作,即對于任意j(i+1<=j<=n),目前步的機率為(j-1)/j,那麼最終的機率為:

(1/i)*((i)/(i+1))*...*((n-1)/n) = 1/n

以一篇隻有6行的文檔為例,最終選擇第2行的機率為:

1/2*(2/3)*(3/4)*(4/5)*(5/6) = 1/6

擴充:

原問題可簡化為:

如何從n個有序對象中等機率地任意抽取1個,簡記為sample(n,1),其中n未知;

若将該問題改為:

如何從n個有序對象中等機率地任意抽取m個,簡記為sample(n,m),其中n未知;

分析:

若n已知,sample(n,m)是普通的抽樣問題;當n未知時,可否根據上述算法進行相應的轉化求解?

解決方案:

将sample(n,m)問題轉化為m個sample(n*,1)問題,更具體一點是,轉化為

sample(n,1);sample(n-1,1);sample(n-2,1)....;sample(n-m+1,1)問題。

仍然以一篇6行文檔為例,任取其中2行,做法如下:

第一遍,以如下機率選中一行:

1(1)   2(1/2)  3(1/3)  4(1/4)  5(1/5)  6(1/6)

假設選中第2行,接着機率修改如下:

3(1)  4(1/2)  5(1/3)  6(1/4)  1(1/5)

[說明]:當選中第2行,從第3行開始修改機率,并将第2行排除在外,繼續掃描,這樣能保證在剩下的5個數中仍然以等機率抽取其中的一個。

11、實際上如查刮到1,2,3以外的數,就繼續,是以看遇到這三個數中的哪一個,是以勝的機率是2/3,輸的機率是1/3

繼續閱讀