Чому я отримую саме цей колірний малюнок під час використання 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()

Тут ви робите щось подібне, але з відніманням. Поки х більший за y, ви матимете щось подібне до попереднього зображення. Але там , де у більше, то результат буде дуже велике число , так як xі yбез знака (негативні результати обернути навколо верхньої частини діапазону тип без знака в), а потім в % 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% -ною ймовірністю використовуйте піксель із зображення А. Решту часу вибирайте між зображенням B і зображенням C, B, коли обидві координати непарні, C, де будь-яка парна.

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


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 ++ тепер має ряд хороших примітивів покоління випадкових чисел у своїх стандартних бібліотеках, якщо вони доступні вам.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.