Я попытался создать файл изображения, как это:
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; */
}
Ответы:
Первоначально я собирался получить тот же ответ, что и все остальные, и связать его с проблемами
rand()
. Однако я подумал, что лучше сделать это, и вместо этого проанализировал распределение, которое фактически производит ваша математика.TL; DR: шаблон, который вы видите, не имеет ничего общего с базовым генератором случайных чисел, а просто связан с тем, как ваша программа манипулирует числами.
Я буду придерживаться вашей синей функции, так как они все похожи.
Каждое значение пикселя выбирают одну из трех функций из:
(x + y) % rand()
,(x - y) % rand()
, иrand()
;Давайте посмотрим на изображения, созданные каждым из них в одиночку.
rand()
Это то, что вы ожидаете, только шум. Назовите это "Образ C"
(x + y) % rand()
Здесь вы складываете пиксельные координаты вместе и берете остаток от деления на случайное число. Если изображение 1024x1024, то сумма находится в диапазоне [0-2046]. Случайное число, на которое вы ныряете, находится в диапазоне [0, RAND_MAX], где RAND_MAX составляет не менее 32 КБ, а в некоторых системах - 2 миллиарда. Другими словами, в лучшем случае есть шанс 1 к 16, что остаток не только
(x + y)
. Так что по большей части эта функция будет просто создавать градиент увеличения синего в направлении + x + y.Однако вы используете только младшие 8 битов, потому что вы возвращаете a
uint8_t
, поэтому у вас будут полосы градиентов шириной 256 пикселей.Назовите это «Образ А»
(x - y) % rand()
Здесь вы делаете что-то похожее, но с вычитанием. Пока х больше, чем у, у вас будет что-то похожее на предыдущее изображение. Но там , где у больше, то результат будет очень большое число , так как
x
иy
без знака (отрицательные результаты обернуть вокруг верхней части диапазона тип без знака в), а затем в% rand()
кайф, и вы действительно получите шум.Назовите это "Образ B"
Каждый пиксель в вашем конечном изображении взят из одного из этих трех изображений с использованием функций
rand() % 2
и((x * y % 1024) % rand()) % 2
. Первый из них может быть прочитан как выбор с вероятностью 50% (игнорирование проблем сrand()
битами младшего разряда).Вот крупным планом, где
rand() % 2
истина (белые пиксели), поэтому выбрано изображение А.Вторая функция
((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, где любая из них является четной.
Наконец, поскольку вы делаете одно и то же для трех разных цветов, но с разной ориентацией узоры ориентированы по-разному в каждом цвете и создают полосы пересечения или узор сетки, которые вы видите.
источник
Многие вычисления, которые вы делаете в своем коде, не приведут к действительно случайным значениям. Те острые линии, которые вы видите, соответствуют местам, где относительные значения ваших координат x и y торгуются друг с другом, и когда это происходит, вы используете принципиально разные формулы. Например, вычисления
(x + y) % rand()
, как правило, возвращают вам значениеx + y
, посколькуrand()
(обычно) возвращают число намного, намного большее, чемx + y
заданное,RAND_MAX
которое обычно является довольно большим числом. В этом смысле вы не должны ожидать возврата белого шума, поскольку алгоритм, который вы используете для генерации вещей, смещен от генерации белого шума. Если вы хотите белый шум, просто установите каждый пиксель вrand()
, Если вам нужен хороший шаблон, подобный тому, который у вас есть выше, но с небольшой случайностью, оставленной здесь и там, продолжайте использовать код, который вы написали.Кроме того, как отметил @ pm100 в комментариях,
rand
функция не возвращает действительно случайные числа, а вместо этого использует псевдослучайную функцию для получения значений. Реализация по умолчаниюrand
во многих системах использует тип генератора псевдослучайных чисел, называемый линейным конгруэнтным генератором, который генерирует числа, которые в коротких пакетах могут казаться случайными, но которые на практике явно неслучайны. Например, вот анимация из Википедии, показывающая, как случайные точки в пространстве, выбранные с помощью линейного конгруэнтного генератора, попадают в фиксированное число гиперплоскостей:Если вы замените координаты x, y и z на координаты R, G и B, это будет выглядеть очень похоже на вывод, создаваемый вашей программой. Я подозреваю, что это, вероятно, не основная проблема здесь, так как другой аспект, упомянутый выше, вероятно, будет гораздо более выраженным.
Если вы ищете более качественные случайные числа, вам нужно использовать более качественный случайный источник. В C вы можете рассмотреть чтение байтов из
/dev/urandom/
(в Linux-подобной системе), что дает довольно равномерно случайные значения. C ++ теперь имеет несколько хороших примитивов генерации случайных чисел в своих стандартных библиотеках, если это доступно для вас.источник