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; */
}
Réponses:
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.
Chaque valeur de pixel est choisi parmi l' une des trois fonctions suivantes:
(x + y) % rand()
,(x - y) % rand()
etrand()
;Regardons les images produites par chacun d'eux seul.
rand()
C'est ce à quoi vous vous attendez, juste du bruit. Appelez cela "Image C"
(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"
(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
x
ety
sont 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"
Chaque pixel de votre image finale est extrait de l'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 avecrand()
et ses bits de poids faible.)Voici un gros plan de l'endroit où
rand() % 2
est vrai (pixels blancs) pour que l'image A soit sélectionnée.La deuxième fonction a
((x * y % 1024) % rand()) % 2
encore 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)%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, 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()) % 2
est vrai pour que l'image B puisse être sélectionnée. Il sélectionne exactement où les deux coordonnées sont impaires.Et voici un gros plan de l'endroit où l'image C pourrait être sélectionnée:
Enfin, en combinant les conditions, voici où l'image B est sélectionnée:
Et là où l'image C est sélectionnée:
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.
la source
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 valeurx + y
, carrand()
(généralement) retournera un nombre beaucoup, beaucoup plus grand que cex + y
quiRAND_MAX
est 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
rand
fonction 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 derand
sur 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: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.la source