What happened
Exact enumeration gives probability 43/256 for four die faces and 42/256 for two faces under naive modulo. Rejection sampling gives exactly 1/6 to every face under the uniform-source model.
The experiment magnifies the mapping issue with an eight-bit range. A wider source can make the same kind of bias harder to observe, but whether the mapping is exact is a counting question, not a judgement based on a smooth chart.
Method and benchmark
Map one million uniform bytes to six faces with modulo; separately generate one million accepted faces by rejecting bytes 252–255. Enumerate the 256 byte values to obtain exact probabilities.
The two methods each produce one million accepted die outcomes. Rejection required 1,016,045 proposals in this run, discarding the surplus values 252–255. The CSV reports observed counts alongside exact mapping probabilities.
Python 3.12.14 · NumPy 2.3.5 · NumPy PCG64 · seed 20260933. Each experiment starts a separate stream. Code and all datasets are linked below.
What this does not establish
Eight bits deliberately magnify the bias. This is a mapping demonstration using a reproducible PRNG, not a test of Web Crypto or an estimate of bias in a specific lottery.
This is a controlled PRNG simulation and a finite mapping proof. It is not an audit of a browser’s entropy source. Its lesson transfers to bounded integer generation: count how many accepted source values map to each destination before claiming uniformity.
These are original educational simulations prepared for RoomForChance. They are not historical lottery records, a physical-machine audit, an external peer review or evidence of a prediction advantage.
Inspect the data
Download modulo-bias.csv · All results and metadata (JSON) · Download the complete Python program · Download figure-generation code
| face | modulo count | rejection count | exact modulo probability | exact rejection probability |
|---|---|---|---|---|
| 1 | 168,259 | 166,758 | 0.16796875 | 0.166666667 |
| 2 | 168,321 | 167,109 | 0.16796875 | 0.166666667 |
| 3 | 168,006 | 166,341 | 0.16796875 | 0.166666667 |
| 4 | 168,243 | 166,998 | 0.16796875 | 0.166666667 |
| 5 | 163,742 | 166,811 | 0.1640625 | 0.166666667 |
| 6 | 163,429 | 165,983 | 0.1640625 | 0.166666667 |
Reproduce the experiment
Download the Python program to an empty working folder. Use the recorded environment for an exact replay. The program runs all eleven studies and creates a lab-data folder containing the result files. The largest study performs ten million draws; allow time for it to finish.
python -m pip install numpy==2.3.5
python run_experiments.pyExpected CSV SHA-256: 8b715aea024eda31b7da7642909994aba1fe81ab4eaf395a79fcc1846793a77f. A matching seed alone is insufficient if you change the implementation or call sequence. The CSV files use CC BY 4.0; the original code uses the MIT license included with the downloads.
Read the reasoning
Cryptographically Secure Random Numbers: What the Claim Means
Learn why CSPRNGs concern unpredictability, why Math.random is different, and why a secure source still needs unbiased range conversion.
How RoomForChance Generates Random Numbers: An Inspectable Method
An implementation-level explanation of RoomForChance’s browser random source, rejection sampling, partial shuffle and separate simulation methodology.
Mega Millions Odds: The 70 × 24 Matrix Explained
Calculate current Mega Millions jackpot odds as C(70,5) × 24, understand the old-denominator trap and separate multipliers from number selection.
Modulo Bias Explained: Why a Random Byte Makes an Unfair Die
See the exact 256-to-6 mapping behind modulo bias, calculate the imbalance, and understand how rejection sampling removes it.
Random Number Generators: From Entropy to a Fair Choice
Understand how random number generators combine a source, an algorithm and a mapping—and why each stage matters for fair lottery selections.
Rejection Sampling: How Discarding Values Can Preserve Fairness
A practical derivation of rejection sampling for integer ranges and distinct lottery numbers, with efficiency and failure cases explained.
Technical references
NumPy PCG64 documentation describes the generator family. Harvard Stat 110 provides university-level probability background. The model-specific derivation is linked above.