标准库rand()函数的缺陷以及Blitz++随机数生成的简介

当我们需要在某个任务中使用随机数,通常我们习惯于使用标准库的rand函数。像这样:srand(time(0)); // 时间种子

        rand() % MAX_RAND ;

标准库的rand函数使用线性同余算法,是生成速度相当快的一种随机数生成算法。在多数情况下也确实能满足我们的要求,但是对于一些特殊目的应用这个算法生成的随机数是不行的,比如某些加密算法,蒙特卡罗积分等(在.NET中创建随机密码的加密安全随机数就不能使用Random类的线性同余随机数,而要使用System.Security.Cryptography命名空间中的相关随机数生成类)。

这个线性同余算法的实现可以在很多书籍中找到。下面我给出一个The C Programming Langurage 2nd中的一个实现,这也是普遍使用的标准库随机数算法的实现:


   unsigned long int next =1;

 

   /* rand:  return pseudo-random integer on 0..32767 */

    int rand(void)

   {

       next = next *1103515245+12345;

       return ( unsigned int )(next/65536)%32768;

   }

 

   /* srand:  set seed for rand() */

   void srand(unsignedint seed)

   {

       next = seed;

   }

 

这个实现的问题在于rand函数return行中的那个32768,在标准库中这个数字定义为RAND_MAX宏,在VisualC++和Mingw32编译器的stdlib.h头文件(或者cstdlib)中你都可以发现RAND_MAX的值为32768。也就是说这个算法的随机数分布在0--RAND_MAX中,而在一般编译器中就是0--32768。假设你的算法需要的是300000多个的随机数,那么使用rand函数会产生重负次数近30次的随机数!

所以在这里我将简单介绍一下如何使用Blitz++库中的随机数生成类。不过在这里我不能够证明Blitz++随机数算法相对于标准库有什么优越性。Blitz++的源代码是开放的,你可以完全了解它的随机数算法的实现。

在Blitz++中随机数类组织在ranlib namespace中,使用方法也非常简单,seed成员函数种入种子后,再用random成员函数就可以了。Blitz++中包括了产生各种概率分布情况的随机数,列举如下:均匀分布(Uniform),正态分布(Normal),指数分布(Exponential),Beta分布,Gamma分布,Χ方分布,F分布,离散均匀分布。具体地可以参考Blitz++文档的第九章。本文将会演示正态分布的随机数类。

				NormalUnit<>()  
				标准正态分布,
				μ
				 = 0,
				σ
				 = 1;
				 
		
				Normal<>(T mean, T standardDeviation)
				正态分布,N(
				μ
				,
				σ
				^2
				)
				,其中mean就是
				μ
				,standardDeviation
				就是
				σ
				。
		

Blitz++ 中随机数类的seed是共享的,调用一个类的seed后,其他类也同样种入了相同的种子。对于种子的来源,Blitz++文档中有这样一个注意事项:

Note: you may be tempted to seed the random number generator from a static initializer. Don't do it! Due to an oddity of C++, there is no guarantee on the order of static initialization when templates are involved. Hence, you may seed the RNG before its constructor is invoked, in which case your program will crash. If you don't know what a static initializer is, don't worry -- you're safe!

(幸运的是我也不太清楚static initializer具体指什么,:-) )

1 ,先来看一个标准正态分布的例子,根据3 σ 法则(正态分布的随机数几乎全部落在( μ -3 σ , μ +3 σ ) 中),这些随机数应该大部分落在(-3, 3)之间。

下面的程序产生10000个正态分布随机数,然后导入Matlab生成图形,横坐标是随机数的序数,纵坐标是随机数值。很显然,大部分点在3 σ区间内。在区间 ( μ - σ , μ + σ)中数据点最密。


#include <random/uniform.h>

#include <random/normal.h>

#include <iostream>

#include <fstream>

#include <ctime>

#include <cstdlib>

using namespace std;

using namespace ranlib;

 

int main()

