内容来自互联网,做了一些修改

参考:

http://blog.csdn.net/tianshuai1111/article/details/7583297

http://blog.csdn.net/tianshuai1111/article/details/7583297

http://blog.csdn.net/tianshuai1111/article/details/7583297

http://blog.csdn.net/tianshuai1111/article/details/7583297

1

(1)C库函数rand()通常返回约15个随机位。如果要求返回至少30个随机位应该怎么处理?

rand()函数介绍:http://blog.csdn.net/wordwarwordwar/article/details/41006669

给定一个rand(),可以产生从0到RAND_MAX的随机数,其中RAND_MAX很大(常见值:16位int能表示的最大整数32767),写出利用rand()生成[a,b]中任意整数的函数,其中a>=0, b<=RAND_MAX,且b-a<<RAND_MAX.

利用库函数<stdlib.h>中的rand()函数可以产生0到RAND_MAX范围内的随机整数。

   RAND_MAX是在前面头文件中定义的宏,具体大小与实现有关,至少为32767(2^15-1).

   一般32位机,int型为4字节,故RAND_MAX大小为2147483647(2^31-1).

  两个相关函数:

   产生很大随机整数bigrand():RAND_MAX *rand() + rand();实际上就是先产生前15位,再产生后15位,这样的话就可以返回至少30个随机位。

也即rand()<<15 | rand();

  产生指定范围随机整数 randint(l,u): rand()%(u-l+1)+l; 产生的数据在[l,u]之间.

(2)产生[m,n]范围内的随机整数

<span style="font-size:18px;"></pre><pre name="code" class="cpp">#include<iostream>
#include<stdlib.h>
#include<time.h>
using namespace std;
void GetRandomNum(int n,int m)//产生[0~n-1)范围内的m个随机数.注意包括了0但是不包括n-1.
{srand(time(NULL));for(int j=0;j<3;j++){cout<<rand()%(n-m+1)+m<<endl;}
}
int main()
{int n=60;int m=40;GetRandomNum(n,m);return 0;
}</span>

2

题目不要求子集的概率相等,就可以不使用前面的方法

的“以等概率选择搜有元素,但是有些m元子集被选中的概率比其他子集大”的算法:直接选择1个数,则这个m元集合为它本身即后续的一共m个数,可能包括回绕。

如果要求子集的概率相等,这里就不能够采用上面的做法了。

  抽象后的问题如下:输入两个整数m和n,(m < n).输出0~n-1范围内的m个随机整数的有序列表,不允许重复。

  也就是说,要对0~n-1范围内的数字进行选择,每个数字被选中的概率相等.

  有两点要注意:不允许重复,结果有序;

2.2方法一:使用set集

  这种方法也是一种较为通用的方法,使用set不仅排除了生成相同的数字,对其遍历的过程也实现了排序。

