Ir al contenido

El Fin del Determinismo: Generando Números Aleatorios con Cúbits y Q#

17 de marzo de 2026 por
El Fin del Determinismo: Generando Números Aleatorios con Cúbits y Q#
Gerardo


En el desarrollo de software clásico, el azar no existe realmente. Cuando pides un número aleatorio en Python o C#, obtienes un número pseudoaleatorio: el resultado de una fórmula matemática determinista. Si conoces la "semilla" (seed), puedes predecir todos los números siguientes.

La computación cuántica cambia esto fundamentalmente. Al medir la superposición de partículas subatómicas, podemos obtener números verdaderamente aleatorios, imposibles de predecir por la naturaleza misma de la física.

En este artículo, desglosaremos dos programas en Q# (el lenguaje de programación cuántica de Microsoft) para entender cómo pasar de un simple "cara o cruz" cuántico a un generador de números enteros funcional.


Parte 1: El Átomo del Azar (El Bit Cuántico)

Para entender el sistema completo, primero debemos entender la unidad fundamental. El siguiente código (Main.qs) tiene un único propósito: generar un solo bit (0 o 1) con una probabilidad perfecta del 50/50.

El Código Fundamental
operation GenerateRandomBit() : Result {
    // 1. Asignación: Pedimos un cúbit limpio al simulador o hardware.
    use q = Qubit();

    // 2. Superposición: La puerta Hadamard pone el cúbit en estado |0> + |1>
    H(q);

    // 3. Medición: El cúbit colapsa aleatoriamente a 0 o 1
    let result = M(q);

    // 4. Limpieza: Devolvemos el cúbit al estado base |0>
    Reset(q);

    // 5. Retorno
    return result;
}

Análisis Profundo:

La línea crítica aquí es H(q);. En la computación clásica, un bit es 0 o 1. La puerta Hadamard (H) toma nuestro cúbit (que empieza en 0) y lo coloca en una superposición lineal.

Matemáticamente, el estado del cúbit pasa a ser: (|0⟩ + |1⟩) / √2

Esto significa que el cúbit coexiste en ambos estados con amplitudes de probabilidad idénticas. Al ejecutar M(q) (Medición), forzamos al universo a "elegir" un estado. Dado que las amplitudes eran iguales, la probabilidad de obtener un 0 o un 1 es exactamente 50%. No hay algoritmo detrás de esta elección; es comportamiento físico puro.


Parte 2: Orquestando el Caos (QRNG.qs)

Generar ceros y unos es científicamente fascinante, pero poco práctico para el usuario final. Para crear una herramienta útil, necesitamos generar números enteros dentro de un rango específico (ej. entre 50 y 100).

Aquí es donde entra el segundo código. Este script completo (QRNG.qs) actúa como un "puente híbrido": utiliza lógica clásica (bucles, matemáticas, recursividad) para orquestar el recurso cuántico.

El Código Completo

Este es el archivo completo que debes ejecutar. Observa cómo incluye las importaciones, el punto de entrada Main, la lógica del rango y la generación del bit, todo en uno:

import Std.Convert.*;
import Std.Math.*;

operation Main() : Int {
    let min = 50;
    let max = 100;
    Message($"Generating a random number between {min} and {max}: ");

    // Generate random number in the 0..max range.
    return GenerateRandomNumberInRange(min, max);
}

/// Generates a random number between 0 and `max`.
operation GenerateRandomNumberInRange(min : Int, max : Int) : Int {
    // Determine the number of bits needed to represent `max` and store it
    // in the `nBits` variable. Then generate `nBits` random bits which will
    // represent the generated random number.
    mutable bits = [];
    let nBits = BitSizeI(max);
    for idxBit in 1..nBits {
        set bits += [GenerateRandomBit()];
    }
    let sample = ResultArrayAsInt(bits);

    // Return random number if it's within the requested range.
    // Generate it again if it's outside the range.
    return (sample > max or sample < min) ? GenerateRandomNumberInRange(min, max) | sample;
}

operation GenerateRandomBit() : Result {
    // Allocate a qubit.
    use q = Qubit();

    // Set the qubit into superposition of 0 and 1 using the Hadamard operation
    H(q);

    // Measure the qubit value using the `M` operation, and store the
    // measurement value in the `result` variable.
    let result = M(q);

    // Reset qubit to the |0〉 state.
    Reset(q);

    // Return the result of the measurement.
    return result;
}

Análisis Detallado de la Lógica

Este algoritmo es más complejo porque debe traducir el lenguaje cuántico (qubits) al lenguaje humano (enteros). Analicemos los 4 pasos clave:

1. ¿Cuántos cúbits necesitamos? (BitSizeI)

