从图片看出伪随机数的周期性

今天尝试用代码生成了一张大小为1024*1024,每个像素的RGB值都是随机数的图片(以下图片经过了一定程度的压缩):

random1

本应该是完全随机的图案,但是在图片中却可以明显地看出许多竖线,这显然是伪随机数的周期性导致的。

由于图片的宽度是1024像素,所以我猜测其中的周期一定和1024这个数有关。我决定看一看随机数以1024为周期的统计特性。

我把每个颜色值的生成方法改为连续获取1024个随机数,算出总和,并对256取余数,得到了这幅图片:

random2

从这幅图片中可以明显地看出很强的周期性!

我再把统计的周期取大一些,例如32768,仅仅通过简单的计算就可以看出随机数分布的不均匀。写这样一段C语言程序:

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

int main(){
    unsigned int i,s,j;
    srand(time(0));
    for(i=0;i<100;i++){
        s=0;
        for(j=0;j<32768;j++){
            s+=rand();
        }
        printf("%d",s%2);
    }
    return 0;
}

运行的结果:

01010101010101010101010101010101……

我又做了一种尝试——在一张1024*1024的黑色背景图片上,从坐标为(512,512)的点开始,每一次取随机数除以4的余数,对应于上下左右四个方向,在图片上随机行走,用渐变的颜色画出轨迹。

代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define DIM 1024

int r[DIM][DIM],g[DIM][DIM],b[DIM][DIM];
FILE *fp;

void init(){
    int R,G,B;
    int x=DIM/2,y=DIM/2;
    srand(time(0));
    for(R=0;R<256;R++)for(G=0;G<256;G++)for(B=0;B<256;B++){
        r[x][y]=R;g[x][y]=G;b[x][y]=B;
        switch(rand()&3){
            case 0:y--;break;//up
            case 1:x++;break;//right
            case 2:y++;break;//down
            case 3:x--;break;//left
        }
        x&=1023;
        y&=1023;
    }
}

void pixel_write(int i, int j){
    static unsigned char color[3];
    color[0] = r[i][j]&255;
    color[1] = g[i][j]&255;
    color[2] = b[i][j]&255;
    fwrite(color, 1, 3, fp);
}

int main(){
    init();
    fp = fopen("Pic.ppm","wb");
    fprintf(fp, "P6\n%d %d\n255\n", DIM, DIM);
    for(int j=0;j<DIM;j++)
        for(int i=0;i<DIM;i++)
            pixel_write(i,j);
    fclose(fp);
    return 0;
}

运行结果:

random3

可以看出整张图片只有一小部分被颜色覆盖了,而且图案的对称性很强。

这些代码是在Windows上用Dev-C++中的GCC编译器编译的,如果换一种环境,随机数的周期性还会这么强吗?

我又试了Visual Studio 2012,效果和之前完全一样。

但是当我在Ubuntu上使用GCC编译时,情况发生了变化。

首先我编译运行了那段很短的C语言代码,程序给出的结果中找不到规律。我又运行了随机行走的代码,效果如下:

random4

可以看出颜色随机覆盖了几乎整个屏幕,毫无规律可循。这说明Ubuntu下的GCC编译器使用的随机数算法比Windows下的要好。

用随机颜色生成的随机图案还是很美的。

《从图片看出伪随机数的周期性》有7个想法

  1. VC++內建的rand()可能是出於效率考量,是使用Linear congruential generator來產生偽隨機數的,GCC改成Mersenne twister了。

    如果想在VC++中使用Mersenne twister,要用MT19937

  2. linux下随机数生成比较好。建议试试windows下的 CryptGenRandom(而非cryptRandom ), 有结果给我发个邮件,谢谢

    1. 我试过了,效果和linux下一样,看不出周期。测试的源代码和效果图已经发邮件了

      1. 师弟把测试windows下的CryptGenRandom和cryptRandom代码和图片也更新放到这篇文章里吧。

  3. Pingback: Si-Yuan's

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注