Uniform distribution
random number generation
statistical analysis
probability theory
computational methods

Prove a random generated number is uniform distributed

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

You cannot mathematically "prove" a random number generator (RNG) produces a uniform distribution from its output alone — you can only gather statistical evidence that it is consistent with uniformity. The standard approach is to generate a large sample and apply statistical tests (chi-square, Kolmogorov-Smirnov, or visual methods) that measure how closely the observed distribution matches the expected uniform distribution.

What Is a Uniform Distribution?

A uniform distribution over the interval [a, b] means every value in that range is equally likely. For a discrete uniform distribution over integers 1 to k, each value has probability 1/k.

Key properties:

  • PDF (continuous): f(x) = 1/(b-a) for x in [a, b], 0 otherwise
  • Mean: (a + b) / 2
  • Variance: (b - a)² / 12

Test 1: Chi-Square Goodness of Fit

The chi-square test is the standard method for testing discrete uniform distributions. It compares observed frequencies against expected frequencies:

python
1import numpy as np
2from scipy import stats
3
4# Generate random integers from 1 to 6 (like a die)
5rng = np.random.default_rng(42)
6n = 10000
7samples = rng.integers(1, 7, size=n)  # 1 to 6 inclusive
8
9# Count observed frequencies
10observed = np.bincount(samples)[1:]  # Skip index 0
11expected = np.full(6, n / 6)         # Each face should appear n/6 times
12
13# Chi-square test
14chi2_stat, p_value = stats.chisquare(observed, expected)
15
16print(f"Observed:  {observed}")
17print(f"Expected:  {expected}")
18print(f"Chi-square statistic: {chi2_stat:.4f}")
19print(f"P-value: {p_value:.4f}")
20
21if p_value > 0.05:
22    print("Cannot reject uniformity (p > 0.05)")
23else:
24    print("Evidence against uniformity (p <= 0.05)")

The chi-square statistic measures the total squared deviation between observed and expected counts, normalized by expected counts:

χ² = Σ (Oᵢ - Eᵢ)² / Eᵢ

A small χ² (high p-value) means the data is consistent with uniformity. A large χ² (low p-value) means the data deviates significantly from uniform.

Test 2: Kolmogorov-Smirnov Test

The KS test works for continuous distributions. It measures the maximum difference between the empirical CDF and the theoretical CDF:

python
1# Generate continuous uniform samples in [0, 1)
2samples = rng.random(size=10000)
3
4# KS test against uniform distribution
5ks_stat, p_value = stats.kstest(samples, 'uniform')
6
7print(f"KS statistic: {ks_stat:.6f}")
8print(f"P-value: {p_value:.4f}")
9
10if p_value > 0.05:
11    print("Consistent with uniform distribution")
12else:
13    print("Not consistent with uniform distribution")

The KS test is more appropriate for continuous data because it does not require binning.

Test 3: Visual Inspection

Histogram

python
1import matplotlib.pyplot as plt
2
3samples = rng.random(size=10000)
4
5plt.figure(figsize=(10, 4))
6
7plt.subplot(1, 2, 1)
8plt.hist(samples, bins=50, density=True, alpha=0.7, edgecolor='black')
9plt.axhline(y=1.0, color='red', linestyle='--', label='Expected (uniform)')
10plt.title('Histogram')
11plt.legend()
12
13plt.subplot(1, 2, 2)
14sorted_samples = np.sort(samples)
15expected_cdf = np.linspace(0, 1, len(sorted_samples))
16plt.plot(sorted_samples, np.arange(1, len(sorted_samples)+1) / len(sorted_samples), label='Empirical CDF')
17plt.plot([0, 1], [0, 1], 'r--', label='Theoretical CDF')
18plt.title('CDF Comparison')
19plt.legend()
20
21plt.tight_layout()
22plt.show()

A uniform histogram should show roughly equal bar heights. The empirical CDF should closely follow the diagonal line from (0,0) to (1,1).

Test 4: Runs Test for Independence

Uniformity alone is not sufficient — the values should also be independent. The runs test checks for patterns:

python
1from statsmodels.sandbox.stats.runs import runstest_1samp
2
3samples = rng.random(size=1000)
4z_stat, p_value = runstest_1samp(samples, cutoff='median')
5
6print(f"Runs test p-value: {p_value:.4f}")

Comprehensive Testing Function

python
1def test_uniformity(samples, alpha=0.05, n_bins=None):
2    """Run multiple uniformity tests on a sample."""
3    n = len(samples)
4    results = {}
5
6    # 1. Chi-square test (bin continuous data)
7    if n_bins is None:
8        n_bins = max(10, int(np.sqrt(n)))
9    observed, bin_edges = np.histogram(samples, bins=n_bins)
10    expected = np.full(n_bins, n / n_bins)
11    chi2, p_chi = stats.chisquare(observed, expected)
12    results['chi_square'] = {'statistic': chi2, 'p_value': p_chi, 'pass': p_chi > alpha}
13
14    # 2. KS test
15    ks, p_ks = stats.kstest(samples, 'uniform',
16                             args=(min(samples), max(samples) - min(samples)))
17    results['ks_test'] = {'statistic': ks, 'p_value': p_ks, 'pass': p_ks > alpha}
18
19    # 3. Basic statistics
20    mean = np.mean(samples)
21    var = np.var(samples)
22    a, b = min(samples), max(samples)
23    expected_mean = (a + b) / 2
24    expected_var = (b - a) ** 2 / 12
25    results['mean'] = {'observed': mean, 'expected': expected_mean}
26    results['variance'] = {'observed': var, 'expected': expected_var}
27
28    # Summary
29    all_pass = all(r.get('pass', True) for r in results.values())
30    results['overall'] = 'PASS' if all_pass else 'FAIL'
31
32    return results
33
34# Usage
35samples = np.random.uniform(0, 1, size=50000)
36results = test_uniformity(samples)
37for test, result in results.items():
38    print(f"{test}: {result}")

Interpreting P-Values

P-valueInterpretation
> 0.10Strong evidence for uniformity
0.05 - 0.10Weak evidence, borderline
0.01 - 0.05Evidence against uniformity
< 0.01Strong evidence against uniformity

Important: a high p-value does not prove uniformity. It means the data is consistent with uniformity — you failed to find evidence against it. With small samples, even a biased RNG might pass the tests.

Common Pitfalls

  • Sample size: Statistical tests need large samples (n > 1000) to detect small deviations from uniformity. With n = 50, even a clearly biased generator might pass.
  • Multiple testing: Running many tests increases the chance of a false positive. Apply Bonferroni correction when running multiple tests (divide alpha by the number of tests).
  • Binning artifacts: Chi-square test results depend on bin count. Too few bins hide structure; too many bins have low expected counts (expected count per bin should be >= 5).
  • Proof vs evidence: Statistical tests can only reject the null hypothesis (uniformity), never prove it. A passing test means "no evidence of non-uniformity," not "proven uniform."
  • Pseudo-randomness: Most programming language RNGs (like Python's random module) use Mersenne Twister, which passes basic statistical tests but is not cryptographically secure. Use secrets module for cryptographic applications.

Summary

  • Use the chi-square test for discrete distributions and the KS test for continuous distributions
  • Visual inspection (histograms, CDF plots) provides quick intuition but is not rigorous
  • Always use large sample sizes (10,000+) for reliable test results
  • A passing test means "consistent with uniform" — it does not prove uniformity
  • Test independence (runs test) in addition to distribution shape
  • Combine multiple statistical tests for stronger evidence

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.