Pourquoi ai-je ce modèle de couleur particulier en utilisant rand ()?

170

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 à obtenir quelque chose d'aléatoire (bruit blanc). Cependant, la sortie est intéressante:

Savez-vous pourquoi?


Éditer

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

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

Petit poney
la source
26
cos rand n'est pas rand - belle démo de celui-ci. Il est 100% déterministe
pm100
50
@ pm100: Comme la réponse de bames53 l'explique si bien, vous obtiendrez le même modèle même si vous utilisiez un générateur de nombres aléatoires parfait.
TonyK
13
Excusez une question: puisque vous utilisez les coordonnées x et y pour calculer les valeurs de pixel, pourquoi vous attendriez-vous à ce que ces valeurs soient indépendantes des coordonnées? Si l'image est trop uniformément aléatoire, c'est ce dont vous auriez besoin, non?
Thomas Padron-McCarthy
15
Leçons apprises: Faire des choses aléatoires avec des nombres aléatoires ne produit pas de résultats aléatoires :)
Hagen von Eitzen

Réponses:

355

J'allais initialement avoir la même réponse que tout le monde et attribuer cela aux problèmes avec rand(). Cependant, j'ai pensé qu'il valait mieux 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 et est simplement dû à la façon dont votre programme manipule les nombres.

Je vais m'en tenir à votre fonction bleue car ils sont tous 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 choisi parmi l' une des trois fonctions suivantes: (x + y) % rand(), (x - y) % rand()et rand();

Regardons les images produites par chacun d'eux seul.

  • rand()

C'est ce à quoi vous vous attendez, juste du bruit. Appelez cela "Image C"

entrez la description de l'image ici


  • (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 de 1024x1024, alors la somme est dans la plage [0-2046]. Le nombre aléatoire par lequel vous plongez est dans la plage [0, RAND_MAX], où RAND_MAX vaut au moins 32k et sur certains systèmes est de 2 milliards. En d'autres termes, il y a au mieux 1 chance sur 16 que le reste ne soit pas juste (x + y). Donc, pour la plupart, cette fonction produira simplement un gradient de bleu croissant vers la direction + x + y.

Cependant, vous n'utilisez que les 8 bits les plus bas, car vous renvoyez a uint8_t, vous aurez donc des bandes de dégradés de 256 pixels de large.

Appelez cela "Image A"

entrez la description de l'image ici


  • (x - y) % rand()

Ici, vous faites quelque chose de similaire, mais avec soustraction. Tant que x est supérieur à y, vous aurez quelque chose de similaire à l'image précédente. Mais où y est supérieur, le résultat est un très grand nombre car xet ysont non signés (les résultats négatifs s'enroulent vers le haut de la plage du type non signé), puis le% rand() coup de pied et vous obtenez en fait du bruit.

Appelez cela "Image B"

entrez la description de l'image ici

Chaque pixel de votre image finale est extrait de l'une de ces trois images à l'aide des fonctions rand() % 2et ((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 avecrand() et ses bits de poids faible.)

Voici un gros plan de l'endroit où rand() % 2est vrai (pixels blancs) pour que l'image A soit sélectionnée.

entrez la description de l'image ici

La deuxième fonction a ((x * y % 1024) % rand()) % 2encore une fois le problème où rand()est généralement supérieur à la chose que vous divisez (x * y % 1024), qui est au maximum de 1023. Ensuite, (x*y%1024)%2ne 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, donc%2 suite des valeurs paires trois quarts du temps produiront 0 trois quarts du temps.

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

entrez la description de l'image ici

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

entrez la description de l'image ici

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

entrez la description de l'image ici

Et là où l'image C est sélectionnée:

entrez la description de l'image ici

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 entre 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.

bames53
la source
45

De nombreux calculs que vous effectuez dans votre code ne conduiront pas à des valeurs vraiment aléatoires. Les lignes nettes que vous voyez correspondent à des endroits où les valeurs relatives de vos coordonnées x et y s'échangent, et lorsque cela se produit, vous utilisez des formules fondamentalement différentes. Par exemple, l'informatique vous (x + y) % rand()rendra généralement la valeur x + y, car rand()(généralement) retournera un nombre beaucoup, beaucoup plus grand que ce x + yqui RAND_MAXest généralement un nombre assez grand. 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 choses est biaisé loin de générer du bruit blanc. Si vous voulez du bruit blanc, réglez simplement chaque pixel surrand(). Si vous souhaitez un joli modèle comme celui que vous avez ci-dessus, mais avec un peu d'aléatoire ici et là, continuez à utiliser le code que vous avez écrit.

De plus, comme @ pm100 l'a noté dans les commentaires, la randfonction ne renvoie pas de nombres vraiment aléatoires et utilise à la place une fonction pseudo-aléatoire pour produire ses valeurs. L'implémentation par défaut de randsur de nombreux systèmes utilise un type de générateur de nombres pseudo-aléatoires appelé générateur congruentiel linéaire qui produit des nombres qui, dans de courtes rafales, peuvent apparaître aléatoires, mais qui sont décidément non aléatoires en pratique. Par exemple, voici une animation de Wikipedia montrant comment des points aléatoires dans l'espace choisis avec un générateur congruentiel linéaire finissent par tomber dans un nombre fixe d'hyperplans:

L'image

Si vous remplacez les coordonnées x, y et z par les coordonnées R, G et B, cela ressemble remarquablement à la sortie produite par votre programme. Je soupçonne que ce n'est probablement pas le problème principal ici, car l'autre aspect mentionné ci-dessus sera probablement beaucoup plus prononcé.

Si vous recherchez des nombres aléatoires de meilleure qualité, vous devrez utiliser une source aléatoire de meilleure qualité. En C, vous pouvez envisager de lire des octets depuis /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.

templatetypedef
la source