【人工智能算法】算法基础之随机数生成

本文重点:

  • 伪随机数生成
  • 线性同余生成法(Linear Congruential Generator ,LCG)
  • 进位乘数法(Multiply With Carry,MWC)
  • 梅森旋转算法
  • 蒙特卡洛方法

伪随机数生成算法的概念

要理解伪随机数生成算法需要先了解几个重要概念

  • 种子(seed)

“种子”决定了你会得到什么样的随机数序列,同时也决定了内部状态的初始值。对于给定的种子,你总会得到相同的随机数序列;而另一方面,几乎每一个互不相同的种子都会生成一个不同的随机数序列。

  • 内部状态(internal state)

内部状态”由伪随机数生成算法用于生成随机数和下一个内部状态的多个变量构成。只要你知道了内部状态以及伪随机数生成器或是密码学安全伪随机数生成器的算法类型,你就可以预测下一个随机数了。

  • 周期(period)

每个随机数序列的长度就是“周期”。一旦超出周期范围,伪随机数生成器生成的值就会开始重复。正是因为伪随机数生成器生成的值按照固定的间隔或者说周期重复,我们才说伪随机数生成器是周期函数。
正弦函数

随机数分布类型

均匀分布

通常希望得到的随机数服从均匀分布,伪随机数生成器一般也确实可以产生(0,1)区间内具有等可能性的随机数。生成大量(0, 1)区间内的随机数的结果,如下图所示,服从均匀分布。
随机数的均匀分布
随机数在(0,1)范围内等可能地分布,这种现象被称作“均匀分布”或“均匀随机数”。这种分布使得在特定区间内取到任意值的可能性都相等。
我们可以将得到的随机数缩放到任何目标区间,缩放方法和归一化的方法有点类似。

rand(high,low)=rand()*(high-low)+low

正态分布

有一些编程语言会生成正态分布随机数的接口。
随机数的正态分布
出现概率最大的随机数集中在0附近,其中并没有明确的上限和下限。每个整数都表示一个标准差,随着标准差在正负两个方向上逐渐增大,这些随机数被取到的概率急剧下降,在区间(-4,4)之外几乎就取不到什么数了。正态分布随机数最常见的应用场景是通过增加一个微小的随机偏置来改变某个数字。

语言实现

  • C#
Uniform:Random.NextDouble
Normal:NA
  • C/C++
Uniform:rand
Normal:NA
  • Java
Uniform:Random.NextDouble
Normal:Random.nextGaussian
  • Python
Uniform:random.random
Normal:random.randn
  • R
Uniform:runif
Normal:rnorm

轮盘模拟法

这也是一种随机数方案,只不过这种技术与赌场中实际的轮盘略有相似之处而已。通常当你要在3个及以上类别中做选择时,就需要用到这一技术。

示例

假设你要创造一个在栅格中随机游走的机器人,并且这个机器人只具备下列3种行为能力:

  • 前进
  • 左转
  • 右转
    虽说机器人的行动是随机的,但你也许并不希望机器人采取这3种行动的可能是均等的,反而希望机器人按下面这个分配比例来行动:
  • 前进(占80%)
  • 左转(占10%)
  • 右转(占10%)
    这样一个随机数生成器很容易实现。首先必须将所有选项排序以便它们分别占据(0,1)区间的某个部分,然后利用服从均匀分布的伪随机数生成算法来生成(0,1)区间内的随机数。
    假设x是一个随机数,那么可以定义如下的行为:
if 0<x<0.8 then move forward //低于0.8的情况占80%
if 0.8<x<0.9 then move left  //0.80.9的情况占10%
if 0.9<x<1.0 then move right  //0.91.0的情况占10%

伪随机数生成算法

性能是选择伪随机数生成算法的另一个重要的考量因素。

1.线性同余生成法

线性同余生成法是历史最悠久同时也最为常用的伪随机数生成算法,并且还是C/C++、Java和C#等语言内置的伪随机数生成算法。
线性同余生成法不适用于对随机性有较高要求的应用场景;此外由于算法的序列相关性,线性同余生成法通常对蒙特卡洛方法也无甚大用(序列相关又称“自相关”,指的是变量随时间序列的变化对自身也会产生影响)。这就意味着线性同余生成法得到的随机数品质并不太好,也就不适用于密码学场景。
算法的实现要用到一个被限制在一个特定的周期内的线性函数线性同余方程

  • m 0<m 模数
  • a 0<a<m 乘法因子
  • c 0<=c <m 增量
  • X0 0<= X0<m 种子 或者说初始值
    其中“Xn”的值每生成一个随机数都会更新一次。对线性同余生成法而言,下一次随机数生成过程的“Xn”值属于内部状态。虽然这种方法得到的随机数都是整数,但只需将得到的随机数除以算法所能生成的最大整数,即可将随机数转换到(0,1)区间,而这个“算法所能生成的最大整数”又取决于m、a和c三者的值。
    m a c 三者的值对结果的随机性影响很大,大量研究已经找到了各种最优值

m = 2e31
a = 1103515245
c = 12345

