Почему я получаю именно этот образец цвета при использовании 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
13
Извините вопрос: поскольку вы используете координаты x и y для вычисления значений пикселей, почему вы ожидаете, что эти значения не зависят от координат? Если изображение выглядит слишком случайно, это то, что вам нужно, верно?
Томас Падрон-Маккарти,
15
Извлеченные уроки: выполнение случайных вещей со случайными числами не приводит к случайным результатам :)
Хаген фон Айцен

Ответы:

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 составляет не менее 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, где любая из них является четной.

Наконец, поскольку вы делаете одно и то же для трех разных цветов, но с разной ориентацией узоры ориентированы по-разному в каждом цвете и создают полосы пересечения или узор сетки, которые вы видите.

bames53
источник
45

Многие вычисления, которые вы делаете в своем коде, не приведут к действительно случайным значениям. Те острые линии, которые вы видите, соответствуют местам, где относительные значения ваших координат 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 ++ теперь имеет несколько хороших примитивов генерации случайных чисел в своих стандартных библиотеках, если это доступно для вас.

templatetypedef
источник