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


170

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

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);
    }
}

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

你知道原因吗?


编辑

现在,很明显,它与 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; */
    }


26
cos rand isnt rand-不错的演示。它是100%确定性的
pm100


50
@ pm100:正如bames53的答案解释得很好,即使您使用完美的随机数生成器,也将获得相同的模式。
TonyK '18

13
请问一个问题:既然您使用的是x和y坐标来计算像素值,为什么还要期望这些值与坐标无关?如果图像看起来过于均匀随机,那就是您所需要的,对吗?
Thomas Padron-McCarthy

15
获得的经验教训:使用随机数执行随机操作不会产生随机结果:)
Hagen von Eitzen

Answers:


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();
}

每个像素值是从三个功能一种:(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位,因为您返回uint8_t,所以您将拥有256像素宽的渐变条纹。

将此称为“图片A”

在此处输入图片说明


  • (x - y) % rand()

在这里,您可以执行类似的操作,但是要减去。只要x大于y,您将获得与上一张图像相似的图像。但是在y较大的情况下,结果是一个非常大的数字,因为xy是无符号的(负结果环绕在无符号类型范围的顶部),然后是% 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,两个坐标均为偶数的C。

最后,由于您要对三种不同的颜色进行相同的操作,但是方向不同,因此每种颜色的图案取向不同,并会产生您看到的交叉带或网格图案。


45

您在代码中执行的许多计算都不会导致真正的随机值。您看到的那些尖线对应于x和y坐标的相对值相互交易的地方,并且在这种情况下,您使用的是根本不同的公式。例如,计算(x + y) % rand()通常会返还您的价值x + y,因为rand()(通常)返回的数字会比远大得多。如果您想要一个与上面一样的不错的模式,但是到处乱七八糟,请继续使用编写的代码。x + y给定RAND_MAX的通常大的数字大很多。从这个意义上讲,您不应该期望重新获得白噪声,因为用于生成事物的算法与生成白噪声有偏差。如果您想要白噪声,只需将每个像素设置为rand()

此外,正如@ pm100在注释中指出的那样,该rand函数不会返回真正的随机数,而是使用伪随机函数来产生它的值。rand许多系统上的默认实现使用一种称为线性同余生成器的伪随机数生成,该伪随机数生成生成的数字在短脉冲串中可能看起来是随机的,但实际上却是非随机的。例如,下面是Wikipedia的动画,它显示了使用线性同余生成器选择的空间中的随机点如何最终落入固定数量的超平面中:

图片

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

如果您要寻找质量更高的随机数,则需要使用质量更高的随机数。在C语言中,您可以考虑从/dev/urandom/(在类似Linux的系统上)读取字节,该字节给出了相当均匀的随机值。如果您可以使用,C ++现在在其标准库中有许多良好的随机数生成原语。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.