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

Posted

技术标签:

【中文标题】为啥我在使用 rand() 时会得到这种特殊的颜色模式?【英文标题】:Why do I get this particular color pattern when using rand()?为什么我在使用 rand() 时会得到这种特殊的颜色模式? 【发布时间】:2019-02-26 08:26:38 【问题描述】:

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

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; */
    

【问题讨论】:

cos rand isnt rand - 很好的演示。它是 100% 确定性的 ***.com/questions/822323/… @pm100:正如 bames53 的回答解释得很好,即使您使用完美的随机数生成器,您也会得到相同的模式。 请教一个问题:既然您使用 x 和 y 坐标来计算像素值,为什么您希望这些值与坐标无关?如果图像看起来太均匀随机,那就是你需要的,对吧? 经验教训:用随机数做随机事情不会产生随机结果:) 【参考方案1】:

我最初会得到与其他人相同的答案,并将其归结为 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 亿。换句话说,最多有 16 分之一的机会余下的不仅仅是 (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 中任一坐标均为偶数。

最后,由于您对三种不同的颜色执行相同的操作,但方向不同,每种颜色的图案方向不同,并产生您所看到的交叉条或网格图案。

【讨论】:

【参考方案2】:

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

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

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

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

【讨论】:

以上是关于为啥我在使用 rand() 时会得到这种特殊的颜色模式?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我在解析 XML 时会从此方法中得到重复记录?

为啥使用相同的 Keras 模型和输入进行预测时会得到不同的结果?

为啥我总是用 rand() 得到相同的随机数序列?

为啥我总是用 rand() 得到相同的随机数序列?

为啥我在 pentaho 中过滤行年龄 > 0 时会减号?

为啥我在实例化接口时会出错?