density.py
5.1 kB · python · 170 lines
1import itertools2from fractions import Fraction3from math import log4import numpy as np56Q = 37NMAX = 148FACTOR_DEG = 69EULER_N = 1010EULER_DEG = 511LEVELS = (10, 12, 14)1213def trim(c):14 while c and c[-1] == 0:15 del c[-1]16 return c1718def divmod_poly(a, b):19 r = list(a)20 db = len(b) - 121 inv = pow(b[-1], Q - 2, Q)22 q = [0] * max(0, len(r) - db)23 while r and len(r) - 1 >= db:24 d = len(r) - 1 - db25 f = (r[-1] * inv) % Q26 q[d] = f27 for i, c in enumerate(b):28 r[i + d] = (r[i + d] - f * c) % Q29 trim(r)30 return tuple(trim(q)), tuple(r)3132def monics(d):33 for tail in itertools.product(range(Q), repeat=d):34 yield tail + (1,)3536def irreducibles(maxdeg):37 out = []38 for d in range(1, maxdeg + 1):39 for p in monics(d):40 if all(divmod_poly(p, s)[1] != () for s in out if len(s) - 1 <= d // 2):41 out.append(p)42 return out4344def label(p):45 parts = []46 for k in range(len(p) - 1, -1, -1):47 c = p[k]48 if c == 0:49 continue50 if k == 0:51 parts.append(str(c))52 else:53 base = "t" if k == 1 else "t^%d" % k54 parts.append(base if c == 1 else "%d%s" % (c, base))55 return " + ".join(parts) if parts else "0"5657def poly_of(i, n):58 return tuple(trim([(i >> k) & 1 for k in range(n)]))5960def masks(primes, n):61 bits = ((np.arange(1 << n)[:, None] >> np.arange(n)) & 1).astype(np.int64)62 out = {}63 for p in primes:64 dp = len(p) - 165 rows = np.zeros((n, dp), dtype=np.int64)66 for k in range(n):67 r = divmod_poly(tuple([0] * k + [1]), p)[1]68 rows[k, :len(r)] = r69 out[p] = ~((bits @ rows) % Q).any(axis=1)70 return out7172def factor_sets(primes, msk, n):73 small = [[] for _ in range(1 << n)]74 for p in primes:75 for i in np.nonzero(msk[p])[0]:76 if i:77 small[int(i)].append(p)78 ids = {p: k for k, p in enumerate(primes)}79 degs = [len(p) - 1 for p in primes]80 seen = {}81 sets = [()] * (1 << n)82 for i in range(1, 1 << n):83 h = poly_of(i, n)84 keys = []85 for p in small[i]:86 keys.append(ids[p])87 while True:88 q, r = divmod_poly(h, p)89 if r != ():90 break91 h = q92 if len(h) > 1:93 if h not in seen:94 seen[h] = len(degs)95 degs.append(len(h) - 1)96 keys.append(seen[h])97 sets[i] = tuple(sorted(keys))98 return sets, degs99100def sieve_count(sets, degs, n, degcap=None):101 cnt = {}102 for i in range(1, 1 << n):103 ps = sets[i]104 if degcap is not None:105 ps = tuple(k for k in ps if degs[k] <= degcap)106 for k in range(len(ps) + 1):107 for sub in itertools.combinations(ps, k):108 cnt[sub] = cnt.get(sub, 0) + 1109 total = 0110 for key, m in cnt.items():111 total += (-1 if len(key) % 2 else 1) * (m * m + 2 * m)112 return total113114def brute(n):115 polys = [poly_of(i, n) for i in range(1 << n)]116 c = 0117 for a in polys:118 for b in polys:119 x, y = a, b120 while y != ():121 x, y = y, divmod_poly(x, y)[1]122 c += 1 if len(x) == 1 else 0123 return c124125primes = irreducibles(FACTOR_DEG)126msk = masks(primes, NMAX)127sets, degs = factor_sets(primes, msk, NMAX)128129print("DOMAIN q = 3, S = {0, 1}, degree below n, n up to %d, ordered pairs over all %d^2 including the zero polynomial"130 % (NMAX, 1 << NMAX))131print("irreducibles of degree at most %d: %d" % (FACTOR_DEG, len(primes)))132133for n in range(1, 5):134 assert sieve_count(sets, degs, n) == brute(n), n135print("cross-check against Euclid on every ordered pair, n = 1 to 4: passed")136137for n in LEVELS:138 z = sieve_count(sets, degs, n)139 print("n = %2d Z = %d / %d density = %.6f gap from 9/16 = %+.6f"140 % (n, z, 4 ** n, z / 4 ** n, z / 4 ** n - 9 / 16))141142print("prediction (2/3) * (3/4) / (8/9) = 9/16 = %.6f" % (9 / 16))143144pi = {p: Fraction(int(msk[p][:1 << EULER_N].sum()), 1 << EULER_N)145 for p in primes if len(p) - 1 <= EULER_DEG}146for p in primes:147 if len(p) - 1 == 1:148 print("pi(%s) at n = %d = %s = %.6f" % (label(p), EULER_N, pi[p], float(pi[p])))149for d in (2, 3):150 ps = [p for p in primes if len(p) - 1 == d]151 devs = [abs(pi[p] - Fraction(1, Q ** d)) for p in ps]152 print("degree %d, %d primes, mean |pi - 3^-%d| = %.6f, max = %.6f"153 % (d, len(ps), d, float(sum(devs) / len(devs)), float(max(devs))))154155prod = Fraction(1)156for d in range(1, EULER_DEG + 1):157 for p in primes:158 if len(p) - 1 == d:159 prod *= 1 - pi[p] ** 2160 print("marginal Euler product through degree %d = %.6f" % (d, float(prod)))161162shared = sieve_count(sets, degs, EULER_N, degcap=EULER_DEG)163exact = Fraction(shared, 4 ** EULER_N)164print("exact no shared prime of degree at most %d at n = %d = %d/%d = %.6f"165 % (EULER_DEG, EULER_N, shared, 4 ** EULER_N, float(exact)))166print("marginal product minus exact = %+.6f" % float(prod - exact))167168g = log(4) / log(3)169print("gamma = log_3(4) = %.6f gamma/2 = %.6f window (gamma/2, 1/2] empty = %s"170 % (g, g / 2, g / 2 > 0.5))