Quantum Random Number Generators occupy a special place in the hierarchy of randomness. Unlike pseudo-random number generators (PRNGs) — which are deterministic algorithms that produce sequences that merely look random — a true QRNG extracts entropy from genuinely non-deterministic quantum mechanical events. The most common physical mechanism is the polarization of individual photons: when a photon passes through a 50/50 polarizing beam splitter, quantum mechanics dictates that it will be transmitted or reflected with equal probability, and crucially, no amount of information about the system's prior state can predict the outcome. This is not an engineering limitation; it is a fundamental property of nature described by the Born rule.
The consequences are profound for security-critical applications. A PRNG seeded with a known value will always produce the same stream, making it vulnerable to reconstruction attacks. Hardware RNGs that rely on thermal noise or clock jitter can carry subtle biases or correlations that statistical adversaries exploit over large samples. A properly implemented QRNG with bias correction produces outputs that are, to within measurement precision, perfectly uniform and serially uncorrelated — not because we've been clever with our algorithm, but because the universe provides no other option.
This application simulates a photon-polarization QRNG using the best available hardware entropy source on each platform (BCryptGenRandom on Windows, arc4random on macOS, /dev/urandom on Linux) and applies rigorous post-processing (Von Neumann debiasing or XOR folding) to guarantee the statistical properties a cryptographic consumer expects. It then subjects the output to the NIST SP 800-22 test suite in real time, displaying the results as interactive charts and gauges, giving you immediate scientific feedback on the quality of your generated randomness.
The physical model implemented here is a photon polarization beam splitter QRNG:
Each generated bit represents a single photon encountering a 50/50 polarizing beam splitter. According to the Born rule of quantum mechanics, the probability that the photon is transmitted (output 1) or reflected (output 0) is exactly:
P(0) = |⟨H|ψ⟩|² = 0.5
P(1) = |⟨V|ψ⟩|² = 0.5
where |ψ⟩ is the photon's polarization state and ⟨H|, ⟨V| are the horizontal/vertical projection operators. No hidden variable, no prior knowledge, no adversarial strategy can predict the outcome — this is guaranteed by the completeness of quantum mechanics.
In practice, real hardware suffers from small biases (imperfect beam splitters, detector efficiency asymmetry). This application addresses that with Von Neumann debiasing: raw bits are consumed in pairs. If a pair is (0,1) it outputs 0; if (1,0) it outputs 1; if both bits are equal the pair is discarded. This procedure guarantees that the output probability is exactly 0.5 regardless of the source bias — at the cost of throughput efficiency (typically 50% of raw bits are discarded for a slightly biased source). For applications that don't require perfect debiasing, the XOR fold mode provides much higher throughput by XORing all 64 hardware bits with bit-rotated intermediates.
- CMake ≥ 3.20
- Qt6 (Core, Widgets, Concurrent) — install via Qt Online Installer, Homebrew, or your Linux package manager
- A C++20-capable compiler: GCC ≥ 11, Clang ≥ 13, or MSVC 2022
# Install Qt6 (Ubuntu 22.04+)
sudo apt install qt6-base-dev
# Build
cd qrng_app
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --parallel $(nproc)
./build/qrng# Install Qt6 via Homebrew
brew install qt6
export CMAKE_PREFIX_PATH=$(brew --prefix qt6)
cd qrng_app
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --parallel $(sysctl -n hw.logicalcpu)
open build/qrng.app# Assumes Qt6 installed to C:\Qt\6.x.x\msvc2022_64
cmake -B build -DCMAKE_PREFIX_PATH="C:\Qt\6.x.x\msvc2022_64" -G "Visual Studio 17 2022"
cmake --build build --config Release
.\build\Release\qrng.exeThe main window is divided into four interactive regions:
Toolbar — Source selector, mode selector, batch size and speed sliders, and transport controls (Start/Pause/Stop/Reset/Export). A theme toggle switches between the default dark oscilloscope aesthetic and a light mode.
Bit Stream View (top-left) — Displays the last 256 generated bits as a 16×16 grid of colored squares. Blue squares are 1s; gray squares are 0s. New bits slide in from the right with a smooth animation. Hovering a cell shows its bit index and value; clicking copies the index to the clipboard. Below the grid, a live scrolling hex dump shows the raw bytes in offset format.
Frequency Chart (top-right, upper half) — A scrolling line chart showing the ones-ratio (fraction of 1s) over the last N batches. A green confidence band marks the ±2σ region for a given batch size. A blue line tracks the raw ratio; an orange dashed line tracks the 10-sample moving average. Ideal random output stays within the band.
Run-Length Histogram (top-right, lower half) — A bar chart showing how often runs of each length (1 bit, 2 bits, 3 bits, …) occur. The blue curve overlay is the theoretical geometric distribution for i.i.d. Bernoulli(0.5) bits. Bars are colored green (close to theory), yellow (marginal), or red (significant deviation).
Entropy Gauge (bottom-left) — A circular arc gauge displaying Shannon entropy in bits-per-bit. The arc is red below 0.85, yellow between 0.85 and 0.95, and green above 0.95. A 600 ms animated needle eases to each new value.
Test Results Panel (bottom-right) — A table of six NIST SP 800-22 tests, each showing its name, a p-value fill bar, the numeric p-value, and a color-coded PASS/FAIL badge. Hovering a row reveals the full test interpretation. A "Re-run Tests" button triggers an immediate evaluation.
NIST Special Publication 800-22 defines a battery of statistical tests for evaluating random number generators. Each test produces a p-value: the probability of seeing a result at least as extreme as observed, assuming a perfect random source. A p-value below the threshold (default α = 0.01) indicates a statistically significant departure from randomness.
Frequency (Monobit) — Verifies that the total number of 1s is close to n/2. Uses the complementary error function (erfc) of the normalized count deviation.
Block Frequency — Divides the sequence into equal-length blocks and verifies that the proportion of 1s in each block is close to 0.5. Uses a chi-squared test over block proportions evaluated via the regularized upper incomplete gamma function.
Runs — Counts the total number of maximal same-bit sequences (runs). Random sequences oscillate at a specific rate; too many or too few runs indicate periodicity or clustering. Uses erfc of the normalized run count deviation.
Longest Run of Ones — Checks that the longest run of consecutive 1s in M-bit blocks conforms to theoretical expectations from NIST lookup tables. Uses a chi-squared test over classified run lengths.
Serial (2-bit patterns) — Verifies that all four 2-bit overlapping patterns (00, 01, 10, 11) occur with equal frequency, using the delta-psi statistic evaluated against the upper incomplete gamma function.
Serial Correlation — Computes the lag-1 autocorrelation of the bit sequence and tests whether successive bits are statistically independent. Uses a Z-score from the expected autocorrelation of 0.25 for i.i.d. Bernoulli bits.
Cryptography — Key generation, nonces, initialization vectors, and one-time pads all require true randomness. Predictable or correlated keys dramatically reduce the effective key space. QRNG-derived entropy is used in hardware security modules (HSMs), smart cards, and protocol bootstrapping.
Monte Carlo Simulations — Financial risk models, physics simulations, option pricing, and climate models all rely on random sampling. Correlated PRNG outputs can introduce systematic bias that accumulates over millions of samples. QRNG sources eliminate this class of error.
Gaming and Lotteries — Regulated gambling and national lotteries require certified randomness sources that cannot be predicted or manipulated. Several jurisdictions mandate hardware RNG certification; QRNG is the gold standard.
Quantum Key Distribution (QKD) — QKD protocols such as BB84 require random basis choices for photon measurement. Using a QRNG for these choices closes the "basis selection" loophole that can leak information in imperfectly implemented QKD systems.
Scientific Research — Randomized controlled trials, A/B testing at scale, random sampling surveys, and permutation tests all benefit from higher-quality randomness, particularly when detecting subtle effects that could be confounded by autocorrelation.
The codebase is organized into eight source files:
| File | Responsibility |
|---|---|
src/core.h |
QuantumSource (platform entropy + Von Neumann/XOR processing), BitBuffer (circular buffer + analytics), NISTTestSuite (six statistical tests, igamc from scratch) |
src/engine.h |
QRNGEngine declaration — state machine, signals, thread management |
src/engine.cpp |
QRNGEngine implementation — background QThread + QTimer tick loop, cross-thread signaling |
src/mainwindow.h |
MainWindow declaration, embedded SettingsDialog forward declaration |
src/mainwindow.cpp |
MainWindow + SettingsDialog — full UI layout, toolbar, splitters, status bar, keyboard shortcuts, export logic |
src/widgets.h |
BitStreamView, EntropyGauge, FrequencyChart, HistogramWidget, TestResultsPanel declarations |
src/widgets.cpp |
All custom QPainter-based widget implementations — no QChart, no external plotting library |
src/theme.h |
Theme singleton + ThemeSignaller — complete dark/light color token system, full QSS stylesheet generation |
CMakeLists.txt |
C++20, Qt6, platform-specific entropy library linking, LTO for release builds |
The threading model is straightforward: QRNGEngine moves itself and a QTimer to a QThread. The timer fires at the configured interval, calls QuantumSource to generate a batch, updates BitBuffer under a mutex, and emits Qt signals that are delivered to the UI thread via the event queue. All UI construction is code-only (no .ui files); all widget rendering is QPainter-based (no QChart); all signal connections use the type-safe pointer-to-member syntax.
