python学习笔记 --- 随机数进阶

你真的懂随机数?

Author : Jasper Yang
School : Bupt

Q:为什么要写这篇文章?
A:因为我发现在最近的科学计算中,常常遇到随机数,所有的随机数都是基于0,1随机,而这个0,1随机怎么实现呢?下面我会娓娓道来~

这篇文章不同于网路上的杂散的技术文,我是针对 random 这么一个论题展开调研最后将所有相关的知识进行整理叙述,希望每个人看完都可以得到小小的提升~

& 什么是随机数

随机数:数学上产生的都是伪随机数,真正的随机数使用物理方法产生的

随机数种子:随机数的产生是由算术规则产生的,在c++中,srand(seed)的随机数种子不同,rand()的随机数值就不同,倘若每次的随机数种子一样,则rand()的值就一样。所以要产生随机数,则srand(seed)的随机数种子必须也要随机的。在 python 中就是 random.seed()来设置种子。

下面我讲的随机数不仅仅讲随机数生成的原理,也会讲在python中以及在c++中怎么去实现,当然,大部分资料也都是网上找的,我只是做了一个整理汇总,并用自己的语言加以叙述。

& 随机数的原理

这里我看了一篇博客,由于这篇博客是那个博主转的,但是该博主并没有表明是从哪里转来的,我就不po出链接了,大家往下看~

有位朋友问那博主关于一段程序的错误。

C/C++ code
for (int i =0;i< n;++i)
{srand((unsigned)time( NULL )); int r = rand()%100;cout << r << ",";
}

这里很明显他是想输出一串小于100的随机的数列.可是运行结果输出的却是类似 97,97,97,97,....97,30,30,30,30,30,30,30,30,30,30,30,30,....,27,27,27,27,27,27,....的序列.很明显这样完全看不出有任何的随机性.这是由于他对C的rand函数不理解导致的错误用法.而这两天逛C#区我也同样看到了几个类似的错误用法(C和C#的rand从大体的原理上差不多).想想自己初学的时候类似的错误犯得也不少.所以自己下去查了写资料总结了在随机数使用上的一些错误的用法.希望能对初学者有所帮助。

现在各种语言中的随机数产生函数所产生的"随机数",实际上被称之为"伪随机数".可以将
整个随机数函数看做这样一个表达式:

$$A = R(s)$$

其中R是随机函数,s是种子.A是一个数列.即对于任意一个种子s,经过R的计算后,总有一个确定的数列A与之对应.而当在C#里调用var rnd = new Random (s)或在C里调用srand(s)实质上所做工作之一就是设定这个种子.而rnd.Next();或rand()只不过是在A上取下一个元素而已.当然实际的实现不可能事先计算一个数列A,所以rand()相当于由s计算出下一个数字s',然后将s'作为新的种子赋值给s,最后将s'作为结果返回。

往细了讲,就是这样。

如果约定:$a_1=f(seed),a_{n+1}=f(an)$
那你可以行到一个序列:$a_1,a_2,a_3...a_n$,那么要制作一个伪随机函数rand,只需要让它每调用一次就返回序列的下一个元素就行。

下面是两种常见的错误做法

C# code
for (int i=0;i<n;++i)
{var rnd = new Random (s);//s是实先确定的一个数字Console.Write ("{0},",rnd.Next());
}

这样,每次使用Random,都去申请了一个变量rnd,然后才用这个变量去找随机数(rnd.Next())。这样其实就是在随机数的序列中总是在找第一个。这样下来,第一个数肯定是固定的,就不存在什么随机数了。

第二种情况更加常见。

C# code
for (int i=0;i<n;++i)
{var rnd = new Random ();//用系统时间作为种子Console.Write ("{0},",rnd.Next());
}

之前的第一种情况使用了一个固定的常数s来做种子,这里选用了系统时间做种子,想要达到随机的效果,但是得到的结果往往就会是和博主那位朋友一样的结果97,97,97,97,....97,30,30,30,30,30,30,30,30,30,30,30,30,....,27,27,27,27,27,27,.... 。

