Почему я получаю этот конкретный цветовой паттерн при использовании rand()?

Я попытался создать файл изображения, например:

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

Ответы

Ответ 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()

Это то, чего вы ожидаете, просто шум. Назовите это "Image C"

enter image description here


  • (x + y) % rand()

Здесь вы добавляете координаты пикселя вместе и отделяете остаток от случайного числа. Если изображение равно 1024x1024, тогда сумма находится в диапазоне [0-2046]. Случайное число, которое вы ныряете, находится в диапазоне [0, RAND_MAX], где RAND_MAX составляет не менее 32 тыс., А на некоторых системах - 2 млрд. Другими словами, в лучшем случае вероятность 1 из 16 заключается в том, что остаток не просто (x + y). Таким образом, по большей части эта функция будет генерировать только градиент синего цвета в направлении +x + y.

Однако вы используете только самые младшие 8 бит, потому что вы возвращаете uint8_t, поэтому у вас будут полосы градиентов шириной 256 пикселей.

Назовите это "Изображение A"

enter image description here


  • (x - y) % rand()

Здесь вы делаете что-то подобное, но с вычитанием. Пока x больше y, вы будете иметь что-то похожее на предыдущее изображение. Но где y больше, результат - очень большое число, потому что x и y без знака (отрицательные результаты округляются до вершины диапазона без знака), а затем появляется % rand() и вы действительно получаете шум.

Назовите это "Изображение B"

enter image description here

Каждый пиксель в конечном изображении берется из одного из этих трех изображений, используя функции rand() % 2 и ((x * y % 1024) % rand()) % 2. Первый из них можно считать выбором с вероятностью 50% (игнорируя проблемы с rand() и битами младшего порядка).

Здесь крупным планом, где rand() % 2 истинно (белые пиксели), так что выбрано изображение A.

enter image description here

Вторая функция ((x * y % 1024) % rand()) % 2 снова имеет проблему, где rand() обычно больше, чем вещь, которую вы делите, (x * y % 1024), которая не более 1023. Тогда (x*y%1024)%2 не производит 0 и 1 одинаково часто. Любое нечетное число, умноженное на любое четное число, является четным. Любое четное число, умноженное на любое четное число, также четное. Только нечетное число, умноженное на нечетное число, является нечетным, поэтому %2 при значениях, составляющих даже три четверти времени, будет производить 0 три четверти времени.

Здесь крупным планом, где ((x * y % 1024) % rand()) % 2 истинно, чтобы можно было выбрать изображение B. Он выбирает именно там, где обе координаты нечетны.

enter image description here

И здесь крупным планом, где можно выбрать Image C:

enter image description here

Наконец, комбинируя условия здесь, где выбрано изображение B:

enter image description here

И где выбрано изображение C:

enter image description here

Получившаяся комбинация может быть прочитана как:

При 50% вероятности используйте пиксель из изображения A. Остальное время выбирается между изображением B и изображением C, B, где обе координаты нечетны, C, где один из них четный.

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

Ответ 2

Многие из вычислений, которые вы делаете в своем коде, не приведут к действительно случайным значениям. Те острые линии, которые вы видите, соответствуют местам, где относительные значения ваших координат x и y торгуют друг с другом, и когда это происходит, вы используете принципиально разные формулы. Например, вычисление (x + y) % rand() обычно возвращает вам значение x + y, так как rand() будет (обычно) возвращать число, намного большее, чем x + y учитывая, что RAND_MAX обычно является довольно большое количество. В этом смысле вы не должны ожидать возврата белого шума, так как алгоритм, который вы используете для генерации вещей, смещен от создания белого шума. Если вы хотите белый шум, просто установите каждый пиксель в rand(). Если вы хотите получить хороший шаблон, подобный тому, который у вас есть выше, но с небольшим количеством случайности, брошенным здесь и там, продолжайте использовать код, который вы написали.

Кроме того, как отмечает @pm100 в комментариях, функция rand не возвращает действительно случайные числа, а вместо этого использует псевдослучайную функцию для получения ее значений. По умолчанию реализация rand во многих системах использует тип генератора псевдослучайных чисел, называемый линейным конгруэнтным генератором, который производит числа, которые в коротких всплесках могут казаться случайными, но которые на практике неслучайны. Например, здесь анимация из Википедии, показывающая, как случайные точки в пространстве, выбранные с помощью линейного конгруэнтного генератора, попадают в фиксированное число гиперплоскостей:

The image

Если вы замените координаты x, y и z координатами R, G и B, это выглядит очень похоже на результат, создаваемый вашей программой. Я подозреваю, что это, вероятно, не является основной проблемой здесь, так как другой аспект, упомянутый выше, вероятно, будет гораздо более выраженным.

Если вы ищете более качественные случайные числа, вам нужно использовать более качественный случайный источник. В C вы можете рассмотреть возможность чтения байтов из /dev/urandom/ (в Linux-подобной системе), что дает довольно равномерно случайные значения. C++ теперь имеет ряд хороших примитивов генерации случайных чисел в своих стандартных библиотеках, если это доступно вам.