成都高新seo,宿迁seo公司,有哪些专门做创意门头的网站,女生学电子商务好吗你真的懂随机数#xff1f; Author : Jasper YangSchool : Bupt Q:为什么要写这篇文章#xff1f;A:因为我发现在最近的科学计算中#xff0c;常常遇到随机数#xff0c;所有的随机数都是基于0,1随机#xff0c;而这个0,1随机怎么实现呢#xff1f;下面我会娓娓道来~ 这篇… 你真的懂随机数 Author : Jasper YangSchool : 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_1f(seed),a_{n1}f(an)$那你可以行到一个序列$a_1,a_2,a_3...a_n$那么要制作一个伪随机函数rand只需要让它每调用一次就返回序列的下一个元素就行。 下面是两种常见的错误做法 C# code
for (int i0;in;i)
{var rnd new Random (s);//s是实先确定的一个数字Console.Write ({0},,rnd.Next());
} 这样每次使用Random都去申请了一个变量rnd然后才用这个变量去找随机数(rnd.Next())。这样其实就是在随机数的序列中总是在找第一个。这样下来第一个数肯定是固定的就不存在什么随机数了。 第二种情况更加常见。 C# code
for (int i0;in;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 i0;in;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: tsorgygmail.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 类的新实例./// /summarypublic Random(): this(Environment.TickCount) {}/// summary/// 使用指定的种子值初始化 System.Random 类的新实例./// /summary/// param nameSeed用来计算伪随机数序列起始值的数字。如果指定的是负数则使用其绝对值。/param/// exception crefSystem.OverflowExceptionSeed 为 System.Int32.MinValue在计算其绝对值时会导致溢出。/exceptionpublic 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 位带符号整数。/returnspublic virtual int Next() {return this.InternalSample();}/// summary/// 返回一个小于所指定最大值的非负随机数./// /summary/// param namemaxValue要生成的随机数的上界随机数不能取该上界值。maxValue 必须大于或等于零。/param/// returns大于或等于零且小于 maxValue 的 32 位带符号整数即返回的值范围包括零但不包括 maxValue。/returns/// exception crefSystem.ArgumentOutOfRangeExceptionmaxValue 小于零。/exceptionpublic 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 nameminValue返回的随机数的下界随机数可取该下界值。/param/// param namemaxValue返回的随机数的上界随机数不能取该上界值。maxValue 必须大于或等于 minValue。/param/// returns一个大于或等于 minValue 且小于 maxValue 的 32 位带符号整数即返回的值范围包括 minValue 但不包括 maxValue。如果minValue 等于 maxValue则返回 minValue。/returns/// exception crefSystem.ArgumentOutOfRangeExceptionminValue 大于 maxValue。/exceptionpublic 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 namebuffer包含随机数的字节数组。/param/// exception crefSystem.ArgumentNullExceptionbuffer 为 null。/exceptionpublic 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 的双精度浮点数字。/returnspublic virtual double NextDouble() {return this.Sample();}/// summary/// 返回一个介于 0.0 和 1.0 之间的随机数./// /summary/// returns大于或等于 0.0 而小于 1.0 的双精度浮点数字。/returnsprotected virtual double Sample() {return (this.InternalSample() * 4.6566128752457969E-10);}}
} 这里我要另外提到一个大家听到了很多次的东西 ------------ 线性同余法 这也是实现随机数的一种方式 线性同余方法LCG 它的递归公式 $$N_{j1} (A * N_j B) (mod M)$$ 其中A,B,M是产生器设定的常数。 LCG的周期最大为M但大部分情况都会少于M。要令LCG达到最大周期应符合以下条件 B,M互质M的所有质因子的积能整除A-1若M是4的倍数A-1也是A,B,$N_0$都比M小A,B是正整数最后生成的就是一个 $N_i$ 序列这个序列应该满足下面的几个条件。 这个函数应该是一个完整周期的产生函数。也就是说,这个函数应该在重复之前产生出0 到m之间的所有数产生的序列应该看起来是随机的这个函数应该用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翻译就可以了但是我有点懒不太想去看了就用了这篇博文的内容~ h3random.random/h3random.random()用于生成一个0到1的随机符点数: 0 n 1.0 h3random.uniform/h3 random.uniform的函数原型为random.uniform(a, b)用于生成一个指定范围内的随机符点数两个参数其中一个是上限一个是下限。如果ab则生成的随机数n: a n b。如果 $ab$ 则 b n a。 h3random.randint/h3 random.randint()的函数原型为random.randint(a, b)用于生成一个指定范围内的整数。其中参数a是下限参数b是上限生成的随机数n: a n b。 h3random.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) 等效。 h3random.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)) h3random.shuffle/h3 random.shuffle的函数原型为random.shuffle(x[, random])用于将一个列表中的元素打乱 h3random.sample/h3 random.sample的函数原型为random.sample(sequence, k)从指定序列中随机获取指定长度的片断。sample函数不会修改原有序列。 OK告一段落了~朋友们有没有觉得进步了一点点呢~ paper done 2017/05/13