¿Por qué obtengo este patrón de color particular cuando uso rand ()?

170

Traté de crear un archivo de imagen, así:

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

Esperaba obtener algo al azar (ruido blanco). Sin embargo, el resultado es interesante:

¿Sabes por qué?


Editar

Ahora, está claro que no tiene nada que ver rand().

Prueba también este código:

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

Pequeño pony
fuente
26
cos rand isnt rand - bonita demostración de ello. Es 100% determinista
pm100
50
@ pm100: Como la respuesta de bames53 explica muy bien, obtendría el mismo patrón incluso si usara un generador de números aleatorios perfecto.
TonyK
13
Disculpe una pregunta: dado que está utilizando las coordenadas xey para calcular los valores de píxeles, ¿por qué esperaría que esos valores sean independientes de las coordenadas? Si la imagen también se ve uniformemente al azar, eso es lo que necesitarías, ¿verdad?
Thomas Padron-McCarthy
15
Lecciones aprendidas: Hacer cosas al azar con números aleatorios no produce resultados aleatorios :)
Hagen von Eitzen

Respuestas:

355

Inicialmente iba a tener la misma respuesta que todos los demás y atribuir esto a los problemas rand(). Sin embargo, pensé mejor en hacerlo y en su lugar analicé la distribución que sus matemáticas realmente están produciendo.

TL; DR: El patrón que ve no tiene nada que ver con el generador de números aleatorios subyacente y, en cambio, se debe simplemente a la forma en que su programa está manipulando los números.

Me atendré a tu función azul ya que todas son similares.

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

Cada valor de píxel se selecciona de entre una de las tres funciones: (x + y) % rand(), (x - y) % rand()y rand();

Veamos las imágenes producidas por cada uno de estos solo.

  • rand()

Esto es lo que esperarías, solo ruido. Llame a esto "Imagen C"

ingrese la descripción de la imagen aquí


  • (x + y) % rand()

Aquí está agregando las coordenadas de píxeles juntas y eliminando el resto de la división por un número aleatorio. Si la imagen es 1024x1024, entonces la suma está en el rango [0-2046]. El número aleatorio por el que está buceando está en el rango [0, RAND_MAX], donde RAND_MAX es al menos 32k y en algunos sistemas es de 2 mil millones. En otras palabras, en el mejor de los casos hay una probabilidad de 1 en 16 de que el resto no sea solo (x + y). Entonces, en su mayor parte, esta función solo producirá un gradiente de azul creciente hacia la dirección + x + y.

Sin embargo, solo está utilizando los 8 bits más bajos, porque devuelve un uint8_t, por lo que tendrá franjas de gradientes de 256 píxeles de ancho.

Llame a esto "Imagen A"

ingrese la descripción de la imagen aquí


  • (x - y) % rand()

Aquí haces algo similar, pero con resta. Siempre que x sea mayor que y, tendrá algo similar a la imagen anterior. Pero donde y es mayor, el resultado es un número muy grande porque xy yno tiene signo (los resultados negativos se envuelven en la parte superior del rango del tipo sin signo), y luego se % rand()activa y realmente se obtiene ruido.

Llame a esto "Imagen B"

ingrese la descripción de la imagen aquí

Cada píxel en su imagen final se toma de una de estas tres imágenes usando las funciones rand() % 2y ((x * y % 1024) % rand()) % 2. El primero de estos puede leerse como elegir con un 50% de probabilidad (ignorando los problemas con rand()y sus bits de bajo orden).

Aquí hay un primer plano de dónde rand() % 2es verdadero (píxeles blancos) para que se seleccione la Imagen A.

ingrese la descripción de la imagen aquí

La segunda función ((x * y % 1024) % rand()) % 2nuevamente tiene el problema de que rand()generalmente es mayor que la cosa que está dividiendo (x * y % 1024), que es como máximo 1023. Luego (x*y%1024)%2no produce 0 y 1 con la misma frecuencia. Cualquier número impar multiplicado por cualquier número par es par. Cualquier número par multiplicado por cualquier número par también es par. Solo un número impar multiplicado por un número impar es impar, y así sucesivamente los %2valores que son pares las tres cuartas partes del tiempo producirán 0 tres cuartos de las veces.

Aquí hay un primer plano de dónde ((x * y % 1024) % rand()) % 2es cierto para que se pueda seleccionar la Imagen B. Está seleccionando exactamente dónde ambas coordenadas son impares.

ingrese la descripción de la imagen aquí

Y aquí hay un primer plano de dónde se podría seleccionar la imagen C:

ingrese la descripción de la imagen aquí

Finalmente, combinando las condiciones aquí es donde se selecciona la Imagen B:

ingrese la descripción de la imagen aquí

Y donde se selecciona la imagen C:

ingrese la descripción de la imagen aquí

La combinación resultante se puede leer como:

Con una probabilidad del 50%, use el píxel de la Imagen A. El resto del tiempo elija entre la Imagen B y la Imagen C, B donde ambas coordenadas son impares, C donde cualquiera de las dos es par.

Finalmente, dado que está haciendo lo mismo para tres colores diferentes, pero con diferentes orientaciones, los patrones están orientados de manera diferente en cada color y producen las tiras cruzadas o el patrón de cuadrícula que está viendo.

bames53
fuente
45

Muchos de los cálculos que está haciendo en su código no conducirán a valores verdaderamente aleatorios. Esas líneas afiladas que estás viendo corresponden a lugares donde los valores relativos de tus coordenadas x e y se intercambian entre sí, y cuando eso sucede estás usando fórmulas fundamentalmente diferentes. Por ejemplo, la informática (x + y) % rand()generalmente le devolverá el valor x + y, ya rand()que (generalmente) devolverá un número mucho, mucho más grande que el x + ydado, que RAND_MAXgeneralmente es un número bastante grande. En ese sentido, no debe esperar recuperar el ruido blanco, ya que el algoritmo que está utilizando para generar cosas está predispuesto a generar ruido blanco. Si desea ruido blanco, simplemente configure cada píxel enrand(). Si desea un patrón agradable como el que tiene arriba, pero con un poco de aleatoriedad arrojado aquí y allá, siga usando el código que ha escrito.

Además, como ha señalado @ pm100 en los comentarios, la randfunción no devuelve números verdaderamente aleatorios, y en su lugar utiliza una función pseudoaleatoria para producir sus valores. La implementación predeterminada de randmuchos sistemas utiliza un tipo de generador de números pseudoaleatorio llamado generador congruencial lineal que produce números que en ráfagas cortas pueden parecer aleatorios, pero que son decididamente no aleatorios en la práctica. Por ejemplo, aquí hay una animación de Wikipedia que muestra cómo los puntos aleatorios en el espacio elegidos con un generador congruencial lineal terminan cayendo en un número fijo de hiperplanos:

La imagen

Si reemplaza las coordenadas x, y y z con las coordenadas R, G y B, esto se ve notablemente similar a la salida producida por su programa. Sospecho que este no es el problema principal aquí, ya que el otro aspecto mencionado anteriormente probablemente será mucho más pronunciado.

Si está buscando números aleatorios de mayor calidad, deberá utilizar una fuente aleatoria de mayor calidad. En C, podría considerar leer bytes de /dev/urandom/(en un sistema similar a Linux), que proporciona valores aleatorios bastante uniformes. C ++ ahora tiene una serie de buenas primitivas de generación de números aleatorios en sus bibliotecas estándar, si está disponible para usted.

templatetypedef
fuente