{

    const size_t MAXGEN =10000;

    NormalUnit<float> rnd;

 

    rnd.seed(time(0));

 

    ofstream file("c:\\file.txt");

 

    // generator Normal distribution random number

    for (size_t i=0; i<MAXGEN;++i)

    {

        file << rnd.random()<<" ";

    }

}

2 , 下面是一个服从于 μ= 10,σ= 2的正态分布,根据3σ法则,大部分点应该落在(4,16)之间。


#include <random/uniform.h>

#include <random/normal.h>

#include <iostream>

#include <fstream>

#include <ctime>

#include <cstdlib>

using namespace std;

using namespace ranlib;

 

int main()

{

    const size_t MAXGEN =1000000;

    const int mu =10;

    const int sigma =2;

    Normal < float > rnd(mu,sigma);

 

    rnd.seed(time(0));

 

    ofstream file("c:\\file.txt");

 

    // generator Normal distribution random number

    for (size_t i=0; i<MAXGEN;++i)

    {

        file << rnd.random()<<" ";

    }

}

 

3,生成前述正态分布的钟型曲线(PDF)。

这里产生1M的float随机数,然后乘以10转型为整数,统计在区间0-170之间随机数出现的概率,这样再导入Matlab生成图形就是一个近似的正态分布的钟型曲线了。

 

 


#include <random/uniform.h>

#include <random/normal.h>

#include <iostream>

#include <fstream>

#include <ctime>

#include <cstdlib>

using namespace std;

using namespace ranlib;

 

int main()

{

    const size_t MAXGEN =1000000;

    const int mu =10;

    const int sigma =2;

    Normal < float > rnd(mu,sigma);

 

    rnd.seed(time(0));

 

    ofstream file("c:\\file.txt");

 

    float *rndArray =newfloat[MAXGEN];

    // generator Normal distribution random number

    for (size_t i=0; i<MAXGEN;++i)

    {

        rndArray[i]= rnd.random();

    }

 

    int *rndConvertIntArray =newint[MAXGEN];

    int multiple =10;

    // convert float random number to integer

    for (size_t i=0; i<MAXGEN;++i)

    {

        rndConvertIntArray[i]=int(rndArray[i]* multiple);

    }

 

    const size_t PDFSIZE =(mu +3* sigma)* multiple;

    const size_t redundancy =10;

    int *pdf =newint[PDFSIZE+redundancy];

    for (size_t i=0; i<PDFSIZE+redundancy;++i)

    {

        pdf[i]=0;

    }

 

    // generator PDF(Probability Distribution Function), Normal distribution is a "bell" shape

    for (size_t i=0; i<MAXGEN;++i)

    {

         if (rndConvertIntArray[i]<= PDFSIZE+redundancy-1)

        {

            ++pdf[rndConvertIntArray[i]];

        }

    }

 

    for (size_t i=0; i<PDFSIZE+redundancy;++i)

    {

        file << pdf[i]  <<" ";

    }

 

    delete [] rndArray;

    delete [] rndConvertIntArray;

    delete [] pdf;

}


 

钟型曲线,当然不是特别光滑 ( J ), 大部分随机数都出现在( 4 , 16 )区间内。

 

总结, Blitz++ 的随机数生成类应该说是值得信任的。

时间: 2024-10-02 18:20:58

标准库rand()函数的缺陷以及Blitz++随机数生成的简介的相关文章

2信号处理之:信号产生原因,进程处理信号行为,信号集处理函数,PCB的信号集,sigprocmask()和sigpending(),信号捕捉设定,sigaction,C标准库信号处理函数,可重入函数,

 1信号产生原因 2.进程处理信号行为 manpage里信号3中处理方式: SIG_IGN SIG_DFL                                            默认Term动作 a signal handling function 进程处理信号 A默认处理动作 term   中断 core    core(调试的时候产生) gcc –g file.c     ulimit –c 1024     gdb a.out core ign      忽略 stop

PHP SPL标准库中的常用函数介绍

  这篇文章主要介绍了PHP SPL标准库中的常用函数介绍,本文着重讲解了spl_autoload_extensions().spl_autoload_register().spl_autoload()三个函数,需要的朋友可以参考下 PHP SPL标准库中提供了一些函数用来处理如自动加载.迭代器处理等. spl_autoload_extensions()添加spl_autoload()可加载的文件扩展名 spl_autoload_register()注册函数到SPL __autoload函数栈中

