Upload problem_solvers/lehmer_phenomena.py
Browse files
problem_solvers/lehmer_phenomena.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
PROBLEM 8: Zero Spacing Records — Lehmer Phenomena
|
| 3 |
+
====================================================
|
| 4 |
+
Unusually close zeros are called "Lehmer phenomena".
|
| 5 |
+
OPEN: characterize the distribution of closest pairs among large zeros.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import numpy as np
|
| 9 |
+
from typing import Dict
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class LehmerPhenomenaAnalyzer:
|
| 13 |
+
def __init__(self, zeros: list):
|
| 14 |
+
self.zeros = np.array(zeros)
|
| 15 |
+
self.results = {}
|
| 16 |
+
|
| 17 |
+
def analyze(self) -> Dict:
|
| 18 |
+
spacings = np.diff(self.zeros)
|
| 19 |
+
# Normalize by local mean spacing
|
| 20 |
+
window = 50
|
| 21 |
+
normalized = np.zeros(len(spacings))
|
| 22 |
+
for i in range(len(spacings)):
|
| 23 |
+
start = max(0, i - window // 2)
|
| 24 |
+
end = min(len(spacings), i + window // 2)
|
| 25 |
+
local_mean = np.mean(spacings[start:end])
|
| 26 |
+
normalized[i] = spacings[i] / local_mean if local_mean > 0 else 1.0
|
| 27 |
+
|
| 28 |
+
# Find minimum spacings
|
| 29 |
+
min_spacing = float(np.min(normalized))
|
| 30 |
+
min_idx = int(np.argmin(normalized))
|
| 31 |
+
|
| 32 |
+
# Small spacing distribution
|
| 33 |
+
small_threshold = 0.3
|
| 34 |
+
n_small = int(np.sum(normalized < small_threshold))
|
| 35 |
+
|
| 36 |
+
# GUE prediction for small spacings: P(s) ~ (πs)²/2 for small s
|
| 37 |
+
# Wigner surmise: p(s) = (π/2)s exp(-πs²/4)
|
| 38 |
+
s_bins = np.linspace(0, 3, 60)
|
| 39 |
+
hist, _ = np.histogram(normalized, bins=s_bins, density=True)
|
| 40 |
+
wigner = (np.pi / 2) * s_bins[:-1] * np.exp(-np.pi * s_bins[:-1] ** 2 / 4)
|
| 41 |
+
|
| 42 |
+
self.results = {
|
| 43 |
+
'n_zeros': len(self.zeros),
|
| 44 |
+
'min_normalized_spacing': min_spacing,
|
| 45 |
+
'min_spacing_index': min_idx,
|
| 46 |
+
'min_spacing_gamma': float(self.zeros[min_idx]),
|
| 47 |
+
'mean_spacing': float(np.mean(spacings)),
|
| 48 |
+
'std_spacing': float(np.std(spacings)),
|
| 49 |
+
'n_small_spacings': n_small,
|
| 50 |
+
'fraction_small': float(n_small / len(normalized)),
|
| 51 |
+
'spacing_histogram': hist.tolist(),
|
| 52 |
+
'wigner_prediction': wigner.tolist(),
|
| 53 |
+
'bin_centers': ((s_bins[:-1] + s_bins[1:]) / 2).tolist(),
|
| 54 |
+
'top_10_smallest': [float(v) for v in np.sort(normalized)[:10]],
|
| 55 |
+
}
|
| 56 |
+
return self.results
|
| 57 |
+
|
| 58 |
+
def summary(self) -> str:
|
| 59 |
+
r = self.results
|
| 60 |
+
s = f"Lehmer Phenomena Analysis\n{'='*50}\n"
|
| 61 |
+
s += f"Zeros: {r['n_zeros']:,}\n"
|
| 62 |
+
s += f"Min normalized spacing: {r['min_normalized_spacing']:.6f}\n"
|
| 63 |
+
s += f"At γ ≈ {r['min_spacing_gamma']:.2f} (index {r['min_spacing_index']})\n"
|
| 64 |
+
s += f"Spacings < 0.3: {r['n_small_spacings']} ({r['fraction_small']:.4%})\n"
|
| 65 |
+
s += f"Top 10 smallest: {r['top_10_smallest'][:5]}\n"
|
| 66 |
+
return s
|