C/C++编程:随机数总结

1060 篇文章 302 订阅

第一个例子

#include <random>
#include <iostream>
#include <algorithm>
#include <vector>
int main(){
    for (int i = 0; i < 10; ++i) {
        std::default_random_engine e;
        std::uniform_int_distribution<int> di(10, 20);
        for (int i = 0; i < 15; ++i) {
            std::cout <<di(e) <<" ";
        }
        std::cout <<"\n";

        std::uniform_real_distribution<double > dr(10, 20);
        for (int i = 0; i < 8; ++i) {
            std::cout <<dr(e) <<" ";
        }
        std::cout <<"\n";


  

        _sleep(1);
        std::cout <<"\n\r\n";
    }
}

在这里插入图片描述

产生随机数的方法是,将引擎e分别和两个分布di和dr组合起来:

  • 引擎作为随机性的源头,它们是functioon object,能够产生随机的无正负值,并均匀分布于一个预定义的最小和最大值之间
  • 分布表示以何种手法,将这些随机值转换为随机数,后者分布于一个由使用者给定的参数决定的区间内

C++标准库提供了很多引擎,上面的:

std::default_random_engine e;

表示“在效能、大小、质量等综合考量下—至少是个可被接受的引擎”,但究竟是哪一种引擎,由实现决定,不同的平台可能不同。

针对不同的随机值类型,C++标准库也提供了多种分布:线性、正态/高斯、指数等。上面例子是线性分布,只是一个用于整数、一个用于浮点数

 std::uniform_int_distribution<int> di(10, 20);
 std::uniform_real_distribution<double > dr

(1)std::uniform_int_distribution会产生整数:short、int、long、long long以及响应的unsigned类型。如果不指定则默认为int。

  • 构造函数:
    • 构造函数的第一个参数是:最小值,默认为0
    • 构造函数的第二个参数是:最大值,默认为numeric_limits< type>::max
    • 注意,这是个闭合区间。比如di(10, 20);,包括了10和20
  • 如何产生一个随机数:
    • 调用operator(),并将引擎作为实参传入,比如:di(e)
  • 注意,任何引擎的初始状态都有明确定义,并非随机的。因此,下面语句会输出相同的数值:
		std::uniform_int_distribution<int> di(10, 20);
        std::default_random_engine e1;
        std::default_random_engine e2;

        std::cout <<di(e1) <<" ";
        std::cout <<"\n";
         std::cout <<di(e2) <<" ";
        std::cout <<"\n";
  • 如果你需要一个不可预期的随机值,有两个方法:
    • (1)以随机形式设定生成器的状态,也就是处理传入一个“种子(seed)”给引擎构造函数,比如:
      在这里插入图片描述

    • (2)调用成员函数,使用seed修改某个生成器的资源

(2)std::uniform_real_distributionstd::uniform_int_distribution几乎一样。不同点在于:

  • 构造函数传入的最小和最大值是个半开区间,默认为[0.0, 1.0),可能的类型包括float、double(默认)、long double

我们还可以用随机数生成器来弄乱(shuffle)容器或者区间内的元素。std::shuffle利用一个均匀随机数生成器比如std::default_random_engine 来重排元素

       std::default_random_engine e;
        std::vector<int> v {1, 2, 3, 4, 5, 6, 7};
        std::shuffle(v.begin(), v.end(), e);
        for(int i = 0; i < v.size(); i++){
            std::cout << v[i]<<" ";
        }

在这里插入图片描述
你可以但是不应该传入一个临时引擎,原因是每次初始化一个引擎,其初始状态是相同的

#include <random>
#include <iostream>
#include <algorithm>
#include <vector>
int main(){
    for (int i = 0; i < 10; ++i) {
        std::vector<int> v {1, 2, 3, 4, 5, 6, 7};
        std::shuffle(v.begin(), v.end(),  std::default_random_engine());
        for(int i = 0; i < v.size(); i++){
            std::cout << v[i]<<" ";
        }

        _sleep(1);
        std::cout <<"\n\r\n";
    }
}
...

在这里插入图片描述

应该这样写:

#include <random>
#include <iostream>
#include <algorithm>
#include <vector>
int main(){
    std::default_random_engine e;
    for (int i = 0; i < 10; ++i) {
        std::vector<int> v {1, 2, 3, 4, 5, 6, 7};
        std::shuffle(v.begin(), v.end(), e);
        for(int i = 0; i < v.size(); i++){
            std::cout << v[i]<<" ";
        }

        _sleep(1);
        std::cout <<"\n\r\n";
    }
}

在这里插入图片描述

为什么不只用引擎产生的值作为随机数呢?如果区间不吻合,就 % n不就行了吗?

使用rand()%n计算随机数,失败原因:

  • 当商被当前n而且为小整数时,很多伪随机数生成器制造出来的余数并不很随机,比如rand()的连续执行结果非偶即奇
  • 如果n为大数,而且被生成的最大值并非均匀的可被n除尽,那么某些余数的出现率会远远高于其他

所以我们应该用引擎 + 分布来生成随机数

引擎

C++标准库提供了16个随机数引擎,可以使用它们来搭配某个分布来实现随机数或者重新洗牌

随机数引擎是一个带有状态的随机性源头,其状态决定了它将生成哪一个随机值序列,注意这不是随机数。每次以operator()调用它,就可以产出一个随机的无正负号值,并且内部状态会改变,使得可以此后再产出一个新随机值。

注意,通常“状态的转换”和“被生成值”被精确的指定,因此,在任何平台上,带有相同状态的同类型引擎将产生相同的随机值。唯一例外是default_random_engine,它依赖于实现,但是还是会产生可预期值。

如果你真的想要一个不可预期值,那么就必须随机设定生成器的状态,例如运用某些“你无法以代码加以影响”的行为,比如“两次鼠标点击之间的毫秒数”

在这里插入图片描述


使用cstdlib库

生成 [0, num) 范围的随机数

系统:linux

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>

//  Provide random number from 0..(num-1)
#define randof(num)  (int) ((float) (num) * random () / (RAND_MAX + 1.0))

int main()
{

    srandom ((unsigned) time (NULL));  	// 生成随机数种子,必要,否则每次程序的运行结果都是一样的
    for(int i = 0; i < 10; i++){
        printf("%d\n", randof (100000));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值