为什么在使用rand()时会得到这种特殊的颜色模式?

Lit*_*ony 169 c random image

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

我希望随机得到一些东西(白噪声).但是,输出很有意思:

你知道原因吗?


编辑

现在,很明显它与之无关rand().

也试试这段代码:

for (x=0; x<pixel_width; ++x)
    for (y=0; y<pixel_height; ++y)
    {
        r[x][y] = (x+y);
        g[x][y] = (y-x);
        /* b[x][y] = rand()%2? x : y; */
    }
Run Code Online (Sandbox Code Playgroud)

bam*_*s53 355

我最初会得到与其他人一样的答案,并将其归结为问题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();
}
Run Code Online (Sandbox Code Playgroud)

每个像素值是从三个功能一种:(x + y) % rand(),(x - y) % rand(),和rand();

让我们看一下这些产生的图像.

  • rand()

这就是你所期望的,只是噪音.称之为"图像C"

在此输入图像描述


  • (x + y) % rand()

在这里,您将像素坐标加在一起,并将余数除以随机数.如果图像是1024x1024,则总和在[0-2046]范围内.您潜水的随机数在[0,RAND_MAX]范围内,其中RAND_MAX至少为32k,在某些系统上为20亿.换句话说,剩下的不仅仅是十分之一的机会(x + y).所以在大多数情况下,这个函数只会产生朝向+ x + y方向增加蓝色的梯度.

但是,您只使用最低的8位,因为您返回a 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,其中任何一个是偶数.

最后,由于您对三种不同的颜色做了相同的操作,但是在不同的方向上,每种颜色的图案的方向都不同,并产生您所看到的交叉条纹或网格图案.


tem*_*def 45

您在代码中执行的许多计算都不会导致真正的随机值.您所看到的那些尖锐线条对应于您的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++现在在其标准库中有许多好的随机数生成原语,如果可以的话.