二次剩余学习笔记

二次剩余, 俗称模意义开根。

实际上是求 \(x^2\equiv n \pmod p\) 的解。

这有两种解法,一个是利用原根来求,另外一种就是 \(Cipolla\) 算法(只能适用于奇素数的情况)。

我们下面主要介绍第二种。

1.勒让德符号

对于一个正整数 \(n\), 他的勒让德符号 \(\left(\dfrac{n} {p}\right)\)定义为:

\[\left(\dfrac{n}{p}\right) = \begin{cases} 0 \qquad\ n 和 p 同余\\ 1 \qquad n 在模p意义下存在二次剩余\\ -1 \qquad n 在模p意义下不存在二次剩余 \end{cases} \]

根据这个我们可以初步判断一个数是否存在二次剩余。

2.解的数量

对于二次剩余 \(x^2\equiv n\pmod p\) 有多少个解?

假设有多组解,对于任意不相同的两个解有 \(x_0^2 = x_1^2\)

移项可得: \((x_0-x_1)(x_0+x_1) \equiv 0 \pmod p\)

因为 \(p\) 是奇素数,且 \(x_0 \neq x_1\), 所以 \(x_0-x_1 \neq 0\)

那么 \(x_0+x_1\equiv 0\), 也就是说 \(x_0\)\(x_1\) 在模 \(p\) 的意义下互为相反数。

换而言之就是这个方程只有两个解,且这两个解在模 \(p\) 的意义下互为相反数。

另外,每一对相反数都可以对应一个二次剩余,而且这些二次剩余是两两不同的。

也就是说 二次剩余的数量恰为 \(p \over 2\) ,非二次剩余的数量也为 \({p\over 2}\)

3.欧拉判别

\(\left(\dfrac{n}{p} \right) = n^{{p-1}\over 2} \bmod p\)

4.Cipolla

求解二次剩余方程 \(x^2\equiv n \pmod p\)

我们先随机出一个 \(a\) 使得 \(a^2-n\) 不存在二次剩余, 由于非二次剩余的数量接近于 \(p\over 2\)

所以期望随机两次就可以随机到一个合适的 \(a\)

然后设 \(w = \sqrt{a^2-n}\) , 但 \(a^2-n\) 不存在二次剩余,怎么把 \(w\) 求出来呢?

我们实际上不需要把这个 \(w\) 求出来。 类似于复数,定义一个 \(w\) 使得 \(w^2 = a^2-n\)

这样每个数都可以表示成: \(a+bw\) 的形式, \(a\)\(b\) 都是模 \(p\) 意义下的数。

有一个结论: \((a+w) ^{p+1} = n\)

引理1: \((A+B)^p = A^p + B^p\)

证明:由二项式定理展开可得: \((A+B)^p = \displaystyle\sum_{i=1}^{p} C_{p}^{i} x^iy^{p-i}\)

在模 \(p\) 意义下,组合数 \(C_{p}^{i}\) 只有 \(C_{p}^{0},C_{p}^{p}\)\(1\) ,其他的由于分子上的阶乘被消掉了值为 \(0\).

所以 \((A+B)^p = A^p + B^p\)

引理2: \(w^{p} = -w\)

证明: 因为 \(w^{p} = w(w^2)^{p-1\over 2} = w(a^2-n)^{p-1\over 2} = -w\)

我们来通过两个引理证一下上面那个结论

\((a+w)^{p+1} = (a+w) (a^{p} + w^{p}) = (a+w)(a-w) = a^2 - w^2\)

又因为: \(w^2 = a^2 - n\) , 所以: \(n = a^2-w^2\)

得证: \((a+w)^{p+1} = n\)

可以得到 \(x = (a+w)^{p+1\over 2}\), 又因为 \(p\) 为奇素数,所以 \(p+1\over2\) 肯定为整数,那么 \(x\) 也一定可以算出来。

对于 \((a+bw)(c+dw) = (ac + dbw^2) + (ad+bc)w\) ,重载一下乘法,在进行快速幂即可。

还有一个问题,\(x\) 虚部一定为 \(0\)

我们来简单证明一下:

\(x\) 写成复数形式可得: \(x^2\equiv n \Rightarrow (a+bw)^2 \equiv n\)

展开可得: \(a^2 + 2abw + bw^2 \equiv n\)

移项可得: \(a^2+bw^2-n \equiv -2abw \Rightarrow a^2 + b(a^2-n) - n \equiv -2abw\)

因为左边虚部为 \(0\) ,所以右边的虚部肯定也为 \(0\), 即 \(ab = 0\)

假设说 \(b\neq 0\), 那么只能 \(a = 0\)

所以 \((bw)^2 \equiv n \Rightarrow w^2 = {n\over b^2}\),

由于 \(n\over b^2\) 在模 \(p\) 的意义下是确定的,所以 \(w^2\) 是二次剩余,这与前面所说的 \(w^2\) 不是二次剩余相矛盾。

所以 \(b = 0\), 即 \(x\) 的虚部肯定为 \(0\).

最后方程的两个解分别是 \(x\)\(p-x\)

code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<bits/stdc++.h>
using namespace std;
#define int long long
int T,n,p,a,w;
struct Complex
{
    int x,y;
    Complex(){}
    Complex(int a,int b){x = a, y = b;}
};
inline int read()
{
    int s = 0,w = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
    return s * w;
}
Complex operator * (Complex a,Complex b)
{
    Complex c;
    c.x = (a.x * b.x % p + a.y * b.y % p * w % p) % p;
    c.y = (a.x * b.y % p + a.y * b.x % p) % p;
    return c;
}
Complex Pow(Complex a,int b)
{
    Complex c = Complex(1,0);
    
    for(; b; b >>= 1)
    {
        if(b & 1) c = c * a;
        a = a * a;
    }
    return c;
}
int ksm(int a,int b)
{
    int res = 1;
    for(; b; b >>= 1)
    {
        if(b & 1) res = res * a % p;
        a = a * a % p;
    }
    return res;
}
bool pd(int n)
{
    return ksm(n,(p-1)/2) == 1;
}
void slove(int n,int p)
{
    n %= p;
    if(!pd(n))
    {
        printf("Hola!\n");
        return;
    }
    srand(time(0)); rand();
    while(1)
    {
    	a = rand() % p;
    	w  = (a * a % p - n + p) % p;
    	if(ksm(w,(p-1)/2) == p-1) break;
	}
    Complex Ans = Pow(Complex(a,1),(p+1)/2);
    int ans1 = Ans.x, ans2 = p - Ans.x;
    if(ans1 > ans2) swap(ans1,ans2);
    printf("%lld ",ans1);
    if(ans1 == ans2) printf("\n");
    else printf("%lld\n",ans2);
}
signed main()
{
    T = read();
    while(T--)
    {
        n = read(); p = read();
        if(n == 0) printf("%d\n",0);
        else slove(n,p);
    }
    return 0;
}
posted @ 2021-02-08 09:10  genshy  阅读(239)  评论(0编辑  收藏  举报