随机题

--------------------------------------------------------------算法

程序的输入为两个整数m和n,m<n。要输出0~n-1范围内的m个随机整数的有序列表,m个整数不容许重复。这个题目来自<编程珠玑>。编程

 

解法1:函数

由于要求序列有序,那么咱们能够依序对0到n-1的每个数作决策:输出仍是不输出?很显然每一个数字输出的几率应该是m/n才对。用Ai表示第i个数字被输出,须要注意的是,A0,A1,A2...之间不是独立的,好比P(A1)=(m-1/n-1)P(A0)+(m/n-1)(1-P(A0)),也就是对于0有没有被输出,输出1的几率是不一样的。事件

下面是C++代码,能够仔细琢磨一下内存

 

--------------------------------------------------------------数学

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

 

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、0一、十、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,构造一个发生器,使得它构造一、二、3的几率均为1/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。

 

Sulutions:

1,

产生随机数的主要原则是每一个数出现的几率是相等的,若是能够获得一组等几率出现的数字,那么就能够从中找到映射为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。

相关文章
相关标签/搜索