Улучшил предыдущий код:
Код:
/* solve x^2=a mod p, p is prime */
modulop(a,p):=block([h, N1, N2, legandr, N,a1,a2, k,p1,i, b, c, d, j],
if primep(p) then
block(
legandr: power_mod(a,(p-1)/2,p),
if legandr = p-1 then
"No solutions"
else
block(
h:p-1, k:0,
while mod(h,2)=0 do block(h:h/2,k:k+1),
N:2,
while power_mod(N,(p-1)/2,p)=1 do N:N+1,
a1:power_mod(a, (h+1)/2,p), N1:power_mod(N,h,p),
N2:1, j:0, a2:inv_mod(a,p),
for i:0 thru k-2 do
block(
b:mod(a1*N2,p), c:mod(a2*b^2,p),
d:power_mod(c,2^(k-2-i),p),
if d = 1 then j:0,
if d=p-1 then j:1,
N2:mod(N2*power_mod(N1,(2^i*j),p),p)),
j:mod(a1*N2,p),
[j, p-j]))
else
"Not prime")$
Реализовал нахождение решения сравнения
для
:
Код:
/* solve x^2=a mod m, m = p1 * p2 */
modulop1p2(a,m):= block([fact:ifactors(m), ans1, ans2,res:[]],
if length(fact) # 2 then
"m is not equal p1*p2"
else
block(
if (fact[1][2] = 1) and (fact[2][2] = 1) then
block(
ans1:modulop(a,fact[1][1]),
ans2:modulop(a,fact[2][1]),
res:cons(chinese([ans1[1],ans2[1]],[fact[1][1], fact[2][1]]), res),
res:cons(chinese([ans1[1],ans2[2]],[fact[1][1], fact[2][1]]), res),
res:cons(chinese([ans1[2],ans2[1]],[fact[1][1], fact[2][1]]), res),
res:cons(chinese([ans1[2],ans2[2]],[fact[1][1], fact[2][1]]), res),
res)
else
"m is equal p1^a*p2^b, a or b > 1" ))$
Для общего вида
больно муторно перебирать все
комбинаций, где
- число простых делителей
.
Для эффективного перебора нужно еще разработать правила останова для
.
Пусть известно решение
, найдем ограничение для
, для которых возможно лучшее приближение.
Имеем:
т.е. должно выполняться:
Обозначим
Возводим в квадрат:
Возводим еще раз в квадрат:
После всех подстановок и сокращений окончательно получаем (если не ошибся):
Поскольку
, то функция
достигает максимума в точке
.
Поэтому имеем следующее ограничение:
Но это не особенно полезно.
А вот на это можно опираться. Для этого добавим еще порцию кода:
Код:
delta(m,R,L):=block([dd,v,vv,i:1,j],
while not(integerp(xy(m,L[i],R)[1])) and (i<length(L)) do i:i+1,
dd:bfloat(2*m-sqrt((m-L[i])^2-R)-sqrt((m+L[i])^2-R)),vv:L[i],
for j:i+1 thru length(L) do
block(
if (dd>bfloat(2*m-sqrt((m-L[j])^2-R)-sqrt((m+L[j])^2-R))) and (integerp(xy(m,L[j],R)[1])) then
block(dd:bfloat(2*m-sqrt((m-L[j])^2-R)-sqrt((m+L[j])^2-R)),vv:L[j])),
if integerp(xy(m,vv,R)[1]) then
[vv,dd]
else
false)$
xy(m,v,R):=([((m-v)^2-R)/(4*m),((m+v)^2-R)/(4*m)])$
fpprec:30$
v2(m,v1,R1,R2):=block([a], a:bfloat(sqrt((m-v1)^2-R1)+sqrt((m+v1)^2-R1)),bfloat(sqrt((m^2-a^2/4-R2)/(4*m^2/a^2-1))))$
Функция delta(m,R,L) выбирает из списка L решений сравнения
целочисленное решение с минимальной ошибкой
, если целочисленных решений нет, то возвращается false.
Функция xy(m,v,R) просто получает список решений
для заданных
.
Функция v2(m,v1,R1,R2) считает собственно верхнее ограничение для
по полученной формуле для известных
и нового
.
Продемонстрируем на примере треугольного числа (максимального в указанной ранее таблице)
Код:
(%i7) delta(714195944281,1, modulop1p2(1, 714195944281));
(%o7) [714193553974, 2.09178501637662800360928372356b-7]
(%i8) v2(714195944281, 714193553974, 1, 2);
(%o8) 7.14191163658994066022843741247b11
(%i9) delta(714195944281,2, modulop1p2(2, 714195944281));
(%o9) false
(%i10) v2(714195944281, 714193553974, 1, 3);
(%o10) 7.14188773335993878063157787733b11
(%i11) delta(714195944281,3, modulop1p2(3, 714195944281));
(%o11) false
(%i12) v2(714195944281, 714193553974, 1, 4);
(%o12) 7.14186383004993475900066029253b11
(%i13) delta(714195944281,4, modulop1p2(4, 714195944281));
(%o13) [714191163667, 4.18357703366352418061779872005b-7]
(%i14) v2(714195944281, 714191163667, 4, 5);
(%o14) 7.14189968508510919964289242979b11
(%i15) delta(714195944281,5, modulop1p2(5, 714195944281));
(%o15) false
(%i16) v2(714195944281, 714191163667, 4, 6);
(%o16) 7.14188773348010840036651324299b11
(%i17) delta(714195944281,6, modulop1p2(6, 714195944281));
(%o17) false
(%i18) v2(714195944281, 714191163667, 4, 7);
(%o18) 7.14187578185510713252166124388b11
(%i19) delta(714195944281,7, modulop1p2(7, 714195944281));
(%o19) false
(%i20) v2(714195944281, 714191163667, 4, 8);
(%o20) 7.14186383021010529569837096844b11
(%i21) delta(714195944281,8, modulop1p2(8, 714195944281));
(%o21) [214899754753, 1.23165381973183718500308714283b-11]
(%i22) v2(714195944281, 214899754753, 8, 9);
(%o22) 1.08650074855601945444046486567b11 %i
Итак, на
уперлись. Имеем в порядке убывания точности:
Код:
(%i33) bfloat(sqrt(714195944281));
(%o33) 8.45101144408762471059270771886b5
(%i34) bfloat(sqrt(87264806974)+sqrt(302164561727));
(%o34) 8.45101144408762471059263484866b5
(%i35) bfloat(sqrt(2)+sqrt(714193553976));
(%o35) 8.45101144408762470935511324262b5
(%i36) bfloat(sqrt(8)+sqrt(714191163675));
(%o36) 8.45101144408762470811751462432b5
-- Вс янв 15, 2023 17:12:19 --много ли радости в таком решении
Для составных общего вида радости немного, поскольку там наблюдается экспоненциальный рост от количества простых делителей, плюс факторизация нужна.
На алгоритм, который я реализовал в виде функции modulop, оказывается указывал еще
waxtep:
Тогда, для простого
, можно попробовать перебирать значения
по возрастанию (причем, не все
будут разрешены по соображениям взаимности) и в лоб извлекать из них корни по модулю
, - здесь же есть быстрые алгоритмы (не имел реального опыта реализации, но вот пишут: Алгоритм Тонелли—Шенкса
; есть некоторые оговорки, но, на первый взгляд, алгоритм не выглядит устрашающим/безнадежным).
https://ru.wikipedia.org/wiki/%D0%90%D0 ... 1%81%D0%B0