转自http://www.code123.cc/959.html

先给出一个例子,后面会有扩展

题目

给你一个能生成1到5随机数的函数,用它写一个函数生成1到7的随机数。 (即:使用函数rand5()来实现函数rand7())。

解答

rand5可以随机生成1,2,3,4,5;rand7可以随机生成1,2,3,4,5,6,7。 rand5并不能直接产生6,7,所以直接用rand5去实现函数rand7似乎不太好入手。 如果反过来呢?给你rand7,让你实现rand5,这个好实现吗?

一个非常直观的想法就是不断地调用rand7,直到它产生1到5之间的数,然后返回。 代码如下:

 
1
2
3
4
5
6
int Rand5(){
    int x = ~(1<<31); // max int
    while(x > 5)
        x = Rand7();
    return x;
}

等等,这个函数可以等概率地产生1到5的数吗?首先,它确确实实只会返回1到5这几个数, 其次,对于这些数,都是由Rand7等概率产生的(1/7),没有对任何一个数有偏袒, 直觉告诉我们,Rand5就是等概率地产生1到5的。事实呢?让我们来计算一下, 产生1到5中的数的概率是不是1/5就OK了。比如说,让我们来计算一下Rand5生成1 的概率是多少。上面的函数中有个while循环,只要没生成1到5间的数就会一直执行下去。 因此,我们要的1可能是第一次调用Rand7时产生,也可能是第二次,第三次,…第n次。 第1次就生成1,概率是1/7;第2次生成1,说明第1次没生成1到5间的数而生成了6,7, 所以概率是(2/7)*(1/7),依次类推。生成1的概率计算如下:

 
1
2
3
4
5
6
P(x=1)=1/7 + (2/7) * 1/7 + (2/7)^2 * 1/7 + (2/7)^3 * 1/7 + ...
      =1/7 * (1 + 2/7 + (2/7)^2 + ...) // 等比数列
      =1/7 * 1 / (1 - 2/7)
      =1/7 * 7/5
      =1/5
 

上述计算说明Rand5是等概率地生成1,2,3,4,5的(1/5的概率)。从上面的分析中, 我们可以得到一个一般的结论,如果a > b,那么一定可以用Randa去实现Randb。其中, Randa表示等概率生成1到a的函数,Randb表示等概率生成1到b的函数。代码如下:

 
1
2
3
4
5
6
7
// a > b
int Randb(){
    int x = ~(1<<31); // max int
    while(x > b)
        x = Randa();
    return x;
}

回到正题,现在题目要求我们要用Rand5来实现Rand7,只要我们将Rand5 映射到一个能产生更大随机数的Randa,其中a > 7,就可以套用上面的模板了。 这里要注意一点的是,你映射后的Randa一定是要满足等概率生成1到a的。比如,

 
1
2
Rand5() + Rand5() - 1
 

上述代码可以生成1到9的数,但它们是等概率生成的吗?不是。生成1只有一种组合: 两个Rand5()都生成1时:(1, 1);而生成2有两种:(1, 2)和(2, 1);生成6更多。 它们的生成是不等概率的。那要怎样找到一个等概率生成数的组合呢?

我们先给出一个组合,再来进行分析。组合如下:

 
1
2
5 * (Rand5() - 1) + Rand5()
 

Rand5产生1到5的数,减1就产生0到4的数,乘以5后可以产生的数是:0,5,10,15,20。 再加上第二个Rand5()产生的1,2,3,4,5。我们可以得到1到25, 而且每个数都只由一种组合得到,即上述代码可以等概率地生成1到25。OK, 到这基本上也就解决了。

套用上面的模板,我们可以得到如下代码:

 
1
2
3
4
5
6
int Rand7(){
    int x = ~(1<<31); // max int
    while(x > 7)
        x = 5 * (Rand5() - 1) + Rand5() // Rand25
    return x;
}

上面的代码有什么问题呢?可能while循环要进行很多次才能返回。 因为Rand25会产生1到25的数,而只有1到7时才跳出while循环, 生成大部分的数都舍弃掉了。这样的实现明显不好。我们应该让舍弃的数尽量少, 于是我们可以修改while中的判断条件,让x与最接近25且小于25的7的倍数相比。 于是判断条件可改为x > 21,于是x的取值就是1到21。 我们再通过取模运算把它映射到1-7即可。代码如下:

 
1
2
3
4
5
6
int Rand7(){
    int x = ~(1<<31); // max int
    while(x > 21)
        x = 5 * (Rand5() - 1) + Rand5() // Rand25
    return x%7 + 1;
}

这个实现就比上面的实现要好,并且可以保证等概率生成1到7的数。

让我们把这个问题泛化一下,从特殊到一般。现在我给你两个生成随机数的函数Randa, Randb。Randa和Randb分别产生1到a的随机数和1到b的随机数,a,b不相等 (相等就没必要做转换了)。现在让你用Randa实现Randb。

通过上文分析,我们可以得到步骤如下:

    1. 如果a > b,进入步骤2;否则构造Randa2 = a * (Randa - 1) + Randa, 表示生成1到a2 随机数的函数。如果a2 仍小于b,继教构造 Randa3 = a * (Randa2 - 1) + Randa…直到ak > b,这时我们得到Randak , 我们记为RandA。
    2. 步骤1中我们得到了RandA(可能是Randa或Randak ),其中A > b, 我们用下述代码构造Randb:
       
      1
      2
      3
      4
      5
      6
      7
      // A > b
      int Randb(){
          int x = ~(1<<31); // max int
          while(x > b*(A/b)) // b*(A/b)表示最接近A且小于A的b的倍数
              x = RandA();
          return x%b + 1;
      }

      从上面一系列的分析可以发现,如果给你两个生成随机数的函数Randa和Randb, 你可以通过以下方式轻松构造Randab,生成1到a*b的随机数。

       
      1
      2
      3
      Randab = b * (Randa - 1) + Randb
      Randab = a * (Randb - 1) + Randa
       

      如果再一般化一下,我们还可以把问题变成:给你一个随机生成a到b的函数, 用它去实现一个随机生成c到d的函数。有兴趣的同学可以思考一下,这里不再讨论。