Si queremos un número máximo de 100, no podemos pedir bits al azar infinitamente.

  • La función BitSizeI(max) calcula cuántos bits se requieren para representar el número max.
  • Para el 100, necesitamos 7 bits, ya que 2^6 = 64 (muy poco) y 2^7 = 128 (suficiente).
2. Llenando el "Cubo" de Bits

El bucle for llama a nuestra operación cuántica básica (GenerateRandomBit) 7 veces. Esto nos da un arreglo crudo, por ejemplo: [0, 1, 1, 0, 0, 1, 0]. Cada uno de estos bits es verdaderamente aleatorio.

3. Conversión (ResultArrayAsInt)

Esta función de la librería estándar traduce el arreglo de resultados de medición a un número entero clásico que nuestra computadora puede entender.

  • Ejemplo: [1, 0, 1] se convierte en el número 5.
4. El Filtro de Calidad (Muestreo de Rechazo)

Esta es la parte más inteligente del código, ubicada en la sentencia return.

  • El Problema: Con 7 bits, podemos generar números desde 0 hasta 127. Sin embargo, nuestro rango deseado es 50-100. ¿Qué pasa si el azar cuántico nos da el número 120? ¿O el número 5?
  • ¿Por qué no usar Módulo (%)?: Podríamos hacer 120 % 100, pero eso introduciría un "sesgo". Los números bajos tendrían ligeramente más probabilidad de salir que los altos.
  • La Solución (Rejection Sampling): Si el número generado (sample) es mayor que max o menor que min, lo descartamos por completo y llamamos a la función GenerateRandomNumberInRange de nuevo (recursividad).

    • Esto garantiza que cada número dentro del rango 50-100 tenga exactamente la misma probabilidad de ser elegido, manteniendo la pureza de la aleatoriedad cuántica ("Distribución Uniforme").

Ejecución: Simulador Local vs. Hardware Real

Para probar este código, he utilizado Visual Studio Code ejecutando un Simulador Cuántico.

Es importante aclarar qué significa esto:

  1. ¿Qué estoy usando? Estoy utilizando el "Full State Simulator" que viene integrado con el Quantum Development Kit (QDK). Este simulador utiliza la CPU de mi computadora para imitar matemáticamente las leyes de la física cuántica. No hay partículas subatómicas reales involucradas en mi laptop, pero la lógica es idéntica.
  2. ¿Por qué no usar una computadora cuántica real? Aunque es totalmente posible enviar este mismo código a hardware real (QPUs) de IonQ o Rigetti a través de un Azure Quantum Workspace, hacerlo suele tener un costo o tiempos de espera en cola.
  3. La ventaja del código: Lo brillante de Q# es que este código es agnóstico. Funciona perfectamente en mi simulador local gratuito para pruebas, pero si quisiera certificación de aleatoriedad física real, solo tendría que cambiar el "target" en Azure sin modificar una sola línea de código.


Resultado de la Ejecución

Al ejecutar el script, el simulador procesó las instrucciones Q# y filtró los resultados hasta obtener un número válido dentro del rango [50-100].

 

Aplicaciones en el Mundo Real

¿Por qué invertir en hardware cuántico para generar números aleatorios? La respuesta está en la seguridad y la precisión científica.

1. Ciberseguridad (Quantum Key Distribution - QKD)

La seguridad bancaria depende de claves de encriptación. Si un hacker descubre el patrón del generador de números pseudoaleatorios, puede predecir las claves. Un QRNG crea claves basadas en física, no en matemáticas, haciéndolas imposibles de predecir.

2. Simulaciones de Monte Carlo

En finanzas y ciencia, se simulan millones de escenarios. Los generadores clásicos pueden introducir patrones repetitivos sutiles en simulaciones largas. El código cuántico garantiza independencia total entre cada simulación, mejorando la precisión en modelos de riesgo o diseño de fármacos.

3. Juegos y Loterías Auditables

Un casino online o una lotería estatal podría usar este backend para probar matemáticamente a los auditores que el resultado fue producto de un proceso natural y no de un algoritmo manipulado.

4. Inteligencia Artificial

Inicializar redes neuronales con aleatoriedad pura puede ayudar a los modelos de aprendizaje profundo a explorar el "espacio de soluciones" de manera más efectiva, evitando atascarse en mínimos locales durante el entrenamiento.


Conclusión

Lo que hemos visto en Main.qs y QRNG.qs no son solo scripts; son un puente entre la física teórica y la ingeniería de software. Hemos tomado el comportamiento impredecible de una partícula subatómica y lo hemos domesticado mediante lógica de programación para producir datos útiles y seguros.

Puedes aprender mas en Microsoft Learn: https://learn.microsoft.com/es-es/training/modules/qsharp-create-first-quantum-development-kit/