几个随机题

1. 已知有个rand7()的函数,返回1到7随机自然数,怎样利用这个rand7()构造rand10(),随机1~10。

产生随机数的主要原则是每个数出现的概率是相等的,如果可以得到一组等概率出现的数字,那么就可以从中找到映射为1~10的方法。

rand7()返回1~7的自然数,构造新的函数 (rand7()-1)*7 + rand7(),这个函数会随机产生1~49的自然数。原因是1~49中的每个数只有唯一的第一个rand7()的值和第二个rand7()的值表示,于是它们出现的概率是相等。

但是这里的数字太多,可以丢弃41~49的数字,把1~40的数字分成10组,每组映射成1~10中的一个,于是可以得到随机的结果。

具体方法是,利用(rand7()-1)*7 + rand7()产生随机数x,如果大于40则继续随机直到小于等于40为止,如果小于等于40,则产生的随机数为(x-1)/4+1。

2. 已知有个randM()的函数,返回1到M随机自然数,怎样利用这个randM()构造randN(),随机1~N。

上题的扩展。

当N<=M时可以直接得到。

当N>M时,类似构造(randM()-1)*M + randM(),可以产生1~M^2(即randM^2),可以在M^2中选出N个构造1~N的映射。

如果M^2还是没有N大,则可以对于randM^2继续构造,直到成功为止。

3. 已知一随机发生器,产生0的概率是p,产生1的概率是1-p,现在要你构造一个发生器,使得它产生0和1的概率均为1/2。

考虑连续产生两个随机数,结果只有四种可能:00、01、10、11,其中产生01和产生10的概率是相等的,均为p*(1-p),于是可以利用这个概率相等的特性等概率地产生01随机数。

比如把01映射为0,10映射为1。于是整个方案就是:

产生两个随机数,如果结果是00或11就丢弃重来,如果结果是01则产生0,结果是10则产生1。

4. 已知一随机发生器,产生的数字的分布不清楚,现在要你构造一个发生器,使得它产生0和1的概率均为1/2。

思路类似,考虑连续产生两个随机数a、b,结果有三种情况a==b,a>b,a<b,其中由于a和b的对称性,a>b和a<b出现的概率是相等的,于是可以利用这个概率相等的特性等概率地产生01随机数。方法类似。

或者可以找到另一种概率相等的事件,比如选择一个阈值th,把随机数的结果分为小于阈值和大于等于阈值两种情况,于是连续产生两个随机数,他们一个小于阈值,另一个大于等于阈值的概率是相等。然后类似产生随机数。

5. 已知一随机发生器,产生0的概率是p,产生1的概率是1-p,构造一个发生器,使得它构造1、2、3的概率均为1/3;…。更一般地,构造一个发生器,使得它构造1、2、3、…n的概率均为1/n。

此时我们已经知道,要从n个数中等概率地产生一个随机数,关键是要找到n个或更多个出现概率相等的事件,然后我们重复随机地产生事件,如果是跟这n个事件不同的事件直接忽略,直到产生这n个事件中的一个,然后就产生跟这个事件匹配的随机数。由于n个事件发生的概率相等,于是产生的随机数的概率也是相等的。

考虑连续产生x个随机数,结果应该是x个0跟1的组合,为了使某些结果出现的概率相等,我们应该要让这个结果中0和1出现的次数相等,即各占一半。于是x的长度必须是偶数的,为了方便,考虑连续产生2x个随机数。每个0跟1各出现一半的结果可以赋予1到n的某个数,为了能够表示这n个数,需要0跟1各出现一半的总结果数大于等于n,即

C(2*x, x) >= n

解出最小的x即为效率最高的x。

然后把前n个0和1个出现一半的结果分别赋予1到n的值。随机时连续产生2*x个数,如果不是这n个结果中的一个则重新随机,如果是的话则产生对应的值作为随机结果。

6. 给出从n个数中随机选择m个数的方法。n很大,可以认为是亿级别。m可以很小,如接近1;也可以很大,如接近n。

一个直接的思路是一直重复地随机,直到随机到m个数为止。这个方法有两个弊端:

  1. 难以直到后面随机到的一个数是否在前面已经随机过了,因为数据量很大,无法保存在内存中,如果保存到外存中则时间花费太大。
  2. 如果m很大,甚至接近于n,则后面随机到的数字基本上都是前面随机过的,因而需要尝试的随机次数太多。

