3月12日 二次剩餘(shanks解法)
阿新 • • 發佈:2019-02-01
演算法原理請wiki:Tonelli–Shanks algorithm,迅速深入理解是不太可能的,與cipola演算法相比,shanks解法更數論一點。
(這個演算法是正常的,但是還是tle)
大概流程是:
令其中Q是奇數。若s=1,即
由尤拉判定直接解出對於其他情況,隨機選擇一個z(這個跟cipolla很像),使得,令,並令
(1)若
(2)否則,找一個最小的i,,使得令,,,,,重複(1)。
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
using namespace std;
#define ll long long
ll QP(ll a, ll b, ll p)
{
a %= p;
ll tmp = 1;
while (b)
{
if (b & 1)tmp = tmp*a%p;
b >>= 1;
a = a*a%p;
}
return tmp;
}
ll le(ll n, ll p)
{
return QP(n, (p - 1) >> 1, p);
}
ll solve(ll n, ll p)
{
ll tp = p - 1;
ll s = 0;
while (!(tp & 1))
{
s ++;
tp >>= 1;//tp 也就是 Q
}
if (s == 1)
{
return QP(n, (p + 1) / 4, p);
}
ll z;
while (true)
{
z = rand() % p;
if (le(z, p) + 1 == p)
break;
}
ll c = QP(z, tp, p);
ll R = QP(n, (tp + 1) >> 1, p), t = QP(n, tp, p), M = s;
while (true)
{
if (t == 1)return (R%p + p) % p;
ll i = 1, j = 2;
while (QP(t, j, p) != 1)
{
i++;
j = 1 << j;
}
ll po = 1;
for (int i = 1; i <= M - i - 1; i++)po <<= 1;
ll b = QP(c, po, p);
// ll b=QP(c,1<<(M-i-1),p);
R = R*b%p;
t = t*b*b%p;
c = b*b%p;
M = i;
}
return 0;
}
int main()
{
int num;
scanf("%d", &num);
for (int i = 0; i < num; i++)
{
ll n, p;
scanf("%lld%lld", &n, &p);
if (n == 2)
{
if (n%p == 1)printf("1\n");
else printf("No root\n");
continue;
}
if (le(n, p) + 1 == p)
{
printf("No root\n");
continue;
}
ll a = solve(n, p);
ll b = p - a;
if (a > b)std::swap(a, b);
if (a == b)
{
printf("%lld\n", a);
}
else printf("%lld %lld\n", a, b);
}
return 0;
}