¿Qué son los números pseudoaleatorios?
Categoría: Computadora/Red
Análisis:
Es posible que hayas discutido muchas veces cómo se generan los números aleatorios en las computadoras. En este artículo, exploraré esto. tema con más profundidad y explicar mi comprensión del tema.
Lo primero que hay que aclarar es que las computadoras no generan números absolutamente aleatorios. Las computadoras solo pueden generar "números pseudoaleatorios". De hecho, los números absolutamente aleatorios son simplemente números aleatorios ideales. No importa cómo se desarrolle la computadora, no generará una cadena de números absolutamente aleatorios. Las computadoras sólo pueden generar números relativamente aleatorios, es decir, números pseudoaleatorios.
Los números pseudoaleatorios no son números pseudoaleatorios. Aquí "pseudo" significa regular, es decir, los números pseudoaleatorios generados por las computadoras son tanto aleatorios como regulares. ¿Cómo entenderlo? Los números pseudoaleatorios generados a veces siguen ciertas reglas y, a veces, no siguen ninguna regla; Por ejemplo, "No hay dos hojas con la misma forma en el mundo". Esto solo apunta a las características de las cosas, es decir, al azar, pero las hojas de cada árbol tienen formas similares, que es la naturaleza de las cosas, eso es. es decir, las reglas. Desde esta perspectiva, probablemente aceptará el hecho de que las computadoras sólo pueden generar números pseudoaleatorios pero no pueden generar números absolutamente aleatorios.
Entonces, ¿cómo se generan los números aleatorios en las computadoras? Se podría decir que los números aleatorios se generan mediante una "semilla aleatoria". Sí, una semilla aleatoria es un número que se utiliza para generar números aleatorios. En una computadora, dicha "semilla aleatoria" es un número entero sin signo. Entonces, ¿de dónde vienen las semillas aleatorias?
Mire un programa en C de este tipo a continuación:
rand01.c
#include
static unsigned int RAND_SEED;
unsigned int random(void)
{
RAND_SEED=(RAND_SEED*123+59)%65536;
return(RAND_SEED); p >
}
void random_start(void)
{
int temp[2];
moveata(0x0040, 0x006c ,FP_SEG(temp),FP_OFF(temp),4);
RAND_SEED=temp[0];
}
main()
{
unsigned int i,n;
random_start();
for(i=0;i<10;i++) p >
printf("%u\t",random());
printf("\n");
}
Esto El programa (rand01.c) explica completamente el proceso de generación de números aleatorios:
Primero, el programa principal llama al método random_start(). Estoy muy interesado en esta oración en el método random_start(): p>
movingata(0x0040,0x006c,FP_SEG(temp),FP_OFF(temp),4);
Esta función se utiliza para mover datos de la memoria, donde FP_SEG (puntero lejano al segmento) es tomar la dirección del segmento de la matriz temporal La función, FP_OFF (puntero lejano al desplazamiento) es una función que toma la dirección relativa de la matriz temporal. La función de la función moveata es colocar la palabra doble ubicada en el almacenamiento 0040:006CH. unidad en las dos unidades de almacenamiento declaradas en la temperatura de la matriz. De esta manera, se puede enviar un número de 16 bits en 0040:006CH a RAND_SEED a través de la matriz temporal.
Aleatorio se utiliza para calcular un número aleatorio basado en el valor de la semilla aleatoria RAND_SEED Esta oración:
RAND_SEED=(RAND_SEED*123+59)%65536;
es un método utilizado para calcular números aleatorios. Los métodos de cálculo de números aleatorios son diferentes en diferentes computadoras, incluso en diferentes sistemas operativos instalados en la misma computadora. Lo probé en Linux y Windows respectivamente. La misma semilla aleatoria genera diferentes números aleatorios en estos dos sistemas operativos, lo que demuestra que sus métodos de cálculo son diferentes.
Ahora entendemos dónde se obtiene la semilla aleatoria y sabemos cómo se calcula el número aleatorio a partir de la semilla aleatoria. Entonces, ¿por qué debería tomarse la semilla aleatoria en 0040:006CH en la memoria? ¿Qué se almacena en 0040:006CH?
Las personas que han estudiado el curso "Principios de composición informática y tecnología de interfaz" pueden recordar que el temporizador/contador Intel 8253 se utiliza al programar el programa de servicio de interrupción del reloj ROM BIOS. Es similar a la interrupción Intel 8259. La comunicación permite que se ejecute el programa de servicio de interrupción. Las 18,2 interrupciones generadas por la placa base por segundo son generadas por el procesador que controla el chip de interrupción en función del valor del temporizador/contador. Existe un temporizador/contador en la placa base de nuestra computadora para calcular la hora actual del sistema. Cada ciclo de señal de reloj aumentará el contador en uno. Así es, está en 0040:006CH en la memoria. De hecho, este espacio de memoria se define así:
TIMER_LOW DW ? ; la dirección es 0040:006EH
TIMER_OFT DB ?; la dirección es 0040:0070H
En el programa de servicio de interrupción del reloj, siempre que TIMER_LOW se llene, en este momento, el contador también se llena, el valor del contador se restablece a cero, es decir, el binario de 16 bits en TIMER_LOW se restablece a cero y TIMER_HIGH aumenta en uno.
movedata(0x0040,0x006c,FP_SEG(temp),FP_OFF(temp),4);
en rand01.c son exactamente los dos números binarios de 16 bits TIMER_LOW y TIMER_HIGH. en la matriz temporal y luego enviarlo a RAND_SEED, obteniendo así la "semilla aleatoria".
Ahora bien, una cosa que sí es segura es que la semilla aleatoria proviene del reloj del sistema, para ser precisos, del valor de conteo en la memoria del temporizador/contador de la placa base del ordenador. De esta manera, resumimos el análisis anterior y discutimos la aplicación de estas conclusiones en el programa:
1. Los números aleatorios son valores calculados por semillas aleatorias según ciertos métodos de cálculo. Por lo tanto, siempre que el método de cálculo sea cierto y la semilla aleatoria sea cierta, los números aleatorios generados no cambiarán.
Mire el siguiente programa C++:
rand02.cpp
#include
#include
usando espacio de nombres std;
int main()
{
unsigned int seed=5;
srand(seed);
unsigned int r=rand();
cout< Nota del editor: puede haber un error en el código
}
En el mismo entorno de plataforma , Después de compilar y generar el exe, cada vez que lo ejecuta, los números aleatorios que se muestran son los mismos. Esto se debe a que en el mismo entorno de plataforma de compilación, el método de cálculo para generar números aleatorios a partir de semillas aleatorias es el mismo. Además, las semillas aleatorias son las mismas, por lo que los números aleatorios generados son los mismos.
2. Siempre que el usuario o un tercero no establezca una semilla aleatoria, de forma predeterminada la semilla aleatoria proviene del reloj del sistema (es decir, el valor del temporizador/contador) p>
Mire el siguiente programa C++:
rand03.cpp
#include
#include
usando el espacio de nombres std ;
int main()
{
srand((unsigned)time(NULL));
unsigned int r=rand ();
cout< return 0;
}
Aquí, si el usuario y otros programas no establecen una semilla aleatoria, el valor del sistema El temporizador/contador se utiliza como semilla aleatoria. Por lo tanto, en el mismo entorno de plataforma, después de compilar y generar el exe, los números aleatorios que se muestran serán números pseudoaleatorios cada vez que lo ejecute, es decir, los resultados mostrados serán diferentes. cada vez que lo ejecutas.
3. Sugerencia: si desea generar una secuencia de números aleatorios en un programa, debe configurar la semilla aleatoria al menos una vez antes de generar números aleatorios.
Considere el siguiente programa C++ para generar una cadena aleatoria:
rand04.cpp
#include
#include
usando el espacio de nombres std;
int main()
{
int rNum,m=20;
char *ch =new char[m];
for ( int i = 0; i Como puede ver, la semilla aleatoria se establecerá varias veces en el programa con el bucle for
srand ((unsigned)time(NULL));
rNum=1+(int)((rand()/(double)RAND_MAX)*36); Buscar valor aleatorio
switch); (rNum){
caso 1: ch[i]='a';
break ;
caso 2: ch[i]='b' ;
ruptura ;
caso 3: ch[i]='c';
ruptura ;
caso 4: ch[ i]='d';
ruptura ;
caso 5: ch[i]='e';
ruptura ;
caso 6: ch[i]='f';
ruptura ;
caso 7: ch[i]='g';
ruptura ;
caso 8: ch[i]='h';
descanso ;
caso 9: ch[i]='i';
ruptura ;
caso 10: ch[i]='j';
ruptura ;
caso 11: ch[i]=' k';
ruptura ;
caso 12: ch[i]='l';
ruptura ;
caso 13: ch[i]='m';
ruptura ;
caso 14: ch[i]='n';
ruptura ;
caso 15: ch[i]='o';
descanso ;
caso 16: ch[i]='p';
break ;
caso 17: ch[i]='q';
break ;
caso 18: ch[i]='r';
ruptura ;
caso 19: ch[i]='s';
ruptura ;
caso 20: ch[i] ='t';
ruptura ;
caso 21: ch[i]='u';
ruptura ;
caso 22: ch[i]='v';
ruptura ;
caso 23: ch[i]='w';
ruptura ; p>
p>
caso 24: ch[i]='x';
break ;
caso 25: ch[i]='y';
ruptura ;
caso 26: ch[i]='z';
ruptura ;
<p> caso 27:ch[i]='0';
ruptura;
caso 28:ch[i]='1';
ruptura ;
caso 29:ch[i]='2';
descanso;
caso 30:ch[i]='3'; p> p>
ruptura;
caso 31:ch[i]='4';
ruptura;
caso 32:ch[i ]= '5';
ruptura;
caso 33:ch[i]='6';
ruptura;
caso 34 :ch[i]='7';
descanso;
caso 35:ch[i]='8';
descanso;< /p >
caso 36:ch[i]='9';
break;
}fin del cambio
cout< }fin de for loop
cout< return 0;
}
Cada carácter de la cadena aleatoria mostrada por el resultado en ejecución es el mismo, lo que significa que los caracteres generados La secuencia no es aleatoria, por lo que debemos mover srand((unsigned)time(NULL)); del bucle for y colocarlo delante de la declaración for. Esto puede generar una secuencia de caracteres aleatoria, y la secuencia de caracteres generada lo será. será diferente cada vez que se ejecute (jaja, puede ser lo mismo, pero la posibilidad de que esto suceda es muy pequeña).
Si cambia srand((unsigned)time(NULL)); a srand(2) de esta manera, aunque la secuencia de caracteres generada en una ejecución es aleatoria, la secuencia de caracteres generada en cada una. La ejecución será aleatoria. Las cadenas de secuencia de caracteres son las mismas. Lo mismo ocurre con la eliminación de la frase srand del programa.
Además, puede encontrarse con esta situación al programar usando el control del temporizador, encontrará que un conjunto de números aleatorios generados con el mismo intervalo de tiempo aparecerán regularmente y el evento de comando generado por el usuario. clave El conjunto generado de números aleatorios parece ser relativamente aleatorio ¿Por qué? Según nuestro análisis anterior, puede descubrir rápidamente la respuesta. Esto se debe a que el temporizador es un control que controla con precisión el intervalo de tiempo mediante el contador del reloj de la computadora. El intervalo de tiempo es el mismo y la diferencia entre los valores antes y después del contador es la misma. El valor es lineal, por lo que la semilla aleatoria es lineal, los números aleatorios generados también son regulares. Los números aleatorios generados por los eventos de pulsación de teclas del usuario son de hecho más aleatorios, porque los eventos son causados por pulsaciones de teclas humanas y las personas no pueden garantizar intervalos de tiempo estrictos para pulsar las teclas. Incluso si se hacen estrictamente, es imposible hacerlo con total precisión. Siempre que los intervalos de tiempo sean diferentes en un microsegundo, la diferencia entre los valores antes y después del contador es diferente. Los cambios en la semilla aleatoria pierden el patrón lineal y los números aleatorios generados se vuelven aún más irregulares. el conjunto de números aleatorios generados de esta manera es más aleatorio. Esto me recuerda a los programas de lotería en varias fiestas nocturnas, si la gente está acostumbrada a presionar botones para generar espectadores afortunados, el principio de aleatoriedad se cumplirá mejor y los resultados serán más justos.
Finalmente, resumo dos puntos clave:
1. El número pseudoaleatorio de la computadora es un valor calculado por una semilla aleatoria de acuerdo con un determinado método de cálculo. Por lo tanto, siempre que el método de cálculo sea cierto y la semilla aleatoria sea cierta, el número aleatorio generado es fijo.
2. Siempre que el usuario o un tercero no establezca la semilla aleatoria, la semilla aleatoria proviene del reloj del sistema de forma predeterminada.