一个思路是每个数被选中的概率是m/n,则可以遍历一遍原数据,在遍历每个数字的同时以m/n的概率决定是否要选择当前数字,则当遍历完毕的时候,选择到的数字在平均意义就是m个。这个会随着n的增大而更好地趋近于m,但不能很精确地保证随机到的数字一定是m个。

以上思路虽然不能满足要求,但我们可以进行改进。刚才我们在遍历每个数字的时候都是以同样的概率m/n决定是否要选择该数字,实际上,在当前遍历数字的前面的数字的结果我们是已经知道了,我们可以根据前面的随机结果动态地调整当前的随机策略,使得最终能够保证随机到的数字一定是m个。

具体的做法是,遍历第1个数字时有m/n的概率进行选择,如果选择了第1个数字,则第2个数字被选择的概率调整为(m-1)/(n-1),如果没选择第1个数字,则第2个数字被选择的概率为m/(n-1)。即遍历到第i个数字的时候,如果此时已经选择了k个,则以(m-k)/(n-i+1)的概率决定是否要选择当前的第i个数字。

这样可以保证每次都能够保证在剩下的数字中能选择适当的数使得总体选择的数字是m个。比如,如果前面已经随机了m个,则后面随机的概率就变为0。如果前面一直都没随机到数字,则后面随机到的概率就会接近1。最终得到的结果始终精确地是m个数字。

7. 给出从n个数中随机选择1个的方法。注意,n非常大,并且一开始不知道其具体值。数字是一个一个给你的,当给完之后,你必须立刻给出随机的结果。

这里n的值非常大,而且要求立即给出答案,所以不能把所有的数字先保存起来,然后再慢慢考虑要随机哪个。

这题跟上面一题比较类似,因为我们不知道数字到底有多少个,所以必须在得到每一个数字的时候就有一个当前的结果,这样在数字给完的时候可以给出答案。

于是第1个数字是必须要拿的。问题是当第2个数字来的时候,究竟要保留手上的数字,还是拿当前的第2个数字呢?更一般地,当第i(i>1)个数字来的时候,究竟是保留手上的数字,还是选择当前的第i个数字呢?

答案是要保证每个数字被选取的概率是相等,当第i个数来的时候,如果我们已经保证了前i-1个数每个数被选取的概率都是相等的,那么只要第i个数字被选取的概率是1/i,我们就可以知道所有i个数被选取的概率都是1/i了。所以只需要以1/i的概率决定是否要选取当前的第i个数字即可。

于是可以保证对于任意的n,当给完n个数字时,选择每个数字的概率都是相等的,为1/n。

8. 给出从n个数中随机选择m个的方法。注意,n非常大,并且一开始不知道其具体值。数字是一个一个给你的,当给完之后,你必须立刻给出随机的结果。

这题是上一题的推广,于是可以仿照着进行。

首先前m个数字是必须拿的。问题是当第i(i>m)个数字来的时候,究竟是要丢弃这个数,还是保留这个数?如果要保留这个数的话,则必须得丢弃手中已有的m个数,那是怎么确定丢弃哪个呢?

下面是就具体的做法。第i个数到来的时候,以m/i的概率决定是否要选择这个数字。如果选择了这个数字,则随机地替换掉手上m个数字中的一个。

如果前i-1个数字的时候保证了每个数字被选取的概率相等,则这样做之后可以保证每个数字被选取的概率也相等,为m/i。

  1. 第i个数选择的概率是m/i,因为算法就是这样决定的。
  2. 考虑前i-1个数字中的任意一个,它在第i个数之前被选择的概率是m/(i-1)。在第i个数字的时候,这个数字要被选择的话又两种可能,一是第i个数没有被选中(概率是1-m/i),二是第i个数倍选中了(概率是m/i)但是替换掉的数字不是它(概率是1-1/m),于是这个数在第i个数时仍然被选择的概率是m/(i-1) * ((1-m/i) + (m/i * (1-1/m))) = m / (i-1) * ((i-1) / i) = m/i。

由数学归纳法原理知,对于任意的n,当给完n个数的时候,选择的结果可以保证这n个数中每个被选中的概率都是相等的,为m/n。

时间: 2024-11-05 16:01:29

几个随机题的相关文章

30道四则运算随机题之02

设计思想:1.判断数值范围用 rand() % range. 2.判断是否有乘除,如果有乘除再判断除法有无余数. 3.判断加减有无负数. 4.选择打印的数量. 5.选择打印的方式.

四则运算随机题30道代码

