C++11 随机数学习

        转载请注明出处:http://blog.csdn.net/luotuo44/article/details/33690179



        相对于C++ 11之前的随机数生成器来说,C++11的随机数生成器是复杂了很多。这是因为相对于之前的只需srand、rand这两函数即可获取随机数来说,C++11提供了太多的选择和东西。

 

随机数生成算法:

        随机数生成算法有很多,C++11之前的C/C++只用了一种。C++11则提供下面三种可供选择:

  • linear_congruential_engine线性同余法
  • mersenne_twister_engine梅森旋转法
  • substract_with_carry_engine滞后Fibonacci

        这三种算法,在C++11里面都是用模板的方式实现的。如果我们要使用这三个模板类的话,就必须自己实例化之。但这些实例化参数都是这些算法里面使用到的参数,如果不懂算法的原理的话,真的不知道需要用什么参数才能得到比较好的随机序列。所以我们这些卑微的码农是用不了这些模板类的。C++11标准也想到了这点,所以就帮我们预定义了一些随机数类,这些随机数类都是用比较好的参数实例化上面那三个模板类。注意:在C++11里面,把这些随机数生成器叫做引擎(engines)

       下图列出了一些实例化的随机数类:

        

       当然具体用了哪些参数,我们是不用管的,直接用就行了。

       在上图的左上角,还可以看到一个default_random_engine的类。它也是一个实例化的类。之所以不归入那三种算法,是因为它的实现是由编译器厂家决定的,有的可能用linear_congruential_engine实现,有的可能用mersenne_twister_engine实现。这种现象在C/C++中见多了。不过,对于其他的类,C++11是有明确规定用哪种算法和参数实现的。


       好了,说了这么多还是上一个例子吧。

#include<iostream>
#include<random>
 
usingstd::cout;
usingstd::endl;
usingstd::cin;
 
intmain()
{
    std::default_random_engine random;
 
    for(int i = 0; i < 20; ++i)
        cout<<random()<<' ';
    cout<<endl;
 
    return 0;
}
//gcc编译器需要加上 –std=c++11 选项。
       C++11中,随机数都是定义在random头文件中的。除了default_random_engine,其他的那些实例化随机数类的名字都是怪怪的,所以还是这个好用。从例子中可以看到,是通过operator ()函数来获取下一个随机数。

       对srand熟悉的码农们肯定发现,这里没有使用到随机数种子。其实这里使用了默认种子,默认种子的值可以通过这类的公共静态常量default_seed来获取。如果想为这个类设置自己的种子的话,那么可以通过在构造函数中传入一个参数。也可以在构造之后调用seed()成员函数设置种子。


产生均匀分布的随机数:

       上面例子产生的随机数会比较大,如果我们只想产生0到100的随机数。按照我们之前的做法是直接random()%100。这种做法是不好的。原因可以参见《Accelerated C++》的7.4.4节。

        C++11也知道这一点,这就使得C++11的随机数更加复杂了。


       我们平常说产生随机数,隐含是意思是产生均匀分布的随机数。学过概率论的同学都知道,除了均匀分布还有很多分布,比如正态分布、泊松分布等等。之前在网上看过网友怎么用rand()函数产生的随机数制作这些分布。现在这工作不用码农做了,C++11标准都提供了这些分布。

       C++11提供的均匀分布模板类为:uniform_int_distribution和uniform_real_distribution。前一个模板类名字中的int不是代表整型,而是表示整数。因为它是一个模板类,可以用int、long、short等整数类型来实例化。后一个表示浮点数模板类,可以用float和double来实例化。使用例子如下:

#include<iostream>
#include<random>
#include<time.h>
 
