C 为什么在使用rand()时会出现这种特殊的颜色图案?

C 为什么在使用rand()时会出现这种特殊的颜色图案?,c,image,random,C,Image,Random,我尝试创建一个图像文件,如下所示: 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 ? (x-y)

我尝试创建一个图像文件,如下所示:

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 ? (x-y)%rand() : rand();
}
uint8_t green(uint32_t x, uint32_t y)
{
    return (rand()%2)? (x-y)%rand() : ((x*y%1024)%rand())%2 ? (x+y)%rand() : rand();
}
uint8_t red(uint32_t x, uint32_t y)
{
    return (rand()%2)? (y-x)%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);
    }
}

您在代码中进行的许多计算不会产生真正的随机值。你看到的那些锐利的线条对应于x和y坐标的相对值相互交换的地方,当这种情况发生时,你使用的是根本不同的公式。例如,计算
(x+y)%rand()
通常会返回值
x+y
,因为
rand()
会(通常)返回一个比
x+y
大得多的数字,因为
rand\u MAX
通常是一个相当大的数字。从这个意义上说,你不应该期望得到白噪声,因为你用来产生东西的算法偏离了产生白噪声的方向。如果需要白噪声,只需将每个像素设置为
rand()
。如果你想要一个很好的模式,就像上面的一样,但是有一点随机性,继续使用你写的代码

此外,正如@pm100在注释中所指出的,
rand
函数不会返回真正的随机数,而是使用伪随机函数来生成它的值。在许多系统上,
rand
的默认实现使用一种称为线性同余生成器的伪随机数生成器,该生成器生成的数字在短时间内可能看起来是随机的,但实际上肯定是非随机的。例如,这里有一个来自维基百科的动画,展示了使用线性同余生成器选择的空间中的随机点如何最终落入固定数量的超平面:

如果将x、y和z坐标替换为R、G和B坐标,这看起来与程序生成的输出非常相似。我怀疑这可能不是这里的核心问题,因为上面提到的其他方面可能会更加明显


如果您正在寻找更高质量的随机数,则需要使用更高质量的随机源。在C中,您可以考虑从<代码> /DEV/URANDOM/<代码>(在Linux系统上)读取字节,这些字节提供了相当一致的随机值。C++现在在标准库中有很多好的随机数生成基元,如果你可以使用的话。

< p>我最初会得到和大家一样的答案,并用“代码> RAND())/<代码>来处理这些问题。然而,我认为这样做更好,而是分析了你的数学实际产生的分布

TL;DR:您看到的模式与底层随机数生成器无关,而只是由于您的程序处理数字的方式

我将坚持使用你的蓝色函数,因为它们都是相似的

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()
在这里,您将像素坐标相加,并将剩余部分除以一个随机数。如果图像为1024x1024,则总和在[0-2046]范围内。您所使用的随机数在[0,RAND_MAX]范围内,其中RAND_MAX至少为32k,在某些系统上为20亿。换句话说,剩下的不只是
(x+y)
,最多有16分之一的可能性。所以在大多数情况下,这个函数只会产生一个向+x+y方向增加蓝色的梯度

但是,您只使用最低的8位,因为您返回一个
uint8\u 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。它精确地选择两个坐标都是奇数的位置

下面是可以选择图像C的特写:

最后,结合此处选择图像B的条件:

在选择图像C的位置:

由此产生的组合可以理解为:

以50%的概率使用图像A中的像素。其余时间在图像B和图像C、B之间拾取,其中两个坐标都是
uint8_t blue(uint32_t x, uint32_t y) {
    return (rand() % 2)                  ? (x + y) % rand() :
           ((x * y % 1024) % rand()) % 2 ? (x - y) % rand() :
                                           rand();
}