#include<iostream.h>#include<stdlib.h>int main(){ int a ,b,c,i; for(i=0;i<30;i++){ a=rand()%100; b=rand()%100; c=rand()%4; if(c==0) cout<<a<<"+"<<b<<"="<<endl; else if(c==1) cout<<a<

代码复查

我复审的是胡新宇的代码,我参考了教材的内容,根据教材一步一步地进行了复审,我首先通过VS2015进行了运行,程序可以运行出来,通过测试四则运算程序也没有发生乱码或者闪退等bug.然后我又一步一步地分析他每一行代码的内容和含义,可以很清楚明白的看出他的思想还有要完成的内容.通过对他的代码复审发现其中有很多值得我学习的地方,就如同作业第一道题是一样的,他有着非常严格的代码规范,每一行都可以清楚的看出他思想,而 且代码也没有出现繁复.以后要向他学习. 附其部分代码以便日后学习参考: #include<

山东省第五届省赛总结

省赛终于过去了.感觉准备最充分的一次比赛比的最差,心里很伤心. 省赛之前,目标一直很明确,拿个金牌,向冠军冲刺.没想到最后拿了一个银牌.真的是讽刺. 热身赛的时候,一开始机器很搓.根本运行不了程序.然后就找来技术人员.技术人员搞了半个小时,也没搞出来个毛.最后还是机器自己莫名其妙的能用了. 然后我就上手敲B的随机题.没想到两次就过了,太开心了.然后专心看c题.一开始看出来的做法不对.后来离结束还有一点点的时候,终于想起来了正解.然后跟 scf一说,scf接着上手一敲.结果很对.然后果断交,然后就

四则运算题目的程序

此次作业要求: 编写一个能自动生成小学四则运算题目的程序. 除了整数以外,还能支持分数的四则运算. 对实现的功能进行描述,并且对实现结果要求截图. 题目:自动生成四则运算 主要功能:可以简单方便快的练习加减乘除的运算,完成一些自测的练习.编程软件选择了Visual Studio 2015. 会有三个界面:有分数,整数和退出选项 当选择整数时会有十道题目,如果做对会提示“恭喜你答对了做的不错!“如果做错会提示"答案错误请继续努力”十道题都做完时会出现成绩和分数,整数和退出选项. 分数四则运算里会有

作业2结对(升级版)

//第一个类:登陆界面 import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.border.*; public class Login extends JFrame implements ActionListener{ JTextField f1; JTextField f2; JButton b1; JButton b2; JPanel p5; Image img = Toolk

20172311『Java程序设计』课程 结对编程练习_四则运算第一周阶段总结

20172311『Java程序设计』课程 结对编程练习_四则运算第一周阶段总结 结对伙伴 学号 :20172307 姓名 :黄宇瑭 伙伴第一周博客地址: 需求分析 功能要求 1.自动生成题目 可独立使用(能实现自己编写测试类单独生成题目的功能) 可生成不同等级题目,类似于: 1级题目:2 + 5 =: 10 - 5 = 之类的两个数,一个运算符的题目 2.题目运算(判题) 可独立使用 实现中缀表达式转为后缀表达式并计算 判断用户答题正误,并输出正确结果 3.支持真分数 可独立使用 实现分数算式的

四六级成绩查询,你的『验证码』刷出来了吗?

沉浸在暑假余温里的小可爱们,今天被四六级成绩查询的验证码无情的伤害了一次.就在8月22日,也就是今天上午9点,学霸们泰然自若,学渣们瑟瑟发抖,有的人甚至在心里考虑是否发微博.朋友圈谢谢超越姐姐(此时输入考生信息的小手有点抖). 然而,万万没想到的是,我们无数的考生被这个验证码折了腰,ARE YOU KIDDING ME? 准点进去查成绩,网站崩溃进不去,好不容易挤进去,验证码不知在哪里,刷验证码原来也是这样的不易. 对,就是个坏消息,这个验证码你进不去. 如今各种验证码方式已经融入我们的生活,还

随机产生四则运算题

/*信息:20133075   张勋  <随机生成30道四则运算题目> 要求:除整数外,还要支持真分数的四则运算 设计思路:  1.通过radom函数生成自然数,给变量赋值  2.另一个变量是真分数,分子比分母小  3.随机生成四则运算符  4.注意除数为0情况注意:   这是两个函数!一个是srand函数!这是在调用rand()这个函数之前使用的!rand()是一个产生随机数的函数!而srand是一个设置随机数种子的函数!通常这两个函数是一起使用的!来完成产生随机数的功能!而time(NUL