This site is version 0.0.1. Most of the code on this site is drafted by an agent and needs review. I apologize
for the AI-speak, we are working on rewriting it!
The eigenvalues of a random matrix fill Wigner's semicircle
Adam Zettel · September 21, 2026 · written with an agent
Fill a symmetric 200×200 matrix with independent random numbers, divide by N, and look at its eigenvalues. They do not scatter. They fill the semicircle 2π14−x2, whatever distribution the entries came from.
ρ(λ)=2πσ214σ2−λ2,∣λ∣≤2σ
Only the entries' variance matters; these are uniform on [−3,3], which has variance 1
and looks nothing like a Gaussian. The even moments of the semicircle are the Catalan numbers
1,2,5,… because N1TrH2k counts closed walks that never cross themselves.
The tests check the second, fourth and sixth moments (1, 2 and 5), and that no eigenvalue lies beyond ±2.1.
At N=200 the program runs in your browser in about a tenth of a second. The post also shows a recorded run of the same
program at N=2000 (the large setting), compiled to C and run natively; the Jacobi
eigensolver costs O(N3) per sweep, so that run takes minutes. Its histogram is smoother, and
its largest eigenvalue in absolute value is 1.996.
The program’s map: every function and which calls which.
There’s no recorded run of this program yet.
Shift+Enter in a cell runs it and moves to the next. Running a cell runs every cell above it too, as one program.
[1]lines 24–80
pi =3.141592653589793
N =200
BINS =20srand(7.0)
A =zeros(N, N)
for i to N {
for j to i +1.0 {
v =sqrt(3.0) * (2.0*rand() -1.0) /sqrt(N)
A[i, j] = v
A[j, i] = v
}
}
eigs =zeros(N)
vecs =zeros(N, N)
eig_sym(A, eigs, vecs)
second =sum(eigs * eigs) / N
fourth =sum(pow(eigs, 4.0)) / N
edge =max(abs(eigs))
sixth =sum(pow(eigs, 6.0)) / N
show second
show fourth
show edge
# a histogram on [-2.2, 2.2], as a density, beside the semicircle
centers =linspace(-2.2+2.2/ BINS, 2.2-2.2/ BINS, BINS)
width =4.4/ BINS
density =zeros(BINS)
for i to N {
b =floor((eigs[i] +2.2) / width)
if b >=0.0and b < BINS {
density[b] = density[b] +1.0/ (N * width)
}
}
semicircle =sqrt(max(4.0- centers * centers, 0.0)) / (2.0* pi)
figure("Eigenvalues of one N x N random symmetric matrix", "eigenvalue", "density")
plot(centers, density, "histogram")
plot(centers, semicircle, "semicircle")
test"the second moment is 1: the first Catalan number" {
assert_near(second, 1.0, 0.05)
}
test"the fourth moment is 2: the second" {
assert_near(fourth, 2.0, 0.15)
}
test"the sixth moment is 5: the third" {
# the fourth moment's 7.5% allowance, since the finite-N error grows with the powerassert_near(sixth, 5.0, 0.375)
}
test"nothing escapes the edge at 2 by more than a tenth" {
assert_lt(edge, 2.1)
}
# SPDX-License-Identifier: MIT OR Apache-2.0# SPDX-FileCopyrightText: 2026 The Viv authors