Lab 1 — Quantum Random Number Generator
Overview
Classical computers cannot generate true randomness. Functions like Python's random.random() are pseudo-random: they run a deterministic algorithm seeded by some value, so given the seed you can reproduce the entire sequence. That is fine for games but unacceptable for cryptographic keys.
Quantum mechanics gives us randomness that is, as far as physics knows, fundamental and irreducible. In this lab you put each qubit into an equal superposition with a Hadamard gate and measure it. Each measurement yields 0 or 1 with exactly 50% probability, and no algorithm, seed, or amount of computation can predict the outcome. You will turn a register of such bits into a uniformly distributed integer.
Theory
A single qubit starts in the state . The Hadamard gate is defined by
Applying it to produces the equal superposition
The probability of measuring outcome is given by the Born rule, the modulus-squared of the amplitude:
Measurement collapses the superposition to whichever outcome occurred. The randomness is not "hidden information we lack" — under standard quantum mechanics there is no underlying variable that determined the result in advance.
For independent qubits each in superposition, the -bit outcome is uniformly distributed over all values, because the probabilities multiply:
Implementation
We build an -qubit circuit, apply a Hadamard to every qubit, measure all of them, run a single shot, and interpret the resulting bitstring as an integer.
from qiskit import QuantumCircuit, transpile
from qiskit_aer import AerSimulator
def quantum_random_int(n_bits: int, shots: int = 1) -> list[int]:
"""Generate `shots` uniformly random integers in [0, 2**n_bits - 1]."""
qc = QuantumCircuit(n_bits, n_bits)
# Put every qubit into an equal superposition.
for q in range(n_bits):
qc.h(q)
# Measure each qubit into its matching classical bit.
qc.measure(range(n_bits), range(n_bits))
sim = AerSimulator()
compiled = transpile(qc, sim)
# memory=True lets us read each individual shot, not just aggregate counts.
result = sim.run(compiled, shots=shots, memory=True).result()
bitstrings = result.get_memory()
# Qiskit returns bitstrings with qubit 0 as the rightmost character,
# which int(..., 2) interprets correctly as a binary number.
return [int(bits, 2) for bits in bitstrings]
if __name__ == "__main__":
# One 8-bit random number (0..255).
print("Single 8-bit value:", quantum_random_int(8)[0])
# Verify the distribution is uniform by sampling many 3-bit values.
samples = quantum_random_int(3, shots=8000)
from collections import Counter
hist = Counter(samples)
print("3-bit distribution over 8000 shots:")
for value in range(8):
print(f" {value}: {hist.get(value, 0)}")
What each piece does:
QuantumCircuit(n_bits, n_bits)allocatesn_bitsqubits andn_bitsclassical bits to hold the measurement results.- The loop applies
Hto each qubit, creating an -qubit uniform superposition. qc.measure(...)collapses every qubit and records the bit.memory=Trueis essential here: it returns the individual outcome of each shot so we can produce many independent random numbers, rather than only a frequency histogram.int(bits, 2)converts the binary string to a decimal integer.
Run it
A single run prints something different every time — that is the point. A typical session:
Single 8-bit value: 173
3-bit distribution over 8000 shots:
0: 1002
1: 988
2: 1011
3: 994
4: 1023
5: 977
6: 1005
7: 1000
Each of the 8 outcomes should land near 8000 / 8 = 1000, confirming a uniform distribution. The exact value of "Single 8-bit value" will be unpredictable on every execution.
Exercises
- (Beginner) Modify
quantum_random_intto return a random float in by generating a 16-bit integer and dividing by . Verify the mean over many samples is close to . - (Beginner) Build a quantum coin flip that prints
"Heads"or"Tails". Run it 1000 times and report the ratio. - (Intermediate) A fair die has 6 faces, but 3 qubits give 8 outcomes. Implement rejection sampling: generate 3-bit values, reject
6and7, and retry. Confirm the six surviving outcomes are uniform. - (Intermediate) Replace the Hadamard with an rotation. Derive the value of that biases the qubit so , implement it, and verify empirically. (Hint: .)
- (Advanced) Use Qiskit's
Statevectorclass to print the amplitudes of the 3-qubit superposition before measurement, and confirm every amplitude equals .
Further reading
- Qiskit textbook: Single-qubit gates and the Hadamard
- Nielsen & Chuang, Quantum Computation and Quantum Information, Section 1.3 (the Born rule).
- The intermediate roadmap for where to go next.
- Next lab: Quantum Teleportation.