这是因为Windows系统时钟的更新频率大概在10ms左右.而这个for循环的执行显然要快
得多.于是在一段执行时间内Environment.TickCount (Random的默认种子)或是C的time函数返回的都是同一个值.从而导致rnd.Next在一段时间内返回一个常数。

所以正确的做法应该是把种子移出循环之外。

C# code
var rnd = new Random ();//用系统时间作为种子
for (int i=0;i<n;++i)
{Console.Write ("{0},",rnd.Next());
}

各种库中是怎么实现随机数呢?
在 Linux 下实现的方式类似如下

static unsigned long next = 1;/* RAND_MAX assumed to be 32767 */
int myrand(void) {next = next * 1103515245 + 12345;return((unsigned)(next/65536) % 32768);
}void mysrand(unsigned seed) {next = seed;
}

myrand、mysrand分别对应rand和srand,但实际的rand实现会复杂一些。

下面是这位博主实现的方式,其实挺简单的,我们每个人都可以实现一种自己想要的随机数方式加到自己的私有库中~

** Copyright (c) 2008 Microsoft::Tsorgy.Utils, Reserved.* * Filename:    @(#)Random.cs* Create by:   TsOrgY* Email:       tsorgy@gmail.com* Date:        2008/12/27 15:01:40* * Classname:   Random* Description: 一种能够产生满足某些随机性统计要求的数字序列的设备.*              */
using System;
using System.Runtime.InteropServices;
namespace Tsorgy.Utils {/// <summary>/// 表示伪随机数生成器,一种能够产生满足某些随机性统计要求的数字序列的设备./// </summary>[Serializable][ComVisible(true)]public class Random {private int inext;private int inextp;private const int MBIG = 0x7fffffff;private const int MSEED = 0x9a4ec86;private const int MZ = 0;private int[] SeedArray;/// <summary>/// 使用与时间相关的默认种子值,初始化 Random 类的新实例./// </summary>public Random(): this(Environment.TickCount) {}/// <summary>/// 使用指定的种子值初始化 System.Random 类的新实例./// </summary>/// <param name="Seed">用来计算伪随机数序列起始值的数字。如果指定的是负数,则使用其绝对值。</param>/// <exception cref="System.OverflowException">Seed 为 System.Int32.MinValue,在计算其绝对值时会导致溢出。</exception>public Random(int Seed) {this.SeedArray = new int[0x38];int num2 = 0x9a4ec86 - Math.Abs(Seed);this.SeedArray[0x37] = num2;int num3 = 1;for (int i = 1; i < 0x37; i++) {int index = (0x15 * i) % 0x37;this.SeedArray[index] = num3;num3 = num2 - num3;if (num3 < 0) {num3 += 0x7fffffff;}num2 = this.SeedArray[index];}for (int j = 1; j < 5; j++) {for (int k = 1; k < 0x38; k++) {this.SeedArray[k] -= this.SeedArray[1 + ((k + 30) % 0x37)];if (this.SeedArray[k] < 0) {this.SeedArray[k] += 0x7fffffff;}}}this.inext = 0;this.inextp = 0x15;Seed = 1;}private double GetSampleForLargeRange() {int num = this.InternalSample();if ((((this.InternalSample() % 2) == 0) ? 1 : 0) != 0) {num = -num;}double num2 = num;num2 += 2147483646.0;return (num2 / 4294967293);}private int InternalSample() {int inext = this.inext;int inextp = this.inextp;if (++inext >= 0x38) {inext = 1;}if (++inextp >= 0x38) {inextp = 1;}int num = this.SeedArray[inext] - this.SeedArray[inextp];if (num < 0) {num += 0x7fffffff;}this.SeedArray[inext] = num;this.inext = inext;this.inextp = inextp;return num;}/// <summary>/// 返回非负随机数./// </summary>/// <returns>大于或等于零且小于 System.Int32.MaxValue 的 32 位带符号整数。</returns>public virtual int Next() {return this.InternalSample();}/// <summary>/// 返回一个小于所指定最大值的非负随机数./// </summary>/// <param name="maxValue">要生成的随机数的上界(随机数不能取该上界值)。maxValue 必须大于或等于零。</param>/// <returns>大于或等于零且小于 maxValue 的 32 位带符号整数,即:返回的值范围包括零但不包括 maxValue。</returns>/// <exception cref="System.ArgumentOutOfRangeException">maxValue 小于零。</exception>public virtual int Next(int maxValue) {if (maxValue < 0) {throw new ArgumentOutOfRangeException("maxValue", string.Format("'{0}' must be greater than zero.", maxValue));}return (int) (this.Sample() * maxValue);}/// <summary>/// 返回一个指定范围内的随机数./// </summary>/// <param name="minValue">返回的随机数的下界(随机数可取该下界值)。</param>/// <param name="maxValue">返回的随机数的上界(随机数不能取该上界值)。maxValue 必须大于或等于 minValue。</param>/// <returns>一个大于或等于 minValue 且小于 maxValue 的 32 位带符号整数,即:返回的值范围包括 minValue 但不包括 maxValue。如果minValue 等于 maxValue,则返回 minValue。</returns>/// <exception cref="System.ArgumentOutOfRangeException">minValue 大于 maxValue。</exception>public virtual int Next(int minValue, int maxValue) {if (minValue > maxValue) {throw new ArgumentOutOfRangeException("minValue", string.Format("'{0}' cannot be greater than {1}.", minValue, maxValue));}long num = maxValue - minValue;if (num <= 0x7fffffffL) {return (((int) (this.Sample() * num)) + minValue);}return (((int) ((long) (this.GetSampleForLargeRange() * num))) + minValue);}/// <summary>/// 用随机数填充指定字节数组的元素./// </summary>/// <param name="buffer">包含随机数的字节数组。</param>/// <exception cref="System.ArgumentNullException">buffer 为 null。</exception>public virtual void NextBytes(byte[] buffer) {if (buffer == null) {throw new ArgumentNullException("buffer");}for (int i = 0; i < buffer.Length; i++) {buffer[i] = (byte) (this.InternalSample() % 0x100);}}/// <summary>/// 返回一个介于 0.0 和 1.0 之间的随机数./// </summary>/// <returns>大于或等于 0.0 而小于 1.0 的双精度浮点数字。</returns>public virtual double NextDouble() {return this.Sample();}/// <summary>/// 返回一个介于 0.0 和 1.0 之间的随机数./// </summary>/// <returns>大于或等于 0.0 而小于 1.0 的双精度浮点数字。</returns>protected virtual double Sample() {return (this.InternalSample() * 4.6566128752457969E-10);}}
}

这里我要另外提到一个大家听到了很多次的东西 ------------> 线性同余法

这也是实现随机数的一种方式

线性同余方法(LCG)

它的递归公式:

$$N_{j+1} = (A * N_j +B) (mod M)$$

其中A,B,M是产生器设定的常数。

LCG的周期最大为M,但大部分情况都会少于M。要令LCG达到最大周期,应符合以下条件:

  1. B,M互质

  2. M的所有质因子的积能整除A-1

  3. 若M是4的倍数,A-1也是

  4. A,B,$N_0$都比M小

  5. A,B是正整数

最后生成的就是一个 <$N_i$> 序列,这个序列应该满足下面的几个条件。

  1. 这个函数应该是一个完整周期的产生函数。也就是说,这个函数应该在重复之前产生出0 到m之间的所有数

  2. 产生的序列应该看起来是随机的

  3. 这个函数应该用32bit 算术高效实现

实现

#include <stdio.h>  
#include <time.h>  
static unsigned long rand_seed;  
void mysrand (unsigned long int);  
void myrand ();  
int  
main (void)  
{  int i;  mysrand (time (NULL));  for (i = 0; i < 100; i++)  {  myrand ();  }  return 0;  
}  void  
mysrand (unsigned long seed)  
{  rand_seed = seed;  
}  void  
myrand ()  
{  rand_seed = (rand_seed * 16807L) % ((1 << 31) - 1);  printf ("%ld ", rand_seed);  
}  

可以看到,这个实现和上面提到的 linux 的实现很像,其实就是一样的。

& 随机数使用

因为最近用的c++和python特别的多(我觉得这两个语言是程序员们最需要掌握的两种语言,别的都是补充 ~:)),所以下面我就只讲这两种语言的实现方式。

