Perché ottengo questo particolare schema di colori quando uso rand ()?


170

Ho provato a creare un file di immagine, in questo modo:

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);
    }
}

Mi aspettavo di ottenere qualcosa di casuale (rumore bianco). Tuttavia, l'output è interessante:

Conosci il motivo per cui?


modificare

Ora, è chiaro che non ha nulla a che fare con rand().

Prova anche questo codice:

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 - bella demo. È deterministico al 100%
pm100


50
@ pm100: Come spiega la risposta di bames53 così bene, otterresti lo stesso schema anche se utilizzassi un generatore di numeri casuali perfetto.
TonyK,

13
Scusa una domanda: dal momento che stai usando le coordinate xey per calcolare i valori dei pixel, perché dovresti aspettarti che tali valori siano indipendenti dalle coordinate? Se l'immagine è troppo uniformemente casuale, questo è ciò di cui avresti bisogno, giusto?
Thomas Padron-McCarthy,

15
Lezioni apprese: fare cose a caso con numeri casuali non produce risultati casuali :)
Hagen von Eitzen,

Risposte:


355

Inizialmente avrei avuto la stessa risposta di tutti gli altri e avrei risolto il problema con i problemi rand(). Tuttavia, ho pensato meglio di farlo e ho invece analizzato la distribuzione che la tua matematica sta effettivamente producendo.

TL; DR: il modello che vedi non ha nulla a che fare con il generatore di numeri casuali sottostante e invece è semplicemente dovuto al modo in cui il tuo programma sta manipolando i numeri.

Seguirò la tua funzione blu poiché sono tutti simili.

uint8_t blue(uint32_t x, uint32_t y) {
    return (rand() % 2)                  ? (x + y) % rand() :
           ((x * y % 1024) % rand()) % 2 ? (x - y) % rand() :
                                           rand();
}

Ciascun valore di pixel è selezionato da uno dei tre funzioni: (x + y) % rand(), (x - y) % rand()e rand();

Diamo un'occhiata alle immagini prodotte da ciascuno di questi da solo.

  • rand()

Questo è quello che ti aspetteresti, solo rumore. Chiama questa "Immagine C"

inserisci qui la descrizione dell'immagine


  • (x + y) % rand()

Qui stai sommando le coordinate dei pixel e stai prendendo il resto dalla divisione per un numero casuale. Se l'immagine è 1024x1024, la somma è compresa nell'intervallo [0-2046]. Il numero casuale per cui ti stai immergendo è nell'intervallo [0, RAND_MAX], dove RAND_MAX è almeno 32k e su alcuni sistemi è 2 miliardi. In altre parole, nella migliore delle ipotesi c'è una probabilità 1 su 16 che il resto non sia solo (x + y). Quindi per la maggior parte questa funzione produrrà solo un gradiente di blu crescente verso la direzione + x + y.

Tuttavia stai usando solo gli 8 bit più bassi, perché restituisci a uint8_t, quindi avrai strisce di gradienti larghe 256 pixel.

Chiama questa "Immagine A"

inserisci qui la descrizione dell'immagine


  • (x - y) % rand()

Qui fai qualcosa di simile, ma con sottrazione. Finché x è maggiore di y avrai qualcosa di simile all'immagine precedente. Ma dove y è maggiore, il risultato è un numero molto grande perché xe ysono senza segno (i risultati negativi si spostano in cima all'intervallo del tipo senza segno), quindi i % rand()calci entrano e si ottiene effettivamente rumore.

Chiama questa "Immagine B"

inserisci qui la descrizione dell'immagine

Ogni pixel nell'immagine finale viene preso da una di queste tre immagini usando le funzioni rand() % 2e ((x * y % 1024) % rand()) % 2. Il primo di questi può essere letto come una scelta con probabilità del 50% (ignorando i problemi con rand()e i suoi bit di basso ordine).

Ecco un primo piano di dove rand() % 2è vero (pixel bianchi), quindi è selezionata l'immagine A.