给定1-a的随机数生成器,产生1-b的随机数生成器的更多相关文章

  1. Python_迭代器、生成器、列表推导式,生成器表达式

    1.迭代器 (1)可迭代对象 s1 = ' for i in s1: print(i) 可迭代对象 示例结果: D:\Python36\python.exe "E:/Python/课堂视频/ ...

  2. 【学习笔记】--- 老男孩学Python,day13 生成器,生成器函数,各种推倒式和生成器表达式

    1. 生成器和生成器函数 生成器的本质就是迭代器 生成器的三种创建办法: 1.通过生成器函数 2.通过生成器表达式创建生成器 3.通过数据转换   2. 生成器函数: 函数中包含了yield的就是生成 ...

  3. Python进阶(四)----生成器、列表推导式、生成器推导式、匿名函数和内置函数

    Python进阶(四)----生成器.列表推导式.生成器推导式.匿名函数和内置函数 一丶生成器 本质: ​ 就是迭代器 生成器产生的方式: ​ 1.生成器函数

  4. 【转载】Javascript使用Math.random()随机数函数生成1至1000的随机数

    在Javascript代码编写过程中,有时候我们需要使用Js来生成随机数,清除ajax的get请求缓存的时候我们会带上一个随机数来解决此问题,此外在其他应用中也可能使用到随机数,在Javascript ...

  5. Python基础之函数:6、异常相关和生成器对象、yield用法、生成器表达式

    目录 一.异常常见类型 1.类型错误 2.缩进错误 3.索引错误 4.语法错误 5.属性错误 6.key键错误 二.异常处理语法结构 1.基本语法结构 2.查看错误类型 3.针对不同类型所作措施 4. ...

  6. np.random.rand均匀分布随机数和np.random.randn正态分布随机数函数使用方法

    np.random.rand用法 觉得有用的话,欢迎一起讨论相互学习~Follow Me 生成特定形状下[0,1)下的均匀分布随机数 np.random.rand(a1,a2,a3...)生成形状为( ...

  7. 2018.11.06 生成器函数进阶&列表推导式&生成器表达式

    1.生成器函数进阶 2.列表推导式 3.生成器表达式

  8. 【原创】开源Math.NET基础数学类库使用(13)C#实现其他随机数生成器

                   本博客所有文章分类的总目录:[总目录]本博客博文总目录-实时更新  开源Math.NET基础数学类库使用总目录:[目录]开源Math.NET基础数学类库使用总目录 前言 ...

  9. 解密随机数生成器(二)——从java源码看线性同余算法

    Random Java中的Random类生成的是伪随机数,使用的是48-bit的种子,然后调用一个linear congruential formula线性同余方程(Donald Knuth的编程艺术 ...

随机推荐

  1. C++ 类中的const关键字

    //类中的const关键字 #include<iostream> using namespace std; class Point{ public: //这个const关键字本质上修饰的是 ...

  2. clone命令

    git clone <repository> <directory> git clone https://nulab.backlog.jp/git/BLG/tutorial.g ...

  3. 三角剖分算法(delaunay)

    开篇 在做一个Low Poly的课题,而这种低多边形的成像效果在现在设计中越来越被喜欢,其中的低多边形都是由三角形组成的. 而如何自动生成这些看起来很特殊的三角形,就是本章要讨论的内容. 项目地址:  ...

  4. 使用cordova+Ionic+AngularJs进行Hybird App开发的环境搭建手冊

    一.所需工具 1,JDK:生成 2.安卓SDK开发环境 3,NodeJs:主要使用的还是npm 4,Python开发环境 5.VS 2012(2008,2015也能够,已亲測):安装这个主要是须要一些 ...

  5. jquery书写一个简易的二级联动

    先用php生成一个json数组示例如下 JSON_UNESCAPED_UNICODE 是对汉字进行处理的参数 然后HTML代码如下 把那个json_city赋值成我们用php生成的json即可 < ...

  6. Spring中bean的作用范围

    singleton作用域: Spring的scope的默认值是singleton Spring 只会为每一个bean创建一个实例,并保持bean的引用. <bean id="bean的 ...

  7. docker使用阿里云镜像仓库

    1:阿里云docker仓库 https://dev.aliyun.com/search.html 2:进去注册帐号后,点击自己的管理中心. 3:在管理中心点击加速器,右边面板会有你的加速地址,右边面板 ...

  8. dreamweaver中Dw设置svn进行版本控制

    需要工具: VisualSVN dwcs5+ 点击查看教程

  9. 【Python之路】Python目录

    Python基础1 -- Python由来.Python种类.编码方式, Python基础2 -- Python运算符.数据类型.enumerate.range.for循环 python基础3 -- ...

  10. ERR_PTR,PTR_ERR还有IS_ERR函数详解

    内核中的函数常常返回指针,问题是如果出错,也希望能够通过返回的指针体现出来. 总体来说,如果内核返回一个指针,那么有三种情况:合法指针,NULL指针和非法指针. 1)合法指针:内核返回的指针一般是指向 ...