今天尝试用代码生成了一张大小为1024*1024,每个像素的RGB值都是随机数的图片(以下图片经过了一定程度的压缩):
本应该是完全随机的图案,但是在图片中却可以明显地看出许多竖线,这显然是伪随机数的周期性导致的。
由于图片的宽度是1024像素,所以我猜测其中的周期一定和1024这个数有关。我决定看一看随机数以1024为周期的统计特性。
我把每个颜色值的生成方法改为连续获取1024个随机数,算出总和,并对256取余数,得到了这幅图片:
从这幅图片中可以明显地看出很强的周期性!
我再把统计的周期取大一些,例如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; }
运行结果:
可以看出整张图片只有一小部分被颜色覆盖了,而且图案的对称性很强。
这些代码是在Windows上用Dev-C++中的GCC编译器编译的,如果换一种环境,随机数的周期性还会这么强吗?
我又试了Visual Studio 2012,效果和之前完全一样。
但是当我在Ubuntu上使用GCC编译时,情况发生了变化。
首先我编译运行了那段很短的C语言代码,程序给出的结果中找不到规律。我又运行了随机行走的代码,效果如下:
可以看出颜色随机覆盖了几乎整个屏幕,毫无规律可循。这说明Ubuntu下的GCC编译器使用的随机数算法比Windows下的要好。
用随机颜色生成的随机图案还是很美的。
VC++內建的rand()可能是出於效率考量,是使用Linear congruential generator來產生偽隨機數的,GCC改成Mersenne twister了。
如果想在VC++中使用Mersenne twister,要用MT19937
linux下随机数生成比较好。建议试试windows下的 CryptGenRandom(而非cryptRandom ), 有结果给我发个邮件,谢谢
我试过了,效果和linux下一样,看不出周期。测试的源代码和效果图已经发邮件了
收到了,多谢啊。你们的研究真富有艺术。
师弟把测试windows下的CryptGenRandom和cryptRandom代码和图片也更新放到这篇文章里吧。
师弟好赞!