c++标准库-C++标准库中,set容器的insert函数中的比较函数重写问题

问题描述 C++标准库中,set容器的insert函数中的比较函数重写问题 在**set **容器里我把它的其中的元素定义为map,然后我就不会写compare函数了.因此他的insert函数就跪了--求大神助--哭-- 解决方案 #include #include using std::string; #include using std::map; #include using std::set; #include using std::make_pair; #include using s

PHP SPL标准库中的常用函数介绍_php技巧

PHP SPL标准库中提供了一些函数用来处理如自动加载.迭代器处理等. spl_autoload_extensions()添加spl_autoload()可加载的文件扩展名 spl_autoload_register()注册函数到SPL __autoload函数栈中. 复制代码 代码如下: /*test1.php*/ <?php class Test1 { }   /*test2.lib.php*/ <?php class Test2 { }   /*test.php*/ <?php /

C++著名类库和C++标准库介绍

C++著名类库 1.C++各大有名库的介绍--C++标准库 2.C++各大有名库的介绍--准标准库Boost 3.C++各大有名库的介绍--GUI 4.C++各大有名库的介绍--网络通信 5.C++各大有名库的介绍--XML 6.C++各大有名库的介绍--科学计算 7.C++各大有名库的介绍--游戏开发 8.C++各大有名库的介绍--线程 9.C++各大有名库的介绍--序列化 10.C++各大有名库的介绍--字符串 11.C++各大有名库的介绍--综合 12.C++各大有名库的介绍--其他库 1

一文读懂Go语言的net/http标准库

点击标题下「异步图书」可快速关注 在进行Web应用开发的时候,使用成熟并且复杂的Web应用框架通常会使开发变得更加迅速和简便,但这也意味着开发者必须接受框架自身的一套约定和模式.虽然很多框架都认为自己提供的约定和模式是最佳实践(best practice),但是如果开发者没有正确地理解这些最佳实践,那么对最佳实践的应用就可能会发展为货物崇拜编程(cargo cult programming):开发者如果不了解这些约定和模式的用法,就可能会在不必要甚至有害的情况下盲目地使用它们. 货物崇拜编程 第

C标准库参考指南(1)assert.h

1.1 assert.h 断言头文件用于调试. 宏: assert(); 外部引用: NDEBUG 1.1. assert 声明: void assert(intexpression); 断言头文件中的宏允许你将一些特殊信息写入到标准错误文件. 如果表达式的值为0(false),那么表达式.源文件名和行号都会被发送给标准错误输出,并调用abort函数.如果标识符NDEBUG ("no debug")由#define NDEBUG定义,那么断言头文件中的宏就什么都不做. 标准错误输出的格

Java SE 8:标准库增强

Lambda表达式是Java SE 8的核心功能,大部分的改进都围绕lambda表达式展开.(Jigsaw项目已经被推迟到Java SE 9.)关于lambda表达式的内容,已经在上一篇文章中进行了说明.这篇文章主要介绍Java SE 8中包含的其他Java标准库的增强. 并行排序 随着多核CPU的流行,Java平台的标准库实现也尽可能利用底层硬件平台的能力来提高性能.Java SE 7中引入了Fork/Join框架作为一个轻量级的并行任务执行引擎.Java SE 8把Fork/Join框架用到

为C++标准库容器写自己的内存分配程序

根据sgi 的STL源码的二级分配算法改写的内存池分配程序,只要稍微修改就可以实现共享内存方式管理,使用C++标准库容器中的map,set,multimap,multiset测试通过,vector测试通不过,原因是在内存回收的时候考虑的比较简单,vector每次分配内存个数不固定,回收也不固定,这样的话,程序还需要继续完善. 内存池管理程序源码如下: #ifndef MY_ALLOCATOR_H_#define MY_ALLOCATOR_H_#include "stdafx.h"#in