编程珠玑第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

你可能感兴趣的:(编程珠玑第12章)