zoukankan      html  css  js  c++  java
  • 二次剩余学习笔记

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

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

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

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

    1.勒让德符号

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

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

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

    2.解的数量

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

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

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

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

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

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

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

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

    3.欧拉判别

    (left(dfrac{n}{p} ight) = n^{{p-1}over 2} mod p)

    4.Cipolla

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

    我们先随机出一个 (a) 使得 (a^2-n) 不存在二次剩余, 由于非二次剩余的数量接近于 (pover 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 = displaystylesum_{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-1over 2} = w(a^2-n)^{p-1over 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+1over 2}), 又因为 (p) 为奇素数,所以 (p+1over2) 肯定为整数,那么 (x) 也一定可以算出来。

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

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

    我们来简单证明一下:

    (x) 写成复数形式可得: (x^2equiv 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 eq 0), 那么只能 (a = 0)

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

    由于 (nover 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!
    ");
            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("
    ");
        else printf("%lld
    ",ans2);
    }
    signed main()
    {
        T = read();
        while(T--)
        {
            n = read(); p = read();
            if(n == 0) printf("%d
    ",0);
            else slove(n,p);
        }
        return 0;
    }
    
  • 相关阅读:
    Oracle DBLink 使用情况
    asp.net里AjaxPro简单入门教程
    AjaxPro异步调用的超时设置
    ORA-01552: 非系统表空间 'USERS' 不能使用系统回退段的处理
    andoid 监听返回键退出
    WinForm调用user32.dll实现全屏
    C# Panel 打开 Form 窗口的方法
    Android获取日期及星期的方法
    WinForm 自定义对话框 获取返回值
    52. (待补) 实现对 无头单链表 的基本操作
  • 原文地址:https://www.cnblogs.com/genshy/p/14387595.html
Copyright © 2011-2022 走看看