c++

实例程序

#include "stdafx.h"
#include <time.h>
#include <stdlib.h>
int _tmain(int argc, _TCHAR* argv[])
{// 初始化随机数种子// time函数返回从1970年1月1日零时零分零秒到目前为止所经过的时间,单位为秒srand((int)time(NULL));int j;for (int i = 0; i < 10; i++) {j = (rand() * 10) / RAND_MAX + 1; // 生成1~10之间的随机数printf("j = %d \n", j);}unsigned start = (rand() * 1000)/ RAND_MAX + 15550; // 生成15550~16549之间的随机数printf("start = %d \n", start);start &= ~1; // 把start变为偶数,如果是奇数,则start变为start - 1的偶数printf("start = %d \n", start);getchar();return 0;
}

c++ 其实就是 srand 和 rand 两个函数。
上面的都只是生成的整数,如果需要浮点数什么的就需要自己再加以处理,而在python中提供了比较多的函数。

python

这块的内容是 Capricorn的实验室的整理。其实这块内容直接去官网的doc翻译就可以了,但是我有点懒,不太想去看了,就用了这篇博文的内容~

<h3>random.random</h3>
random.random()用于生成一个0到1的随机符点数: 0 <= n < 1.0

<h3>random.uniform</h3>

random.uniform的函数原型为:random.uniform(a, b),用于生成一个指定范围内的随机符点数,两个参数其中一个是上限,一个是下限。如果a>b,则生成的随机数n: a <= n <= b。如果 $a<b$, 则 b <= n <= a。