inserisci qui la descrizione dell'immagine

La seconda funzione ((x * y % 1024) % rand()) % 2ha di nuovo il problema in cui di rand()solito è maggiore della cosa che stai dividendo (x * y % 1024), che è al massimo 1023. Quindi (x*y%1024)%2non produce 0 e 1 ugualmente spesso. Qualsiasi numero dispari moltiplicato per qualsiasi numero pari è pari. Qualsiasi numero pari moltiplicato per qualsiasi numero pari è anche pari. Solo un numero dispari moltiplicato per un numero dispari è dispari, e così %2via valori che sono anche tre quarti del tempo produrranno 0 tre quarti del tempo.

Ecco un primo piano di dove ((x * y % 1024) % rand()) % 2è vero in modo che l'immagine B possa essere selezionata. Sta selezionando esattamente dove entrambe le coordinate sono dispari.

inserisci qui la descrizione dell'immagine

Ed ecco un primo piano di dove è possibile selezionare l'immagine C:

inserisci qui la descrizione dell'immagine

Infine, combinando le condizioni qui è selezionata l'immagine B:

inserisci qui la descrizione dell'immagine

E dove è selezionata l'immagine C:

inserisci qui la descrizione dell'immagine

La combinazione risultante può essere letta come:

Con una probabilità del 50% usa il pixel dell'immagine A. Il resto del tempo seleziona tra Immagine B e Immagine C, B dove entrambe le coordinate sono dispari, C dove una delle due è pari.

Infine, poiché stai facendo lo stesso per tre colori diversi, ma con orientamenti diversi, i motivi sono orientati in modo diverso in ciascun colore e producono le strisce incrociate o il motivo a griglia che stai vedendo.


45

Molti dei calcoli che stai facendo nel tuo codice non porteranno a valori veramente casuali. Quelle linee taglienti che stai vedendo corrispondono a luoghi in cui i valori relativi delle tue coordinate xey vengono scambiate tra loro e quando ciò accade stai usando formule fondamentalmente diverse. Ad esempio, l'informatica (x + y) % rand()generalmente ti restituirà il valore x + y, poiché rand()(di solito) restituirà un numero molto, molto più grande di quello x + yche di RAND_MAXsolito è un numero abbastanza grande. In tal senso, non dovresti aspettarti di recuperare rumore bianco, poiché l'algoritmo che stai utilizzando per generare le cose è distorto dalla generazione di rumore bianco. Se si desidera il rumore bianco, impostare semplicemente ciascun pixel surand(). Se desideri un modello gradevole come quello che hai sopra, ma con un po 'di casualità lanciato qua e là, continua a usare il codice che hai scritto.

Inoltre, come ha notato @ pm100 nei commenti, la randfunzione non restituisce numeri veramente casuali e utilizza invece una funzione pseudocasuale per produrre valori. L'implementazione di default randsu molti sistemi utilizza un tipo di generatore di numeri pseudocasuali chiamato generatore di congruenza lineare che produce numeri che in brevi raffiche possono apparire casuali, ma che nella pratica sono decisamente non casuali. Ad esempio, ecco un'animazione di Wikipedia che mostra come i punti casuali nello spazio scelti con un generatore congruenziale lineare finiscano per cadere in un numero fisso di iperpiani:

L'immagine

Se si sostituiscono le coordinate x, ye z con le coordinate R, G e B, questo appare notevolmente simile all'output prodotto dal programma. Sospetto che questo non sia probabilmente il problema principale qui, poiché l'altro aspetto sopra menzionato sarà probabilmente molto più pronunciato.

Se stai cercando numeri casuali di qualità superiore, dovrai utilizzare una fonte casuale di qualità superiore. In C, potresti prendere in considerazione la lettura di byte da /dev/urandom/(su un sistema simile a Linux), che fornisce valori casuali in modo abbastanza uniforme. C ++ ora ha un certo numero di buone primitive per la generazione di numeri casuali nelle sue librerie standard, se disponibili.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.