随机化



0) 引论

随机是很有用的一个东西,先不去管什么随机化算法,至少随机数是个很好的东西,就像掷骰子,总可以帮组我们决定一些犹豫不决的并且无关紧要的事。在机器学习中,一般我们都是要在整个数据集中随机抽取一定的数据做训练,另外一些做测试,这样结果才能有说服力,这里也将用到了随机数。因此下面我们首先来讲解一下伪随机数发生器。


1) 伪随机数发生器

真正意义上的随机数是很难产生的,大多数的随机数都是伪随机数,伪随机数是可以计算出来的,并且它有一个周期。但是由于伪随机数拥有随机数的大部分统计性质,因此对于一般的应用,伪随机数就可以用于解决问题了,当然密码学除外,这个是要真的随机数的。

伪随机数发生器的原理是:

,其中M为质数,并且一般取1<=x0<M。

举个例子,如果M=11,A=7,x0=1,那么产生的伪随机数为:7,5,2,3,10,4,6,9,8,1,7,5,.......

这个随机数列的周期为10.

当然如果用上面的数计算随机数列的话,效果相当不好,因为周期太小,得不到几个随机数,因此我们要选择更好的数据,一般来说M要大,这样才能得到更长的周期。同时也要注意A的选取,因为A也会影响周期。

一般来说我们采用M=(2^31)-1 = 2147483647,这个是一个31位的质数,A=48271,这个A能使M得到一个完全周期

  1. static unsigned long Seed = 1;  
  2. define A 48271L  
  3. define M 2147483647  
  4.   
  5. double Random(void)  
  6. {  
  7.     Seed = (A*Seed)%M;  
  8.     return (double) Seed/M;  
  9. }  
static unsigned long Seed = 1;
define A 48271L
define M 2147483647

double Random(void)
{
	Seed = (A*Seed)%M;
	return (double) Seed/M;
}
上面的代码产生的是(0,1)之间的随机数。这段代码由于乘法可能溢出,因此这个只具有理论意义,实际编程需要做一些改进


上面的算法产生的随机数还是有一个问题的,它无法产生重复的随机数。例如5,5,3,7,1这样的数列。

还有很多的随机数的算法利用的是下面的公式:

,这里C为奇数,同时如果数据选择不好的话,很有可能得到周期很短的随机数,例如

,如果我们去Seed=179424105的话,那么随机数的周期为1,也就失去了随机的意义。



在我们的生活中,人们经常会去掷色子来看结果,投硬币来决定行动,这就牵涉到一个问题:随机。
计算机为我们提供好了随机方法(部分计算器也提供了),那么对于有些具有瑕疵的算法,如果配上随机化算法的话,又是可以得到意想不到的结果。

定义

这种算法看上去是凭着运气做事,其实,随机化算法是有一定的理论基础的,我们可以想象,在[1,10000]这个闭区间里,随机1000次,随机到2这个数的几率是多大(约为0.001),何况1000次的随机在计算机程序中仅仅是一眨眼的功夫。可以看出,随机化算法有着广阔的前景。只是由于随机化算法比较难于掌控,所以并不是很多人都接触过他,但肯定有很多人都听说过。

举例

例一

下面,我们就随机化问题,举一个例子:
一个长度在4..10的字符串中,需要判定是否可以在字符串中删去若干字符,使得改变后字符串符合以下条件之一:
(1)AAAA;(2)AABB;(3)ABAB;(4)ABBA。
例如:长度为6字符串“POPKDK”,若删除其中的“O”,“D”两个字母,则原串变为:“PPKK”,符合条件(2)AABB。
分析:
这道题很容易想到一种算法:运用 排列组合:枚举每4个字母,然后逐一判断。算法是可行的,但是如果需要题目中加上一句话:需要判断n个字符串,且n<=100000,那么这样的耗时是不能让人忍受 的,因为在枚举的过程中,是非常浪费时间的。
①:这里是指信息学中要求算法的普遍运算时间为:1000ms)
所以这道题 有可能可以借助于随机化算法,下面我们来算一下在10个字符中取4个字符一共有多少种取法:C(4,10)=210。那么很容易得知,随机化算法如果随机300次,能得到的结果基本上就正确了(概率为1-(209/210)^300,约为0.76),而随机时的时间消耗是O(1),只需要判断没有随机重复即可,判重的时间复杂度也为O(1),并且最多随机300次,这样就可以有效地得到答案,最大运算次数为:O(300n),这是在计算机的承受范围内(1000ms)的。
从这里就能看出,随机化算法是一个很好的概率算法,但是它并不能保证正确,而且它单独使用的情况很少,大部分是与其他的算法:例如贪心、搜索等配合起来运用。

例二

排序问题。 快速排序是排序方法中较为便捷的方法之一,但是由于它极不稳定,最好的时候时间复杂度为O(n㏒n),这里的㏒是指以2为底的对数运算。最坏的时候能达到与普通排序方法一样的O(n^2)。
而制约快速排序的有两个:一是数据,越无序的数据,快排的速度越快;二是中间点的枚举。
因为两个制约条件都与随机有着不可分开的关系。
所以,在快速排序中加入随机化算法无疑是十分重要的。
运用在:
(1)数据读入时,随机排放数据位置。
(2)中间点的枚举进行多次随机化后决定。
这样就基本上将快速排序的时间复杂度维持在最好状态。

srand和 rand()配合使用产生伪 随机数序列。 rand函数在产生随机数前,需要系统提供的生成伪随机数序列的种子,rand根据这个种子的值产生一系列随机数。如果系统提供的种子没有变化,每次调用rand函数生成的伪随机数序列都是一样的。srand(unsigned seed)通过参数seed改变系统提供的种子值,从而可以使得每次调用rand函数生成的伪随机数序列不同,从而实现真正意义上的“随机”。通常可以利用系统时间来改变系统的种子值,即srand(time(NULL)),可以为rand函数提供不同的种子值,进而产生不同的随机数序列.
Linux下随机函数在windows系统的实现

srand48和drand48是Unix库函数,drand48的作用是产生[0,1]之间均匀分布的随机数,采用了线性同余法和48位整数运算来产生伪随机序列

image

其中

image

函数用上面的算法产生一个48位的伪随机整数,然后再取出此整数的高32位作为随机数,然后将这个32位的伪随机数规划到[0,1]之间,用函数srand48来初始化drand48(),其只对于48位整数的高32位进行初始化,而其低16位被设定为随机值。

  
 1: #define MNWZ 0x100000000 
 2: #define ANWZ 0x5DEECE66D
 3: #define CNWZ 0xB16
 4: #define INFINITY 0xFFFFFFFFF
 5: 
 6: int labelsize;
 7: int dim;
 8: 
 9: static unsigned long long seed = 1;
 10: 
 11: double drand48(void)
 12: {
 13: seed = (ANWZ * seed + CNWZ) & 0xFFFFFFFFFFFFLL;
 14: unsigned int x = seed >> 16;
 15: return ((double)x / (double)MNWZ);
 16: }
 17: 
 18: //static unsigned long long seed = 1; 
 19: 
 20: void srand48(unsigned int i)
 21: {
 22: seed = (((long long int)i) << 16) | rand();
 23: }



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值