Creo que el concepto de números / valores aleatorios te resulta familiar: son aleatorios. A veces, en un dispositivo casero, se necesita una variable aleatoria, por ejemplo, para algún tipo de juego o efectos de iluminación. Una fuente de software de números aleatorios reales no es fácil de organizar, a diferencia de una de hardware. Un dado cursi o una máquina de lotería son fuentes bastante buenas de números aleatorios. Un microcontrolador, por cierto, no puede generar números aleatorios, porque es un dispositivo informático preciso, no puede haber accidentes. ¿Qué hacer? Usar algo como números pseudoaleatorios. ¿En qué se diferencian de los aleatorios? Por su naturaleza. Un número pseudoaleatorio se obtiene mediante varias operaciones matemáticas con una semilla, es decir, al tener una semilla, podemos generar un montón de otros números a partir de ella. Pero aquí hay dos problemas:
- Después de una cierta cantidad de números (miles, quizás miles de millones y quizás más), comenzará a repetirse una serie de números pseudoaleatorios generados. Para nuestros propósitos, esto no da tanto miedo, no puedes pensar en ello.
- El generador de números pseudoaleatorios necesita una semilla aleatoria. Esto es realmente un problema. Pero nada, nuestro microcontrolador también tiene hardware para ello.
Arduino y números aleatorios.
Arduino tiene un par de funciones listas para usar para trabajar con números pseudoaleatorios, echémosle un vistazo:
- random(max) ; – devuelve un número pseudoaleatorio en el rango de 0 a (máx – 1). max toma unsigned long, es decir, de 0 a 4294967295
- random(min, max) ; – devuelve un número pseudoaleatorio en el rango entre min y (máx – 1). max será unsigned long, es decir, de 0 a 4294967295
- randomSeed(value) ; – da al generador de números pseudoaleatorios una semilla, un nuevo punto de referencia para la generación. value – cualquier número de tipo unsigned long, entonces en Arduino tenemos 2 ^ 32 ( 4 294 967 295 ) de números pseudoaleatorios. ¡Esto definitivamente es suficiente para toda tu vida!
¿Cómo generar números aleatorios correctamente para que la secuencia sea nueva cada vez? Hay opciones:
- Al iniciar el programa, establezca un número aleatorio en randomSeed(value). ¿Cómo hacerlo? Te lo diré abajo.
- Si el dispositivo interactúa de alguna manera con el mundo exterior, o incluso con el usuario, cuando ocurran algunos eventos aleatorios de hardware (presionar un botón, activar un sensor, recibir datos, etc.), cargue randomSeed ‘en el momento actual desde el inicio de el programa, con las funciones millis () o micros (). ¡Una excelente solución por cierto! Lo llamamos cursi randomSeed( micros ()) eso es todo.
Hardware aleatorio.
Recuerde, en la lección sobre pines digitales, dije que si el pin analogico A0 no está conectado en ningún lugar, entonces atrapa todo tipo de interferencia eléctricas «de la nada». Los ruidos son de una naturaleza casi aleatoria, ¡y sería una tontería no usarlos! Veamos qué valores se pueden obtener de un pin analógico no conectado sondeándolo con el habitual analogRead ();
void loop() { Serial.println(analogRead(A0)); delay(100);
¿Sinusoide? Es bastante lógico, hay muchos cables con tensión de red en las paredes, por lo que inducen todo tipo de interferencias en el microcontrolador. ¿Es posible utilizar el valor bruto del pin analógico como «cebo» para randomSeed()? ¡Es necesario! Es decir, al iniciar el dispositivo, hazlo así:
void setup() { // punto de partida para el generador de números aleatorios // A0 no está conectado en ningún lugar randomSeed(analogRead(A0)); }
Pero también hay una opción interesante que te permitirá obtener números mucho más aleatorios para randomSeed(). Hay información de que los dos primeros bits del resultado de analogRead () tienen el mayor ruido. Echemos un vistazo, mostrando varios (300) resultados en un gráfico. Puedes obtener los dos primeros bits así: analogRead ( A0 ) & 0b0000011, o más en breve – analogRead(A0) & 3.
for (int i = 0; i < 300; i++) { Serial.println(analogRead(A0) & 3); }
Parece bastante aleatorio, ¡no se puede rastrear ningún patrón! Pero los valores solo cambian de 0 a 3. Por lo tanto, puede intentar multiplicarlos y sumarlos, así:
unsigned long seed = 0; // 16 раз for (int i = 0; i < 16; i++) { seed *= 4; seed += analogRead(A0) & 3; } // alimenta el generador de randomSeed randomSeed(seed);
Este es el código que recomiendo usar para obtener una semilla aleatoria para el generador de números aleatorios cuando se inicia el programa. Finalmente, veamos cuál es el resultado de esta construcción:
unsigned long seed; for (int i = 0; i < 400; i++) { seed = 1; for (byte j = 0; j < 16; j++) { seed *= 4; seed += analogRead(A0) & 3; } Serial.println(seed); }
Construí un gráfico en Excel para ver más claramente la dispersión del valor aleatorio resultante:
Tenemos una dispersión prácticamente uniforme y una gran cantidad de valores aleatorios obtenidos multiplicando y sumando “ruido”. Úselo con precaución, =).
Valor «Boolean» aleatorio.
A veces necesitas una bandera aleatoria, es decir cierto/falso. Esto se hace de manera muy simple: en la lección sobre condiciones, les dije que bool (booleano) toma True para cualquier valor distinto de cero. Esto se puede usar para obtener un True/False con una probabilidad dada! Simplemente asignamos el resultado de la función random() a una variable booleana, en el que pasamos el número inverso de la probabilidad de recibir falso:
bool rndFlag = random(5);
La variable rndFlag tendrá valor false con una probabilidad de 1/5, es decir, 20%. Si es necesario True con baja probabilidad – usamos el valor invertido:
bool rndFlag = !random(10);
Ahora la variable rndFlag tendrá valor True con una probabilidad del 10%.