|
Последний раз редактировалось wrest 06.08.2026, 21:17, всего редактировалось 3 раз(а).
YadryaraДержите :) (Оффтоп)
Код: \\ ============================================================ \\ platt.gp — аналитическое pi(x) методом Платта \\ (явная формула Римана + гауссово сглаживание + обращение Мёбиуса) \\ \\ Нулевая часть — рядами (лемма 6.1), решётная поправка — \\ сегментами с разложением Тейлора (§5). Глобальных переменных нет. \\ \\ БЫСТРЫЙ СТАРТ \\ --------------- \\ \r platt.gp \\ \p 50 \\ platt_pi(10^8, 60, 0.04, 500) \\ -> 5761455.00... \\ primepi(10^8) \\ сверка: 5761455 \\ \\ ИНТЕРФЕЙС (единственная функция) \\ --------------------------------- \\ platt_pi(x, nzeros, lam, nseg) \\ x — аргумент pi(x); \\ nzeros — сколько нулей дзета использовать; \\ lam — параметр сглаживания; \\ nseg — число сегментов Тейлора в решётной поправке. \\ \\ Нули кешируются в zeta_zeros.gp автоматически: первый запуск \\ их находит (медленно), повторные — читают из файла (быстро). \\ \\ ПАРАМЕТРЫ (проверенные ориентиры) \\ --------------------------------- \\ x ~ 10^6 : nzeros=12, lam=0.10, nseg=300 \\ x ~ 10^8 : nzeros=60, lam=0.04, nseg=500 \\ Для больших x уменьшайте lam и увеличивайте nzeros. \\ Правило: нулей нужно до высоты ~6/lam; lam балансирует \\ длину интеграла главного члена (~1/lam) и ширину решета (~lam). \\ \\ Точность: погрешность << 0.5, округление platt_pi даёт точное целое. \\ ============================================================
\\ ================= НУЛИ ДЗЕТА-ФУНКЦИИ =================
RStheta(t) = imag(lngamma(1/4 + I*t/2)) - t/2*log(Pi); Zhardy(t) = real(exp(I*RStheta(t)) * zeta(1/2 + I*t));
nextzero(t0, h) = { my(a=t0, va=Zhardy(a), b, vb); while(1, b = a + h; vb = Zhardy(b); if(va*vb < 0, return(solve(u=a, b, Zhardy(u)))); a = b; va = vb; ); }
savezeros(v, file) = { extern(concat("rm -f ", file)); write(file, v); } loadzeros(file) = read(file);
\\ Первые N нулей: из кэша (List по ссылке), иначе из файла, иначе \\ досчитать. Возвращает вектор первых N нулей, пополняет кэш и файл. getzeros(~cache, N, file, h) = { my(v, t, z); if(#cache >= N, return(Vec(cache)[1..N])); if(#cache == 0, iferr(v = loadzeros(file), e, v = []); for(i=1, #v, listput(~cache, v[i])); ); if(#cache >= N, return(Vec(cache)[1..N])); t = if(#cache > 0, cache[#cache] + h, 3.0); while(#cache < N, z = nextzero(t, h); listput(~cache, z); t = z + h; ); savezeros(Vec(cache), file); Vec(cache)[1..N] }
\\ ============ СГЛАЖЕННАЯ НУЛЕВАЯ ЧАСТЬ (Phihat) ============
phihat(s, x, lam) = (x^s/s)*exp(lam^2*s^2/2);
\\ int_0^Del phihat(s0+I*h) dh рядом Тейлора (лемма 6.1), без числ. интегр. step_incr(s0, Del, x, lam, K) = { my(A = log(x) + s0*lam^2, ph0 = phihat(s0,x,lam)); my(eD = exp(I*Del*A), invIA = 1/(I*A), Ik = vector(K+1), c, s=0, n, m, k); Ik[1] = (eD - 1)*invIA; for(k=1, K, Ik[k+1] = Del^k*eD*invIA - k*invIA*Ik[k]); for(k=0, K, c = 0; for(m=0, k\2, n = k - 2*m; c += ((-I)^n/s0^n) * ((-lam^2/2)^m/m!); ); s += c*Ik[k+1]; ); ph0*s }
\\ Re Phihat для всех нулей одним проходом вдоль критической прямой Phihat_re_all(x, lam, zeros, Del, K) = { my(n=#zeros, T, GatZ=vector(n), G=0, t, tnext, j); T = zeros[n] + 15/lam; t = zeros[1]; for(j=1, n, while(t < zeros[j] - 1e-30, tnext = min(t + Del, zeros[j]); G += step_incr(1/2 + I*t, tnext - t, x, lam, K); t = tnext; ); GatZ[j] = G; ); while(t < T, tnext = min(t + Del, T); G += step_incr(1/2 + I*t, tnext - t, x, lam, K); t = tnext; ); vector(n, j, imag(G - GatZ[j])) }
\\ Re Phihat(1) — главный член, проход вдоль Re(s)=1 от t=0 Phihat_re_at_1(x, lam, Del, K) = { my(T = 15/lam, t=0, tnext, G=0); while(t < T, tnext = min(t + Del, T); G += step_incr(1 + I*t, tnext - t, x, lam, K); t = tnext; ); imag(G) }
\\ ============ РЕШЁТНАЯ ПОПРАВКА (§5 Платта) ============
\\ sum_{p^m} (chi-phi)/m; сегменты + Тейлор для phi (erfc раз в сегмент) sieve_corr_taylor(x, lam, nseg) = { my(c=5, lo=x*exp(-c*lam), hi=x*exp(c*lam)); my(sq2lam=sqrt(2)*lam, sqrtpi=sqrt(Pi), invx=1/x); my(B=vector(nseg+1, j, round(lo + (j-1)*(hi-lo)/nseg))); my(s=0, j, a, b, x0, N, S1, S2, C, d, g, eg, ph0, ph1, ph2, p); for(j=1, nseg, a=B[j]; b=B[j+1]-1; if(b >= a, x0=(a+b)/2; N=0; S1=0; S2=0; C=0; forprime(p=a, b, if(p<x, C++); d=x0-p; S1+=d; S2+=d*d; N++; ); g=log(x0*invx)/sq2lam; eg=exp(-g*g); ph0=0.5*erfc(g); ph1=-eg/(sqrtpi*sq2lam*x0); ph2=-eg/(sqrtpi*sq2lam*x0*x0)*(2*g/sq2lam+1); s += C - (ph0*N - ph1*S1 + ph2*S2/2); ); ); my(m=2, q); while(2^m<=hi, forprime(p=ceil(lo^(1/m)), floor(hi^(1/m)), q=p^m; if(q>=lo&&q<=hi, s+=((q<x)-0.5*erfc(log(q*invx)/sq2lam))/m); ); m++; ); s }
\\ ============ ОБРАЩЕНИЕ МЁБИУСА ============
\\ точное f(y)=sum (1/n) pi(y^{1/n}) встроенным primepi \\ (нужно только для поправок при малых аргументах y=x^{1/n}, n>=2) ftarget(y) = sum(n=1, floor(log(y)/log(2)), primepi(sqrtn(y,n))/n);
\\ ============ СБОРКА ============
\\ сглаженное f(x); нули добывает сам через getzeros (кэш в файле) platt_f(x, nzeros, lam, nseg) = { my(cache = List()); my(zeros = getzeros(~cache, nzeros, "zeta_zeros.gp", 0.25)); my(re1 = Phihat_re_at_1(x, lam, 0.5, 40)); my(rez = Phihat_re_all(x, lam, zeros, 2, 24)); my(zsum = sum(j=1, #rez, rez[j])); re1 - 2*zsum - log(2) + sieve_corr_taylor(x, lam, nseg) }
\\ pi(x) = f(x) + поправки Мёбиуса при n>=2 platt_pi(x, nzeros, lam, nseg) = { my(s = platt_f(x, nzeros, lam, nseg), n); for(n=2, floor(log(x)/log(2)), if(moebius(n)!=0, s += (moebius(n)/n)*ftarget(sqrtn(x,n))); ); s } Запуск Код: ? \p 50 realprecision = 57 significant digits (50 digits displayed) ? platt_pi(10^8, 60, 0.04, 500) time = 4,905 ms. 5761454.9920227407395004742199404525311697855001998 ? primepi(10^8) time = 31 ms. 5761455 ? Запускать в линуксе, есть побочный эффект: в домашней папке (откуда запущен gp) будет сохраняться файл zeta_zeros.gp с нулями дзета функции. Скрипт выдавлен из Qwen-а
|