using std::cout;
using std::endl;
using std::cin;
 
 
int main()
{
    std::default_random_engine random(time(NULL));
    std::uniform_int_distribution<int> dis1(0, 100);
   std::uniform_real_distribution<double> dis2(0.0, 1.0);
 
    for(int i = 0; i < 10; ++i)
        cout<<dis1(random)<<' ';
    cout<<endl;
 
    for(int i = 0; i < 10; ++i)
        cout<<dis2(random)<<' ';
    cout<<endl;
 
    return 0;
}

        可以看到,在uniform_int_distribution的构造函数中,参数说明了随机数的范围。uniform_int_distribution的随机数的范围不是半开范围[  ),而是[  ],对于uniform_real_distribution却是半开范围[  )。也是就是说上面的例子中,能产生100,但不会产生1.0。不得不说,这颠覆了之前的认识。对于default_random_engine来说,其产生的随机数范围是在[min(), max()]之间,其中min()和max()为它的两个成员函数。同样,也是非半开范围。对于浮点数,如果真的是想产生[0.0, 1.0]范围的数,可以使用

#include<cmath>
#include<cfloat>
std::uniform_real_distribution<double> dis2(0, std::nextafter(1,DBL_MAX));

        如果uniform_int_distribution使用了无参构造函数,那么其随机数的范围是[0,numberic_limits<type>::max()],也就是0到对应实例化类型能表示的最大值。对于uniform_real_distribution的无参构造函数,则是[0, 1)。

 

概率分布类型:

        C++11提供的概率分布类型有下面这些:

 

均匀分布:

       uniform_int_distribution          整数均匀分布

       uniform_real_distribution        浮点数均匀分布

 

伯努利类型分布:(仅有yes/no两种结果,概率一个p,一个1-p)

       bernoulli_distribution    伯努利分布

       binomial_distribution     二项分布

       geometry_distribution    几何分布

       negative_biomial_distribution  负二项分布

 

Rate-based distributions: 

       poisson_distribution 泊松分布

       exponential_distribution指数分布

       gamma_distribution 伽马分布

        weibull_distribution 威布尔分布

       extreme_value_distribution 极值分布

 

正态分布相关:

       normal_distribution        正态分布

       chi_squared_distribution卡方分布

       cauchy_distribution       柯西分布

       fisher_f_distribution      费歇尔F分布

       student_t_distribution t分布

 

分段分布相关:

       discrete_distribution离散分布

       piecewise_constant_distribution分段常数分布

       piecewise_linear_distribution分段线性分布

 

        这些概率分布函数都是有参数的,在类的构造函数中把参数传进去即可。

        下面是一个泊松分布的例子

#include<iostream>
#include<random>
#include<time.h>
#include<iomanip>
 
intmain()
{
  const int nrolls = 10000; // number ofexperiments
  const int nstars = 100;   // maximum number of stars to distribute
 
  int parameter = 4;
 
  std::minstd_rand engine(time(NULL));
  std::poisson_distribution<int>distribution(parameter);
 
  int p[20]={};
 
  for (int i=0; i<nrolls; ++i)
  {
    int number = distribution(engine);
    if (number < 20)
        ++p[number];
  }
 
  std::cout << "poisson_distribution"<<parameter<< std::endl;
  for (int i=0; i < 20; ++i)
    std::cout<<std::setw(2)<< i<< ": " << std::string(p[i]*nstars/nrolls, '*') <<std::endl;
 
  return 0;
}

        某一个输出结果为:

        

 

        可能大家都忘了泊松分布了,看一下下面的图吧

            