进位乘法法

进位乘数伪随机数生成算法是由George Marsaglia创造的,其目的是生成周期很长的随机整数序列,其周期之长,最低约为260,最高可达22000000。该算法使用一个从2到数千内随机选取的数值作为初始种子,算法的主要优点在于它调用的是简单的计算机整数运算,可以以极快的速度产生随机数序列。
必须要定义一个变量r,用以描述进位乘数法中的“延迟”概念,并且必须提供r个值作为种子。
进位乘数法公式
a为乘法因子,b为模数,此外还有一个表示进位的变量c。
进位计算方式如下:
进位计算公式
变量n表示你当前所计算的数在序列中的序号,必须保证n不小于r,其原因在于n之前的x值会作为种子值,而我们共有r个x的初值作为种子。这个式子是需要向下取整。
进位乘数生成法所得结果的周期远长于线性同余生成法,并且其实现的执行速度还通常都特别快,相对于线性同余生成法来说更具竞争力。

梅森旋转算法

梅森旋转算法[插图]是由Makoto Matsumoto和Takuji Nishimura于1997年开发的伪随机数生成算法,可以以极快的速度生成高质量的伪随机整数。该算法的设计初衷是要解决已有算法的各种缺陷。
梅森旋转算法是一种常用的伪随机数生成算法,也是Ruby、Python和R语言的内置伪随机数生成算法,可以生成适用于蒙特卡洛方法的高质量随机数。虽说梅森旋转算法并非密码学意义上的安全随机数发生器,但其在运行速度和随机性上的优势依然使它在人工智能领域大受欢迎。
“梅森旋转算法”这个名字来自于“该算法的周期总是一个梅森素数”这么一个事实。
所谓“素数”,指的是只能被1和其本身整除的数,比如5就是一个素数。而梅森素数则是满足 M n = 2 n − 1 M_n=2^n-1 Mn=2n1的素数
比如 2 5 = 31 , 32 − 1 = 31 2^5=31 , 32-1=31 25=31,321=31 31是一个素数并且满足上式,所以31是一个梅森素数。

Box-Muller转换法

可以把均匀分布随机数转换为正态分布随机数的算法。因为并非所有编程语言都支持生成正态分布随机数。
X 1 , X 2 X_{1},X_{2} X1,X2 是在[0,1)上遵从均匀分布的随机数(生成[0,1)上遵从均匀分布的随机数通常使用的是梅森旋转算法),令

Y 1 = − 2 l n X 1 c o s ( 2 π X 2 )      Y 2 = − 2 l n X 2 c o s ( 2 π X 1 ) Y_{1}=\sqrt{-2lnX_{1}}cos(2\pi X_{2})\, \, \, \, Y_{2}=\sqrt{-2lnX_{2}}cos(2\pi X_{1}) Y1=2lnX1 cos(2πX2)Y2=2lnX2 cos(2πX1)
( Y 1 , Y 2 ) T (Y_{1},Y_{2})^{T} (Y1,Y2)T 必然遵守二元正态分布,即利用两个独立的遵从均匀分布的随机数得到两个独立的正态分布的随机数

#include <iostream>
#include <cmath>
#include <cstdlib>
 
using namespace std;
const double pi = 3.1415926897932384;
int main() {
	ios::sync_with_stdio(false);
	double x1, x2,y1,y2;
	int seed;//手动输入种子
	cout << "输入种子(任给整数)" << endl;
	cin >> seed;
	srand(seed);
	x1 = rand()% RAND_MAX /(double) RAND_MAX;
	x2= rand() % RAND_MAX / (double)RAND_MAX;
	cout << "产生的均匀分布的随机数种子为" << endl;
	cout << x1 <<" "<< x2 << endl;
	y1 = sqrt(-2 * log(x1))*cos(2 * pi*x2);
	y2 = sqrt(-2 * log(x2))*sin(2 * pi*x1);
	cout << "输出的正态分布的随机数为" << endl;
	cout << y1 << " " << y2 << endl;
	return 0;
}

用蒙特卡洛方法估算PI值

由于很多情况下计算实际值会耗费大量时间,因此蒙特卡洛方法的思想是通过随机采样来对实际值进行估算,并且可以得到良好的估算结果。
蒙特卡洛方法的一个简单示例是用蒙特卡洛方法估算PI值。图4-4是一个内切于正方形的圆。
正方形内切圆
我们在正方形和圆形中随机放置一些点,利用圆内点比上正方形内全部点的比例就可以计算出PI值。正方形的面积等于长和宽的乘积,由于正方形长宽相等,实际上正方形的面积就是“宽乘宽”,或者说是“边长的平方”。圆形的面积公式为“PI乘以半径的平方”,而该圆直径又等于正方形边长。
和π有关的公式

tries = 0
success = 0
for i in from 0 to 1000{
	//随机取点
	x = rand()
	y = rand()
	tries = tries +1
	if(x*x+y*y<=1){
		success++
	}
}
pi = 4*success / tries

由此可以看出,随机的点越多,PI的估计值就越精确。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值