为什么在使用rand()时会得到这种特殊的颜色模式?
我试图创建一个图像文件,如下所示:
uint8_t raw_r[pixel_width][pixel_height]; uint8_t raw_g[pixel_width][pixel_height]; uint8_t raw_b[pixel_width][pixel_height]; uint8_t blue(uint32_t x, uint32_t y) { return (rand()%2)? (x+y)%rand() : ((x*y%1024)%rand())%2 ? (xy)%rand() : rand(); } uint8_t green(uint32_t x, uint32_t y) { return (rand()%2)? (xy)%rand() : ((x*y%1024)%rand())%2 ? (x+y)%rand() : rand(); } uint8_t red(uint32_t x, uint32_t y) { return (rand()%2)? (yx)%rand() : ((x*y%1024)%rand())%2 ? (x+y)%rand() : rand(); } for (y=0; y<pixel_height; ++y) { for (x=0; x<pixel_width; ++x) { raw_b[x][y]=blue(x, y); raw_g[x][y]=green(x, y); raw_r[x][y]=red(x, y); } }
我希望随机得到一些东西(白噪声)。 但是,输出很有意思:
你知道原因吗?
编辑
现在,很明显它与rand()
无关。
也试试这段代码:
for (x=0; x<pixel_width; ++x) for (y=0; y<pixel_height; ++y) { r[x][y] = (x+y); g[x][y] = (yx); /* b[x][y] = rand()%2? x : y; */ }
我最初会得到与其他人一样的答案,并用rand()
来解决这个问题。 但是,我认为这样做更好,而是分析了数学实际产生的分布。
TL; DR:您看到的模式与底层随机数生成器无关,而只是由于程序操作数字的方式。
我会坚持你的蓝色function,因为它们都很相似。
uint8_t blue(uint32_t x, uint32_t y) { return (rand() % 2) ? (x + y) % rand() : ((x * y % 1024) % rand()) % 2 ? (x - y) % rand() : rand(); }
每个像素值选自三个函数之一: (x + y) % rand()
, (x - y) % rand()
和rand()
;
让我们看一下这些产生的图像。
-
rand()
这就是你所期望的,只是噪音。 称之为“图像C”
-
(x + y) % rand()
在这里,您将像素坐标添加到一起,并将余数除以随机数。 如果图像是1024×1024,则总和在[0-2046]范围内。 您潜水的随机数在[0,RAND_MAX]范围内,其中RAND_MAX至少为32k,在某些系统上为20亿。 换句话说,剩下的不仅仅是(x + y)
,最多只有16比1的机会。 所以在大多数情况下,这个函数只会产生朝向+ x + y方向增加蓝色的梯度。
但是你只使用最低的8位,因为你返回一个uint8_t
,所以你会有256像素宽的渐变条纹。
称之为“图像A”
-
(x - y) % rand()
在这里你做类似的事情,但减法。 只要x大于y,就会有类似于前一个图像的东西。 但是当y更大时,结果是一个非常大的数字,因为x
和y
是无符号的(负结果环绕到无符号类型范围的顶部),然后% rand()
启动并且你实际上得到了噪声。
称之为“图像B”
使用函数rand() % 2
和((x * y % 1024) % rand()) % 2
从最终图像中的每个像素中取出这三个图像中的一个像素。 其中第一个可以读作50%概率选择(忽略rand()
及其低位的问题。)
这是rand() % 2
为真(白色像素)的特写,因此选择了图像A.
第二个函数((x * y % 1024) % rand()) % 2
再次出现问题,其中rand()
通常大于你要划分的东西, (x * y % 1024)
,最多为1023然后(x*y%1024)%2
不会同等地产生0和1。 任何奇数乘以任何偶数都是偶数。 任何偶数乘以任何偶数也是偶数。 只有奇数乘以奇数才是奇数,因此即使是四分之三时间的值, %2
也会产生0四分之三的时间。
这是((x * y % 1024) % rand()) % 2
为真的特写,以便可以选择图像B. 它精确地选择两个坐标都是奇数的位置。
这里是可以选择Image C的特写:
最后结合这里的条件选择图像B:
选择图像C的位置:
结果组合可以理解为:
50%的概率使用图像A中的像素。其余时间选择图像B和图像C,B,其中两个坐标都是奇数,C,其中任何一个是偶数。
最后,由于您对三种不同的颜色做了相同的操作,但是在不同的方向上,每种颜色的图案的方向都不同,并产生您所看到的交叉条纹或网格图案。
您在代码中执行的许多计算都不会导致真正的随机值。 您所看到的那些尖锐的线条对应于您的x和y坐标的相对值彼此交换的位置,并且当发生这种情况时,您使用的是根本不同的公式。 例如,计算(x + y) % rand()
通常会返回值x + y
,因为rand()
通常会返回一个比x + y
大得多的数字,因为RAND_MAX
通常是公平的大数字。 从这个意义上讲,你不应该期望得到白噪声,因为你用来生成东西的算法偏离产生白噪声。 如果你想要白噪声,只需将每个像素设置为rand()
。 如果你想要一个像你上面那样漂亮的模式,但是在这里和那里有一点点随机性,继续使用你编写的代码。
另外,正如@ pm100在注释中指出的那样, rand
函数不会返回真正的随机数,而是使用伪随机函数来生成它的值。 许多系统上rand
的默认实现使用了一种称为线性同余生成器的伪随机数生成器 ,它产生的数字在短脉冲中可以随机出现,但在实践中它们显然是非随机的。 例如,这里是来自维基百科的动画,显示了使用线性同余生成器选择的空间中的随机点如何最终落入固定数量的超平面:
如果用R,G和B坐标替换x,y和z坐标,这看起来非常类似于程序生成的输出。 我怀疑这可能不是这里的核心问题,因为上面提到的另一个方面可能会更加明显。
如果您正在寻找更高质量的随机数,则需要使用更高质量的随机数。 在C中,您可以考虑从/dev/urandom/
(在类似Linux的系统上)读取字节,它提供相当均匀的随机值。 C ++现在在其标准库中有许多好的随机数生成原语,如果可以的话。