真正的随机数:

        C++11还提供了一个random_device随机数类。它并不是由某一个数学算法得到的随机序列,而是通过读取文件,读什么文件看具体的实现(Linux可以通过读取/dev/random文件来获取)。文件的内容是随机的,因为文件内容是计算机系统的熵(熵指的是一个系统的混乱程度)。也是当前系统的环境噪声,系统噪音可以通过很多参数来评估,如内存的使用,文件的使用量,不同类型的进程数量等等。Linux的来自键盘计时、鼠标移动等。

         不过gcc好像并没有很好地实现这个类,我手里的Mingw4.9.0就不随机,每次运行都得到同样的序列。

 


        对于C++11的随机类的更多用法可以参考这里

 

 

        参考:《C++标准库 ——自学教程与参考手册》(第2版)

                   《C++ Primer》(第5版)

                     http://blog.csdn.net/akonlookie/article/details/8223525

                     http://stackoverflow.com/questions/19665818/best-way-to-generate-random-numbers-using-c11-random-library?rq=1

                     http://hipercomer.blog.51cto.com/4415661/857870

 

 

 

 

  • 36
    点赞
  • 122
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 要用C语言写深度学习架构,首先需要了解深度学习的基本原理和各种常用架构。其次,需要找到合适的C语言库,如Caffe、TensorFlow C API等,来帮助实现各种网络层和优化算法。最后,可以根据需求编写代码实现自己的深度学习架构。 ### 回答2: 深度学习架构是通过搭建多层神经网络来实现的。具体地,使用C语言编写深度学习架构主要包括以下几个步骤: 1. 定义神经网络结构:首先,需要定义神经网络的结构,包括输入层、隐藏层和输出层。通过定义每一层的神经元数量和激活函数等参数,来确定网络的整体结构。 2. 初始化权重和偏置值:在定义网络结构之后,需要对网络中的权重和偏置值进行初始化。可以使用随机数生成器来产生初始值,并根据网络尺寸进行相应的初始化。 3. 前向传播:通过前向传播,将输入数据从输入层经过每一层的计算,最终得到输出层的预测结果。在每一层中,需要执行矩阵相乘和激活函数的操作,将上一层的输出传递给下一层的输入,并重复进行计算直到输出层。 4. 损失函数计算:在前向传播的过程中,需要计算输出层的预测结果与实际标签之间的损失。选择适合任务的损失函数,例如均方误差或交叉熵等,来度量模型预测结果的准确程度。 5. 反向传播:反向传播是为了通过计算梯度来更新模型中的权重和偏置值,从而使得模型能够更好地拟合训练数据。通过使用链式法则,从输出层到输入层逐层计算每个参数的梯度,并根据梯度下降算法进行参数的更新。 6. 参数更新:根据反向传播计算得到的梯度,使用优化算法(如随机梯度下降)对网络中的参数进行更新。通过根据梯度的方向和大小来调整参数的数值,使得模型在训练数据上的损失尽可能地最小化。 7. 训练和测试:经过不断的迭代更新,训练过程中通过反复执行前向传播、损失计算和反向传播来提升模型性能。最后,使用测试数据对训练好的模型进行评估,得到模型在新数据上的预测效果。 以上是使用C语言编写深度学习架构的主要步骤,通过灵活调整神经网络的结构和参数,不断改进训练算法,可以提高深度学习模型的准确性和性能。 ### 回答3: 要用C语言写一个深度学习架构,首先需要理解深度学习的基本原理和架构。深度学习是一种人工神经网络的应用,通过多层的神经网络模型来实现对数据的学习和预测。 在C语言中,可以通过结构体和指针等基本数据类型来构建深度学习架构。 首先,需要定义一个神经网络的结构体,包含输入层、隐藏层和输出层等各个层的节点数量。可以通过动态内存分配来创建和初始化这些节点。 在每个节点中,需要定义激活函数,例如Sigmoid函数或ReLU函数来实现神经元的激活和输出计算。 接下来,需要定义权重矩阵和偏置向量等参数,并通过随机初始化来开始训练。 训练过程中,可以通过前向传播和反向传播算法来更新权重和偏置,以减小损失函数的值。 前向传播过程中,首先需要将输入层的数据传入隐藏层进行计算,然后在隐藏层中应用激活函数,再将结果传入输出层进行计算。最终得到的结果可以与真实标签进行对比来计算损失。 反向传播过程中,根据损失函数的值和梯度下降算法,可以更新权重和偏置的值,以减小损失函数的值。 在C语言中,可以通过矩阵运算和向量操作来实现矩阵乘法、激活函数的计算和梯度更新等功能。 最后,在训练完成后,可以使用经过训练得到的权重和偏置来进行预测或分类任务。 总的来说,使用C语言编写深度学习架构需要了解深度学习原理、矩阵运算和指针操作等知识,通过结构体和指针等数据类型来构建神经网络的各个层,通过矩阵运算、激活函数和梯度更新等操作来实现神经网络的训练和预测功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值