Судя по размеру текстовых файлов (кроме одного), у Одлыжко точность гораздо хуже, чем у Платта.
Да, но какая вам нужна точность нулей и почему?
-- добавлено через 18 минут --Уже единолично вашей? Вроде это была ваша с Квеном программа.
Вы ещё спросите не взять Платта в соавторы, вроде программа по его статье написана
-- добавлено через 26 минут --Соответственно я и не понял чем нули Одлыжко лучше или проще в использовании,
Там у меня функция, которая получает, нули - отдельно. Можно заменить на свою.
Я не думаю что в диапазонах до 10^12 будет какая-то разница.
Платт же занимался математически строгим вычислением, контролируя погрешность при помощи интервальной арифметики, а у нас тут некая proof of concept.
Впрочем, если где-тотначнёт расходиться то можно будет заменить нули на более точные и посмотреть из-за них ли.
-- добавлено через 3 минуты --или как Вам выше ИИ посоветовал, или одной командой z=lfunzeros(1,9878) (только надо подобрать верхний предел для нужного количества нулей, указал для 10 тысяч), причём с любой желаемой точностью (через команду \p), правда очень долго (часы). Но можно один раз посчитать и сохранить в файл, а потом уже пользоваться.
А вот как раз про эту функцию написано что
Цитата:
Use a naive algorithm which may miss some zeros.
Вот и у меня была наивная реализация, которая нули пропускала.
-- добавлено через 52 минуты --Версия 2
(Оффтоп)
Код:
\\ ============================================================
\\ platt.gp — аналитическое pi(x) методом Платта
\\ (явная формула Римана + гауссово сглаживание + обращение Мёбиуса)
\\
\\ Нули берутся из таблицы Одлыжко (zeros1 = 10^5 нулей,
\\ zeros6 = 10^6 нулей). Нулевая часть — рядами (лемма 6.1),
\\ решётная поправка — сегментами с разложением Тейлора (§5).
\\
\\ БЫСТРЫЙ СТАРТ
\\ ---------------
\\ \r platt_v2.gp
\\ \p 50
\\ platt_pi(10^8, 100, 0.04, 500) \\ -> 5761455.00...
\\ primepi(10^8) \\ сверка: 5761455
\\
\\ ИНТЕРФЕЙС
\\ ---------
\\ platt_pi(x, nzeros, lam, nseg)
\\ x — аргумент pi(x);
\\ nzeros — сколько нулей дзета использовать;
\\ lam — параметр сглаживания;
\\ nseg — число сегментов Тейлора в решётной поправке.
\\
\\ ПАРАМЕТРЫ (проверенные ориентиры)
\\ ---------------------------------
\\ x ~ 10^6 : nzeros=12, lam=0.10, nseg=300
\\ x ~ 10^8 : nzeros=100, lam=0.04, nseg=500
\\ x ~ 10^10: nzeros=8000, lam=0.001, nseg=500
\\ Правило: нулей нужно до высоты ~6/lam; lam балансирует
\\ длину интеграла главного члена (~1/lam) и ширину решета (~lam).
\\
\\ Точность: погрешность << 0.5, округление platt_pi даёт точное целое.
\\ ============================================================
\\ ================= НУЛИ ДЗЕТА-ФУНКЦИИ =================
load_zeros(file, n) = {
my(v = readvec(file));
if(#v < n, error("Не хватает нулей в файле ", file," Надо: ",n," Есть:",#v));
Vec(v)[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 для всех нулей; адаптивный шаг Del = ratio*|1/2+I*t|
Phihat_re_all(x, lam, zeros, K) = {
my(n=#zeros, GatZ=vector(n), G=0, t=zeros[1], j);
GatZ[1] = 0;
for(j=2, n,
G += step_incr(1/2 + I*t, zeros[j] - t, x, lam, K);
t = zeros[j];
GatZ[j] = G;
);
vector(n, j, imag(G - GatZ[j]))
}
\\ Re Phihat(1): главный член, адаптивный шаг Del = ratio*|1+I*t|
Phihat_re_at_1(x, lam, ratio, K) = {
my(T = 8/lam, t=0, tnext, G=0, Del);
while(t < T,
Del = ratio*sqrt(1+t^2);
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 раз в сегмент)
\\ Ширина окна = 2*c*lam*x, c=5 фиксировано (проверено: c=5,8,10 дают одно и то же)
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
ftarget(y) = sum(n=1, floor(log(y)/log(2)), primepi(sqrtn(y,n))/n);
\\ ============ СБОРКА ============
\\ сглаженное f(x)
platt_f(x, nzeros, lam, nseg) = {
my(res);
my(t0=getwalltime());
print1("Получаем ", nzeros, " нулей ...");
my(zeros = load_zeros("zeros1", nzeros));
print(" готово за ", getwalltime()-t0, " мс");
t0=getwalltime();
print1("Вычисляем главный член ...");
my(re1 = Phihat_re_at_1(x, lam, 0.5, 40));
print(" готово за ", getwalltime()-t0, " мс");
t0=getwalltime();
print1("Вычисляем оставшийся ряд phihat ...");
my(rez = Phihat_re_all(x, lam, zeros, 12));
print(" готово за ", getwalltime()-t0, " мс");
t0=getwalltime();
print1("Суммируем ряд ...");
my(zsum = sum(j=1, #rez, rez[j]));
print(" готово за ", getwalltime()-t0, " мс");
t0=getwalltime();
print1("Вычисляем поправку решетом ...");
res=re1 - 2*zsum - log(2) + sieve_corr_taylor(x, lam, nseg);
print(" готово за ", getwalltime()-t0, " мс");
return(res);
}
\\ pi(x) = f(x) + поправки Мёбиуса при n>=2
platt_pi(x, nzeros, lam, nseg) = {
my(s = platt_f(x, nzeros, lam, nseg), n);
my(t0=getwalltime());
print1("Обращение Мёбиуса ...");
for(n=2, floor(log(x)/log(2)),
if(moebius(n)!=0, s += (moebius(n)/n)*ftarget(sqrtn(x,n)));
);
print(" готово за ", getwalltime()-t0, " мс");
return(s);
}
Запуск
Код:
~/gp-scripts $ gp -q platt_v2.gp
? platt_pi(10^11,56000,0.0001,100)
Получаем 56000 нулей ... готово за 119 мс
Вычисляем главный член ... готово за 45 мс
Вычисляем оставшийся ряд phihat ... готово за 11172 мс
Суммируем ряд ... готово за 9 мс
Вычисляем поправку решетом ... готово за 6081 мс
Обращение Мёбиуса ... готово за 1 мс
time = 17,327 ms.
4118054813.0290594618784539930078960821
?
Перед первым запуском, скачать нули в файл
zeros1Команда для линукс (запускать в той папке которую
gp будет считать домашней, в случае выше это
~/gp-scripts ).
Код:
wget -O zeros1 https://www-users.cse.umn.edu/~odlyzko/zeta_tables/zeros1
Или скачать в файл с таким именем более точные нули, если надо. Файл
zeros1 - текстовый, по одному числу на строку, чтобы в
pari/gp работала команда
v=readvec("zeros1")