伪随机与实验

本文通过实验探讨了计算机生成伪随机数的特性,发现不同Linux系统下C++随机数序列的一致性,揭示了循环周期并非传统认为的65536或2^32,并对比了Cygwin与真实Linux系统在随机数生成上的差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

众所周知,计算机产生的是伪随机数。所谓伪随机,就是:当知道种子和随机产生算法之后,就可以完全确定出随机数序列了。并且这个随机数序列是循环重复的。不同的随机产生算法的循环周期不同;好的随机产生算法的循环周期会很长。

有的文章提到,可以通过引入系统以外的变量来达到真随机的目的,比如“Unix 维护了一个熵池,不断收集非确定性的设备事件,即机器运行环境中产生的硬件噪音,来作为种子”。个人觉得,这本质上仍然是伪随机,只是利用硬件噪声生成种子而已,这和使用时间戳作为种子没有本质区别。只是别人可能没法知道种子是啥,因此也无法推测出随机序列。而该文中提到的 /dev/random (其实还有一个 /dev/urandom)是一个字符文件,如果读取它,会取得随机字符,而不是随机数字。当然我们可以想办法把随机字符转换成随机数字。关于如何使用 /dev/random 和 /dev/urandom, 可以参见这篇文章,本文不再赘述。

有的文章中提到,C++中的随机数序列的循环周期是65536或2^32. 本文接下来的部分将验证一下计算机产生的是否是伪随机,以及循环周期是多少。

先看代码:

#include <iostream> 
#include <cstdlib> 
#include <ctime>
using namespace std;

void gen_rand_int(unsigned int seed)
{
    // srand((unsigned)time(NULL));  
    srand(seed);  
    for(int i = 0; i < 10;i++ )  
        cout << rand() << ' ';  
    cout << endl;
}

void gen_2_rand_int(unsigned int seed)
{
    srand(seed);  
    long max_unsigned_int = 4294967295;
    int i_1, i_65536, i_65537, i_max_ui, i_max_ui_1; 
    long yi = 100000000;
    long tmp;
    for (long i=0; i<=max_unsigned_int; ++i) {
        tmp = rand();
        if (i == 0) 
            i_1 = tmp; 
        else if (i == 65535) 
            i_65536 = tmp;
        else if (i == 65536) 
            i_65537 == tmp;
        else if (i == max_unsigned_int) 
            i_max_ui = tmp; 
        
        if (i % yi == 0) 
            cout << i/yi << " Yi have been generated" << endl;
    }
    cout << "the first number is: " << i_1 << endl;
    cout << "the 65536" << " number is " << i_65536 << endl;
    cout << "the 65537" << " number is " << i_65537 << endl;
    cout << "the " << max_unsigned_int+1 << " number is " << i_max_ui << endl;
    cout << "the " << max_unsigned_int+2 << " number is " << rand() << endl;
}

int main() 
{  
    gen_rand_int(10);
    gen_rand_int(10);  // print the same as above
    gen_2_rand_int(10);
    
    return 0; 
}

然后,给出观察结论:

1. 在某一台机器上,由main函数中连续2次调用 gen_rand_int(10) 的打印结果完全一致。
   仍然在这一台机器上,多次运行该二进制可执行文件,gen_rand_int(10)的打印结果完全一致。
   由此可以看出,伪随机的含义就是,一旦知道了种子和算法,随机数序列就是确定的了。这一点在下面还可以继续看出。

2. 笔者将以上程序拿到5台机器上跑,在相同的种子下,结果很有趣:
 

 前10个数第65536个数第65537个数第2^32个数第2^32+1个数
A - Ubuntu 18.04ABCD相同1174608988每次都变18433189981902554484
B - Ubuntu 16.04ABCD相同1174608988每次都变18433189981902554484
C - CentOS 7.3ABCD相同1174608988018433189981902554484
D - RHEL 7.5ABCD相同1174608988018433189981902554484
E - Cygwin on Windows与ABCD不同与ABCD不同0与ABCD不同与ABCD不同

由上可以看出,

a. 一般来说,在给定种子时,在各Linux系统上C++程序取得的随机数序列基本都是一致的; 

b. 第65537个随机数似乎比较特别,Ubuntu每次跑都会是一个不同的值,似乎有点“随机”,而Red Hat系列的Linux此处总为0

c. Cygwin系统的随机数取得与真实Linux系统的随机数差别较大,可能是取随机数的库使用的是Windows上的库。

d. 没有看出来循环周期是多少,但基本可以确定不是65536或2^32;根据网上有些文章,可能采用的是一种较好的随机生成算法,其循环周期远大于2^32. 

(完)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值