<h3>random.randint</h3>

random.randint()的函数原型为:random.randint(a, b),用于生成一个指定范围内的整数。其中参数a是下限,参数b是上限,生成的随机数n: a <= n <= b。

<h3>random.randrange</h3>

random.randrange 的函数原型为:random.randrange([start], stop[, step]),从指定范围内,按指定基数递增的集合中 获取一个随机数。如:random.randrange(10, 100, 2),结果相当于从[10, 12, 14, 16, ... 96, 98]序列中获取一个随机数。random.randrange(10, 100, 2)在结果上与 random.choice(range(10, 100, 2) 等效。

<h3>random.choice</h3>

random.choice从序列中获取一个随机元素。其函数原型为:random.choice(sequence)。参数sequence表示一个有序类型。这里要说明 一下:sequence在python不是一种特定的类型,而是泛指一系列的类型。list, tuple, 字符串都属于sequence。有关sequence可以查看python手册数据模型这一章。下面是使用choice的一些例子:

print random.choice("学习Python")   
print random.choice(["JGood", "is", "a", "handsome", "boy"])  
print random.choice(("Tuple", "List", "Dict"))  

<h3>random.shuffle</h3>

random.shuffle的函数原型为:random.shuffle(x[, random]),用于将一个列表中的元素打乱

<h3>random.sample</h3>

random.sample的函数原型为:random.sample(sequence, k),从指定序列中随机获取指定长度的片断。sample函数不会修改原有序列。

OK,告一段落了~,朋友们,有没有觉得进步了一点点呢~

paper done 2017/05/13

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/289022.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pythonweb服务器怎么让别人访问_Django配置让其他电脑访问网站

其实在Django 带有一个内建的轻量级 Web 服务器&#xff0c;可供站点开发过程中使用。我们提供这个服务器是为了让你快速开发站点&#xff0c;也就是说在准备发布产品之前&#xff0c;无需进行产品级 Web 服务器&#xff08;比如 Apache&#xff09;的配置工作。 但是实际开发中…

C#求一元二次方程的根经典案例程序

目 录 1. 界面设计 2. C#源程序 1. 界面设计 界面说明: 输入一元二次方程的二次项、一次项及常数项,这样的话对应的一元二次方程表达式就确定了,点击【计算】,即可计算出该一元二次方程的根。 2. C#源程序 using System; using System.Collections.Generic; using S…

微软是如何解决 PC 端程序多开问题的

前言在公众号上看到一篇文章《C#中解决PC端程序多开的问题》。该文作者是通过创建互斥锁Mutex实现的:bool mutexCreated; var mutex new Mutex(true, "MyApp", out mutexCreated); if (mutexCreated) {Application.Run(new Form1()); } else {MessageBox.Show("…

磁盘和文件系统管理一

主要掌握规划硬盘中的分区&#xff0c;创建文件系统&#xff0c;挂载卸载文件系统。学会添加并进行分区&#xff0c;学会创建并挂载文件系统。 检测并确认新硬盘 fdisk –l [磁盘设备] 规划磁盘中的分区fdisk [磁盘设备] 用途&#xff1a;在交互是的操作环境中管理磁盘分区 交…

NodeJS-queryString

无论是前端还是后端&#xff0c;经常出现的应用场景是URL中参数的处理。nodeJS的queryString模块提供了一些处理 query strings 的工具。本文将详细介绍nodeJS中的queryStringvar querystring require(querystring);/*{ unescapeBuffer: [Function],unescape: [Function: qsUn…

ASP.NET和ASP.NETCore多环境配置对比

前言多环境配置应该都很熟悉了&#xff0c;最为常见的便是Debug和Release&#xff0c;例如下图是新建的一个asp.net项目&#xff0c;配置文件展开共有三个文件组成据我所知&#xff0c;大多公司从来没编辑过Web.Debug.config和Web.Release.config&#xff0c;一个Web.config文件…

d类功放芯片_应用于无滤波级D类音频功放的新型死区时间控制系统

在音频功率放大领域&#xff0c;由于D类音频功率放大器是基于脉冲宽度调制技术的开关放大器[1-4]&#xff0c;用作放大的功率管几乎总是处于或者完全导通或者完全截止的状态&#xff0c;因此其功率损耗比传统的线性放大器小得多&#xff0c;这使得其效率非常高&#xff0c;符合…

【C语言简单说】三:整数变量和输出扩展(2)

尼玛。。。简直蛋了个翔。。。 没保存&#xff0c;&#xff0c;&#xff0c;&#xff0c; ( E___E ) 念昏了头 注&#xff1a;注意我们每一行代码后面的分号表示我们一句代码的结束&#xff0c;就像我们在写文字的时候的标点符号&#xff0c;一个句号表示一句话的结尾。 注…

UML 用例图

用例模型 用例模型用来记录系统的需求&#xff0c;它提供系统与用户及其他参与者的一种通信手段。 执行者 用例图显示了系统和系统外实体之间的交互。这些实体被引用为执行者。执行者代表角色&#xff0c;可以包括&#xff1a;用户&#xff0c;外部硬件和其他系统。执行者往往被…

MAUI初体验:爽

只是记录&#xff0c;只是Hello World体验&#xff0c;别期望太高。1. 前言经过几个小时折腾&#xff0c;Maui环境终于安装好了&#xff0c;先上Hello World截图&#xff1a;1.1 MAUI Windows上MAUI Windows1.2 MAUI Android上MAUI Android2. 今早看到一个群聊推送点击链接可以…

无法识别的属性“targetFramework”。请注意属性名称区分大写和小写。错误解决的方法...

“/CRM”应用程序中的server错误。 配置错误 说明: 在处理向该请求提供服务所需的配置文件时出错。请检查以下的特定错误具体信息并适当地改动配置文件。 分析器错误消息: 无法识别的属性“targetFramework”。请注意属性名称区分大写和小写。源错误: 行 24: 设…

windows之nslookup命令

1 问题 今天是特别傻逼,既然问了一个很愚蠢的问题,登录后台需要相关的ip,但是我只有域名,这么太突然来,我既然不知道用nslookup命令,好吧,先记录起来,希望下次不要犯这样的傻逼错误 2 查看电脑的所有配置 在windowd 终端输入如下命令 ifconfig/all 3 nslookup正…

动态轮播图

1 /// <reference path"jquery-1.10.2.min.js" />2 var i 0;3 var timer; //设置定时器4 $(function () {5 $("#dlunbo").hover(function () {6 $(".btn").show();7 }, function () {8 $(".btn").hide…

eos操作系统_EOS相机统一的用户界面

自EOS相机诞生起就未改变的基本布局精心设计的操作系统EOS数码单反相机从普及机型到高端机型的按钮布局都是共通的。快门按钮的位置自不必说&#xff0c;主拨盘位置和背面按钮的配置也基本相同。特点是在手柄一侧集中配置用于进行主要操作的按钮。实现了只用右手就能完成拍摄相…

【C语言简单说】三:变量总结ASCII码扩展(5)

前面几个小节都在说变量&#xff0c;那么这一节我们就来总结一下 int表示整数&#xff0c;float表示小数&#xff0c;char表示字符。他们所匹配的&#xff0c;整数&#xff1a;%d&#xff1b;浮点数&#xff1a;%f&#xff1b;字符&#xff1a;%c。 我们来看一个程序&#xf…

Windows之Fiddler抓HTTP和HTTPS请求

1 Fiddler 1) 介绍:Fiddler是抓包工具,原理是以web代理服务器的形式进行工作的,使用的代理地址是:127.0.0.1,端口默认为8888,我们也可以通过设置进行修改 2)下载地址:到Fiddler官网下载,直接百度 Fiddler官网 2 Fiddler抓HTTP的包 比如我们需要抓谷歌浏览器的http请…

