La librería dart:math es la herramienta estándar para realizar cualquier cálculo que vaya más allá de la aritmética básica. Provee un conjunto de constantes matemáticas (como pi o e), funciones trigonométricas, potencias, raíces y la clase Random para generar valores al azar.
Este conjunto de funciones funciona de forma directa sobre tipos numéricos, aprovechando las implementaciones de bajo nivel para asegurar precisión. Cuando trabajas con la clase Random, el sistema utiliza un generador pseudoaleatorio; esto significa que los números no son “realmente” aleatorios, sino el resultado de un algoritmo matemático complejo. Si inicializas el generador con una semilla (seed) fija mediante Random(seed), el algoritmo producirá siempre la misma secuencia de números, lo que es fundamental para que tus pruebas unitarias sean reproducibles. Por otro lado, Random.secure() no usa un algoritmo predecible, sino que consulta la fuente de entropía del sistema operativo para obtener aleatoriedad real, siendo ideal para seguridad pero más lento de ejecutar.
Debes usar Random() para la mayoría de las tareas de lógica de juegos o simulaciones donde la velocidad sea prioridad. Usa Random(seed) específicamente en tus tests para asegurar que un error se repita exactamente igual cada vez que ejecutes la suite. Usa Random.secure() exclusivamente cuando estés lidiando con datos sensibles, como tokens de sesión o contraseñas. Si usas un generador pseudoaleatorio para tareas de seguridad, un atacante con suficiente capacidad de cómputo podría predecir tus próximos “números aleatorios” y comprometer el sistema.
import 'dart:math';
void main() {
// 1. Constantes y funciones de redondeo
const double radio = 5.46;
final piValue = pi; // Constante pi
final area = pi * pow(radio, 2); // pow(base, exponente)
print('--- Matemáticas ---');
print('Área del círculo: ${area.toStringAsFixed(2)}');
print('Radio redondeado: ${radio.round()}'); // Al entero más cercano (6)
print('Radio hacia arriba: ${radio.ceil()}'); // Al siguiente entero (6)
print('Radio hacia abajo: ${radio.floor()}'); // Al entero inferior (5)
print('Radio truncado: ${radio.truncate()}'); // Elimina decimales (5)
print('Valor absoluto: ${(-10).abs()}'); // 10
// 2. Comparaciones y límites
final mayor = max(10, 25);
final menor = min(10, 25);
print('\n--- Comparaciones ---');
print('El máximo es: $mayor, el mínimo es: $menor');
// 3. Aleatoriedad con Random (Pseudoaleatoria)
final random = Random();
final decimal = random.nextDouble(); // [0.0, 1.0)
final dado = random.nextInt(6) + 1; // [1, 6]
final esCritico = random.nextBool(); // true o false
print('\n--- Aleatoriedad (Random) ---');
print('Decimal: $decimal');
print('Dado: $dado');
print('¿Éxito crítico?: $esCritico');
// 4. Reproducibilidad con Seed
final randomConSemilla = Random(42);
print('\n--- Reproducibilidad (Seed: 42) ---');
// Estos valores serán idénticos cada vez que ejecutes el programa
print('Número 1: ${randomConSemilla.nextInt(100)}');
print('Número 2: ${randomConSemilla.nextInt(100)}');
// 5. Seguridad criptográfica
final secure = Random.secure();
print('\n--- Seguridad ---');
print('Token seguro: ${secure.nextInt(1000000)}');
}
Desglose del ejemplo
Al observar el uso de pi y pow(radio, 2), verás que estamos realizando cálculos de geometría básica. El método pow devuelve un num, por lo que es compatible con cualquier tipo de número. Cuando aplicamos métodos como round(), ceil(), floor() o truncate() sobre la variable radio, estamos transformando un double en un entero, pero cada uno con una lógica distinta: round busca la cercanía, mientras que floor y truncate simplemente descienden en la recta numérica.
En la sección de aleatoriedad, random.nextInt(6) + 1 es un patrón común. nextInt(max) devuelve un valor desde 0 hasta max - 1. Para simular un dado de 6 caras, necesitamos que el rango sea de 1 a 6, por lo que desplazamos el resultado sumando 1.
Fíjate en la sección de reproducibilidad. Al instanciar Random(42), estamos fijando el punto de partida del algoritmo. Si ejecutas este código diez veces, los dos primeros números de randomConSemilla serán siempre exactamente los mismos. Esto es vital en entornos de desarrollo para depurar lógicas que dependen del azar. Finalmente, Random.secure() se utiliza para generar el “Token seguro”, un valor que no puede ser predicho mediante ingeniería inversa del estado anterior del generador.
El error frecuente
Si necesitas generar un código de verificación para un proceso de autenticación, nunca uses Random().
// MAL: Predictible y peligroso final code = Random().nextInt(1000000);
Aunque parezca aleatorio a simple vista, si un atacante logra determinar en qué momento se inicializó el generador (por ejemplo, mediante la marca de tiempo del sistema), podría predecir todos los códigos generados posteriormente. Utiliza siempre Random.secure() para cualquier lógica de seguridad.
N° 114