web-dev-qa-db-fra.com

Pourquoi ai-je ce motif de couleur particulier lorsque j'utilise Rand ()?

J'ai essayé de créer un fichier image, comme ceci:

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

Je m'attendais à quelque chose de aléatoire (bruit blanc). Cependant, le résultat est intéressant:

Savez-vous pourquoi?


Modifier

Maintenant, il est clair que cela n’a rien à voir avec Rand().

Essayez aussi ce code:

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

170
Little Pony

Au début, j'allais avoir la même réponse que tout le monde et mettre cela au compte des problèmes rencontrés avec Rand(). Cependant, j’ai pensé qu’il était préférable de le faire et j’ai plutôt analysé la distribution que vos calculs produisent réellement.

TL; DR: Le modèle que vous voyez n'a rien à voir avec le générateur de nombres aléatoires sous-jacent, il est simplement dû à la façon dont votre programme manipule les nombres.

Je vais m'en tenir à votre fonction bleue car elles sont toutes similaires.

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

Chaque valeur de pixel est sélectionnée parmi l'une des trois fonctions suivantes: (x + y) % Rand(), (x - y) % Rand() et Rand();

Regardons les images produites par chacun de ces seuls.

  • Rand()

C'est ce que vous attendez, juste du bruit. Appelez cette "image C"

enter image description here


  • (x + y) % Rand()

Ici, vous additionnez les coordonnées des pixels et prenez le reste de la division par un nombre aléatoire. Si l'image est 1024x1024 alors la somme est dans l'intervalle [0-2046]. Le nombre aléatoire dans lequel vous plongez est compris dans la plage [0, Rand_MAX], où Rand_MAX est au moins égal à 32k et, sur certains systèmes, à 2 milliards. En d'autres termes, il y a au mieux une chance sur 16 que le reste ne soit pas simplement (x + y). Donc, la plupart du temps, cette fonction ne produira qu'un gradient de bleu croissant en direction de la direction + x + y.

Cependant, vous n'utilisez que les 8 bits les plus bas, car vous renvoyez un uint8_t, vous aurez ainsi des bandes de dégradés d'une largeur de 256 pixels.

Appelez cette "image A"

enter image description here


  • (x - y) % Rand()

Ici, vous faites quelque chose de similaire, mais avec soustraction. Tant que x est supérieur à y, vous obtiendrez quelque chose de similaire à l'image précédente. Mais où y est plus grand, le résultat est un très grand nombre parce que x et y ne sont pas signés (les résultats négatifs se situent en haut de la plage du type non signé), puis le % Rand() démarre et vous obtenez réellement du bruit.

Appelez cette "image B"

enter image description here

Chaque pixel de votre image finale provient d'une de ces trois images à l'aide des fonctions Rand() % 2 et ((x * y % 1024) % Rand()) % 2. Le premier d'entre eux peut être lu comme un choix avec une probabilité de 50% (en ignorant les problèmes avec Rand() et ses bits de poids faible.)

Voici un résumé de l'endroit où Rand() % 2 est vrai (pixels blancs) de sorte que l'image A soit sélectionnée.

enter image description here

La deuxième fonction ((x * y % 1024) % Rand()) % 2 présente à nouveau le problème où Rand() est généralement supérieur à ce que vous divisez, (x * y % 1024), qui est au plus égal à 1023. Alors (x*y%1024)%2 ne produit pas 0 et 1 aussi souvent. Tout nombre impair multiplié par un nombre pair est pair. Tout nombre pair multiplié par un nombre pair est également pair. Seul un nombre impair multiplié par un nombre impair est impair, et donc %2sur les valeurs même les trois quarts du temps produiront 0 les trois quarts du temps.

Voici un résumé de l'endroit où ((x * y % 1024) % Rand()) % 2 est vrai pour que l'image B puisse être sélectionnée. Il sélectionne exactement où les deux coordonnées sont impaires.

enter image description here

Et voici un gros plan de l'endroit où l'image C pourrait être sélectionnée:

enter image description here

Enfin, combinant les conditions, voici où l’image B est sélectionnée:

enter image description here

Et où Image C est sélectionné:

enter image description here

La combinaison résultante peut être lue comme suit:

Avec une probabilité de 50%, utilisez le pixel de l'image A. Le reste du temps, choisissez l'image B et l'image C, B où les deux coordonnées sont impaires, C où l'une ou l'autre est paire.

Enfin, puisque vous faites la même chose pour trois couleurs différentes, mais avec des orientations différentes, les motifs sont orientés différemment dans chaque couleur et produisent les bandes de croisement ou le motif de grille que vous voyez.

357
bames53

Un grand nombre des calculs que vous effectuez dans votre code ne mèneront pas à des valeurs vraiment aléatoires. Ces lignes nettes que vous voyez correspondent aux endroits où les valeurs relatives de vos coordonnées x et y se négocient, et lorsque cela se produit, vous utilisez des formules fondamentalement différentes. Par exemple, le calcul de (x + y) % Rand() vous donnera généralement la valeur x + y, puisque Rand() retournera (généralement) un nombre beaucoup plus important que x + y, étant donné que Rand_MAX est généralement un nombre assez important. En ce sens, vous ne devriez pas vous attendre à récupérer du bruit blanc, car l'algorithme que vous utilisez pour générer des objets est biaisé et ne génère pas de bruit blanc. Si vous voulez du bruit blanc, réglez simplement chaque pixel sur Rand(). Si vous souhaitez un motif Nice comme celui que vous avez ci-dessus, mais avec un peu d’aléatoire jeté ici et là, continuez à utiliser le code que vous avez écrit.

De plus, comme l'a noté @ pm100 dans les commentaires, la fonction Rand ne renvoie pas de nombres véritablement aléatoires, mais utilise plutôt une fonction pseudo-aléatoire pour les générer. L'implémentation par défaut de Rand sur de nombreux systèmes utilise un type de générateur de nombres pseudo-aléatoires appelé générateur de congruences linéaires qui produit des nombres qui peuvent semblent aléatoires, mais qui sont décidément non aléatoires dans la pratique. Par exemple, voici une animation de Wikipédia montrant comment des points aléatoires dans l’espace choisis avec un générateur de congruence linéaire finissent par tomber dans un nombre fixe d’hyperplans:

The image

Si vous remplacez les coordonnées x, y et z par les coordonnées R, V et B, cela ressemble de manière remarquable à la sortie produite par votre programme. Je suppose que ce n’est probablement pas la question centrale ici, car l’autre aspect mentionné ci-dessus sera probablement beaucoup plus prononcé.

Si vous recherchez des nombres aléatoires de qualité supérieure, vous devez utiliser une source aléatoire de qualité supérieure. En C, vous pourriez envisager de lire les octets de /dev/urandom/ (sur un système de type Linux), ce qui donne des valeurs assez uniformément aléatoires. C++ a maintenant un certain nombre de bonnes primitives de génération de nombres aléatoires dans ses bibliothèques standard, si cela vous est disponible.

46
templatetypedef