void getsets(int m, int n){set<int> S;while(S.size() < m)S.insert(rand()%n);      //insert保证了如果出现S中已有数字,那就什么都不做set<int>::iterator i;for(i = S.begin(); i != S.end(); i++)cout << *i << endl;}

   时间效率:set每次插入时间为O(logm),while循环总共进行了m次插入,遍历集合需要O(m).故总时间为O(mlogm).额外的空间需求为set集的大小:O(m).

  2.3方法二:Knuth的算法S

  依次考虑整数 0,1,2,……,n-1,通过一个适当的随机测试对每个整数进行选择。通过按序访问整数,保证输出结果有序。

  设m=2, n=5,那么选择第一个整数0的概率就是2/5,这种确定概率,通过 if (rand()%5) < 2 来判断是否选取该数字,然后判断是否选择整数1,若0被选中,以 1/4 的概率选择1, 若0未被选中,以 2/4 的概率选择1,…… ,总之,对于从r个剩余整数中选s个,以 s/r 来选择下一个数。

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

时间效率:该程序的时间复杂度是与n有关的:O(n),空间上只需要几十个字节。

2.4方法三:打乱数组顺序

   对于包含整数0~n-1的数组,打乱数组的前m个元素,然后把前m个元素排序输出即可。

int randint(int l, int u)

//输出i到n-1之间的随机数

{
return l + bigrand() % (u - l + 1);
}

 void genshuf(int m, int n){vector<int> x(n);for(int i = 0; i < n; i++)x[i] = i;for(int j = 0; j < m; j++){int k = randint(i, n-1);//输出i到n-1之间的随机数SWAP(x[i], x[k]);}sort(x, x+m);for(int j = 0; j < m; j++)cout << x[j] << endl;}

时间效率:时间上为O(n+mlogm), 一次初始化和排序。空间上也需要O(n).

  2.5其他方法

  根据问题的实际情况(m和n的相对大小),如若n为100万,m为n-10时,可以生成10个元素的随机样本,然后输出不在样本中的整数

从一般到特殊

  以上讨论的几种方式都不限定m和n的取值,只需m<=n即可。对于特殊的取值,有特殊的解决方案,以下是编程珠玑上的两例:

  1.n = 106而m=n-10,这时可以先生成10个元素,然后输出其余的元素。进行这种处理的额外代码可以提高算法的平均速度。

  2.n=231而m = 107,m<<n,这时可以先生成1.1*107个元素,排序后去掉重复的(由于n很大,m中出现重复元素的概率很低),得到 107个元素的样本。

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张随机赠送的赠券,需要多少次? nln(n)次

对于集合插入问题,每次产生随机数后都需要在集合中检测该元素是否已经存在,这个测试次数和调用随机数函数的次数相同。对于从n个元素中选择m个元素的问题,平均需要测试多少次才能保证选择了m个元素?

这里先处理特殊的问题,即“赠券收集问题”:必须收集多少张棒球卡才能保证拥有所有的n种卡?

记Pi为从收集了i-1种到i种的概率,Pi=(n-i+1)/n

此时需要收集的卡片数目ni = 1* Pi + 2*(1-Pi)*Pi + 3*(1-Pi)2*Pi + ... + n*(1-Pi)n*Pi + ...,这个无穷级数可以求解为ni=1/Pi

那么所需要总的卡片数目sum(ni)  = n1+n2 +... +nn约为nlnn + γn + O(1)。相关维基百科

对于从n个元素中选择m个,相应地sum(ni)  = n1+n2 +... +nm,约为nlnm+γn。(根据调和数的推导)

8

从0~n-1中随机选择m个数,

    输出顺序随机,不重复:(使用方法2.4,但不进行排序)

    fori = [0,n)
              x[i] = i;
           for j = [0,m)
              int k = randint(i, n-1);
              SWAP(x[i], x[k]);

    for i = [0,m)

      cout << x[i]

    输出顺序随机,可重复:(最普通的情况)

      for i = [0, m)

        cout << rand() % n;

    输出有序,不重复:

      前面写的三种方法均可。

    输出有序,可重复:

      删除对生成数字是否已在结果集的判断,(可以采用第13章将要介绍的各种数据结构,也可以直接使用multiset标准STL)

对输出顺序随机,可重复的结果再进行排序

9

上面方法中,如果m接近于n的时候,基于集合的算法(使用容器set)生成的很多随机数都要丢掉。求一个算法,即使在最坏的情况下也只是使用m个随机数。

<span style="font-size:18px;">#include <iostream>
#include<time.h>
#include <set>
using namespace std;
void getSet(int m,int n)//在0 -- n-1 中挑选m个 随机数
{  srand(time(NULL));//这个很关键   set<int> S;  for(int i=n-m;i<n;++i)  {  int t=rand()%(i+1);  //t是[0~i)之间的随机数if(S.find(t) == S.end())//s.find(t)如果s容器中存在按t索引的元素,则返回指向该元素的迭代器。如                                 //果不存在则返回超出末端迭代器  S.insert(t);  else  S.insert(i);  }   set<int>::iterator j;  for(j=S.begin();j!=S.end();++j)  cout<<*j<<" ";
}
int main()
{   getSet(5,10);  return 0;
}   </span>

10

问题:如何随机从n个对象中选择一个对象,这n个对象是按序排列的,但是在此之前你并不知道n的值?

具体些说,在事先并不知道行数的情况下,如何读一个文本文件,随机选择并输出一行?

解答:我们总是选择第一行,并使用二分之一的概率选择第二行,使用三分之一的概率选择第三行,以此类推。在该过程结束的时候,每一行具有相同的选中概率(1/n,其中n是文件的总行数):

i = 0    while more input lineswith probability 1.0/++ichoice = this input line  //如果前面做了选择,并不会break,而是直到最后一个为止。print choice

这里比较有些疑惑的是第一行:总是选第一行 为什么概率还是1/n?

概率=1*(1/2)*(2/3)*(3/4)……(n-1/n) =1/n

<span style="font-size:18px;">int random_select(void)
{int i,num=1;for(i=1;i<n;i++)//i<n代表某种终止条件,n未知    if(rand()%i ==0)num = i;return num;
}</span>

12

每个玩家有一张包含16个覆盖点的纸牌,覆盖点下面隐藏着1~16的随机排列。玩家每次刮开一个点,如果出现3,则判玩家负;如果出现1或2,则判玩家胜。那么,随机选择覆盖点刮开,获胜的概率是多少?

不要把问题复杂化,应该尽量简化。4~16的数字都是没有用的。这里只用考虑1,2,3的排列,共有3*2*1=6种。获胜排列方法只有两种就是*1*2*3*或*2*1*3*

所以获胜的概率是1/3.

编程珠玑第12章习题相关推荐

  1. 编程珠玑之第二章习题5

    问题描述: n元一维向量旋转问题数将向量ab变为ba.如何将向量abc变为cba? (这对交换非相邻内存块问题进行了建模) 问题解析: 1.这里需要用到一个重要的性质:CBA=(ArBrCr)r    ...

  2. 编程珠玑第五章习题五——C++实现二分搜索时进行错误检测

    一,概述 主要讲解如何保证编程的正确性.在程序中加入断言(assert(断言内容) //如果错误,则终止程序.否则正常执行). typdef   //声明自定义类型 typedef int size; ...

  3. 编程珠玑第六章习题二——C++实现一个数的因子分解

    引申为一道题目: 将大于1的自然数N进行因式分解,满足N=a1*a2*a3-am 编一程序,对任意的自然数N, 求N的所有形式不同的因式分解方案总数. 如N=12,共有8种分解方案,它们分别是: 12 ...

  4. 编程珠玑第四章习题答案

    主要内容来自于互联网,自己做了一定的修改 1.为了保证范围不超过范围,我们需要在初始化的时候,让变量不超出范围.这样每次循环得到的新的范围是慢慢缩小的,不会越界. 2 返回t在数组x中第一次出现的位置 ...

  5. 编程珠玑第三章习题答案

    1 税收问题 .if-else语句的每个分支的形式都差不多,我们可以用数组来使循环简单一点.数组中每个点表明一个阶段,用level[i]表示阶段i的起始点,tax[i]表示阶段i的税率.然后就是输入一 ...

  6. 编程珠玑之第二章习题10

    问题描述: 10.某一天,一个新研究员向托马斯·爱迪生报到.爱迪生要求他计算出一个空灯泡盒的容积.在使用测径仪和微积分进行数小时的计算后,这个新员工给出了一个答案--150立方厘米.而爱迪生在几秒钟之 ...

  7. 单片微型计算机徐春辉,单片微机原理及应用 徐春辉第12章 习题答案

    第12章习题解答 1.I/O接口和I/O端口有什么区别?I/O接口的功能是什么? 解:I/O端口简称I/O口,常指I/O接口电路中具有端口地址的寄存器或缓冲器.I/O接口是指单片机与外设间的I/O接口 ...

  8. 【编程珠玑】陪着奶猫看看书--《编程珠玑》第一章

    陪着奶猫看看书–<编程珠玑>第一章 首先说说小奶猫我为什么要读<编程珠玑>这本神作,当年小奶猫刚刚进入大学时候是个纯洁的少年,啥都不懂,要是哪个女生下午在外面问我带身份证没有, ...

  9. c语言判断正整数位数 请用strengh,C语言程序设计-4、12章习题解答.doc

    C语言程序设计-4.12章习题解答 C语言程序设计概述 一个C程序的执行是从 A . A.从main()函数开始,直到main()函数结束B.第一个函数开始,直到最后一个函数结束C.第一个语句开始,直 ...

最新文章

  1. Pytorch笔记(python--类与对象(class and module))
  2. 每天一道LeetCode-----给定序列中2/3/4个元素的和为target的所有集合,或3个元素的和最接近target的集合
  3. some example of SAP odata annotation in metadata
  4. 连接access时的REGDB_E_CLASSNOTREG(0x80040154)错误
  5. 在VMware中的Ubuntu虚拟机安装open-vm-tools调整屏幕可以复制粘贴
  6. Nutanix的野心可不小!
  7. jq中get()和eq()的区别
  8. centos 6 apt.sw.be 错误 无法yum安装软件解决方案
  9. 荒唐可笑的文言文编程语言
  10. CC2530射频通信
  11. 扒一扒贝索斯的接班人,为何选他挑起大梁?
  12. 用户金字塔模型的应用:知乎案例分析
  13. 论文公式自动编号及引用(自动更新)
  14. 注册公司的基本流程 version_1.0
  15. 腾讯云之轻量应用服务器搭建Socks5代理服务器实现游戏单窗口单IP
  16. ZOJ3594 Sexagenary Cycle
  17. 如何清除/删除最近的文档历史记录?
  18. 用鸽 计算机教案,幼儿园音乐教案《鸽子》
  19. 计算机组装与维护论文 致谢,计算机组装与维护论文
  20. 【JSON】04_JSON的生成与解析

热门文章

  1. 2021-春季学习-智能车技术创新与实践-Lesson 1
  2. 单层神经网络-Logistics回归中误差曲线
  3. 智能车竞赛云端比赛第三天:一场在家具建材广场中的智能车比赛
  4. 通过FFT来计算螺旋线的匝数
  5. 低内阻的MOS管 4N04R7
  6. idea 快速定位到某一行的快捷键
  7. 未解决计算机主机与打印机,电脑无法与打印机连接 计算机网考题目2(12)
  8. 硬盘突然提示没有初始化_分享一下固态硬盘不认盘的修复方法
  9. 关于虚拟机的三种网络接口模式(以VXBOX虚拟机为例)
  10. ajax如何请求json文件,简单的ajax请求加载外部json文件