java继承与覆盖_简单的继承,方法重载与方法覆盖

[java]代码库package com.jiarui;public class Demo1 {public static void main(String[] args) {Dog dog1 new Dog(2,"大黄");System.out.println(dog1.name"的年龄为&#xff1a;"dog1.getAge());dog1.Cry();Cat cat1new Cat(3,"小花");cat1.C…

【C语言简单说】四:常量

常量和变量是一种相对的概念&#xff0c;在这里我开始跟大家说说常量和变量的区别。可能前几节的小伙伴们并没有能感受出来&#xff0c;不过有了常量的对比那么将会有很好的理解。 .._|||||.. 头昏眼花 常量&#xff0c;就是一些固定的数据&#xff0c;也就是说你把数据装到…

虚幻4 碰撞过滤

原创翻译&#xff0c;转载请注明出处。&#xff08;http://blog.csdn.net/sinat_24229853/article/details/51090259&#xff09; 英文原文&#xff1a;https://www.unrealengine.com/blog/collision-filtering?langzh-CN 选择什么样的碰撞显然是很重要的。但它可能会很棘手&…

基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客?

前言OK&#xff0c;我也来造轮子了博客系统从一开始用WordPress&#xff0c;再到后来用hexo、hugo之类的静态博客生成放github托管&#xff0c;一直在折腾折腾是为了更好解决问题&#xff0c;最终还是打算自己花时间搞一个好了本系列文章将记录博客的开发过程~ 将会持续更新&am…