• 二次剩余(Cipolla)


    question

    求解

    [x^2 equiv n (mod p) ]

    使用((Cipolla))算法,仅可求解 (p) 是奇素数的情况

    解的个数

    我们称一个数为二次剩余当且仅当存在 (x^2 equiv n (mod p)) 否则为非二次剩余

    考虑二次剩余 (n) 有多少个解

    (n) 的解中任取出 2 个 (x_0, x_1, x_0 e x_1)

    [x_0^2 equiv x_1^2 ]

    移项

    [x_0^2 - x_1^2 equiv 0 ]

    [(x_0 + x_1) (x_0 - x_1) equiv 0 ]

    已知 (x_0 e x_1)(x_0 + x_1 equiv 0)

    由此可知 (x_0, x_1) 互为相反数,且每一对相反数都对应不同的二次剩余

    而且由于 (p) 是奇素数 (x_0 e x_1)

    那么我们知道了,奇素数 (p)(frac {p - 1} {2}) 个二次剩余

    欧拉准则

    我们知道了解的数量,如何判断一个数 (n) 是不是二次剩余呢?

    (n e 0)

    • (n) 是二次剩余 (iff n^{frac{p-1}{2}} equiv 1)

    引理1

    [n ^ {frac{p - 1} 2} equiv pm 1 (mod p) ]

    • 证明

    由欧拉定理得知

    [n ^ {p - 1} equiv 1 ]

    [n ^ {2 frac {p - 1} 2} equiv 1 ]

    [n ^ {frac {p - 1} 2} equiv sqrt 1 ]

    (n ^ {frac {p - 1} 2}) 是 1 开方的结果,所以只能是 (pm 1)

    证明

    (g)(p) 的原根,(n equiv g^k)

    (n ^ {frac {p - 1} 2} equiv 1)(g ^ {k frac {p - 1} 2} equiv 1)

    因为 (g) 是原根 ((p - 1) | ( k frac{p - 1} 2))

    那么 (k) 一定是偶数

    (g^{frac k 2}) 就是 (n) 的一个解

    由此我们得出如果 (n ^ {frac{p-1}{2}} equiv 1)(n) 是二次剩余

    如果 (n) 是二次剩余 (n ^ {frac{p - 1} 2} equiv (x^2)^{frac{p - 1} 2} equiv x^{p - 1} equiv 1)

    (Cipolla) 算法

    对于 (x^2 equiv n) 找到一个 (a) 使得 (a^2 - n)非二次剩余

    这一步期望复杂度 (O(2)) 因为非二次剩余个数接近 (frac n 2)

    定义 (i^2 equiv a^2 - n, i) 是类似复数一样的东西

    所有数都可以表示成 (A + Bi)

    ((a + i)^{frac{p + 1} 2}) 即为一个解

    引理 1

    [i^p equiv -i (mod p) ]

    • 证明

    [i ^ p equiv (i^2) ^ {frac{p - 1} 2} i equiv (a^2 - n) ^ {frac{p - 1} 2} i equiv -i ]

    因为 (a^2 - n) 是非二次剩余,((a^2 - n)^ {frac{p - 1} 2} equiv -1)

    引理 2

    [(A + B)^p equiv A^p + B^p (mod p) ]

    [(A + B)^p = sum_{i = 0} ^ p inom p i A^iB^{p - i} ]

    [inom p i = frac{p!}{i!(p - i)!} ]

    因为 (p) 是素数,所有包含 (p) 的阶乘都被模成了 (0) 只剩下了 (inom p 0 B^p + inom p p A^p)

    证明

    [(a + i)^{p + 1} equiv (a^p + i^p) (a + i) equiv (a + i) (a - i) equiv a^2 - i^2 equiv n ]

    ((a + i)^{frac{p + 1} 2}) 就是答案

    那么 ((a + i)^{frac{p + 1} 2}) 的虚部是否为 0 呢

    假设存在 ((A + Bi) ^ 2 equiv n, B e 0)

    [A^2 + B^2 i^2 - n equiv 2ABi ]

    左边虚部为 0 说明右边也为 0

    (AB equiv 0),因为 (B e 0)(A equiv 0),那么

    [(A + Bi)^2 equiv B^2 i^2 equiv n ]

    [i^2 equiv n B^{-2} ]

    因为 (n) 是二次剩余,设 (x^2 equiv n)

    [i ^ 2 equiv (xB^{-1}) ^ 2 ]

    (i ^ 2) 显然是二次剩余,与前提矛盾

    证毕

    板子

    #include <bits/stdc++.h>
    using namespace std;
    #define I inline
    #define int long long
    int imuli, mod, n;
    struct cpx{
    	int a, b;
    	cpx(int a, int b): a(a), b(b) {}
    	I cpx operator * (const cpx &rhs) const {
    		return cpx((a * rhs.a % mod + b * rhs.b % mod * imuli % mod) % mod, (a * rhs.b % mod + b * rhs.a % mod) % mod);
    	}
    };
    I cpx ksm(cpx a, int b){
    	cpx ans(cpx(1, 0));
    	while(b){ if(b & 1) ans = ans * a; b >>= 1; a = a * a; }
    	return ans;
    }
    I int ksm(int a, int b){
    	int ans = 1;
    	while(b){ if(b & 1) ans = ans * a % mod; b >>= 1; a = a * a % mod; }
    	return ans;
    }
    I bool check(int n){
    	return ksm(n, (mod - 1) / 2) == 1;
    }
    I void Main(){
    	cin >> n >> mod;
    	n %= mod;
    	if(!n) return cout << 0 << '
    ', void();
    	if(n == 2) return cout << "1" << '
    ', void();
    	if(!check(n)) return cout << "Hola!" <<'
    ', void();
    	int a;
    	do{
    		a = (1ll * rand() + 1ll * rand() * rand()) % mod;
    	}while(check((a * a + mod - n) % mod));
    	imuli = (a * a % mod + mod - n) % mod;
    	int x0 = ksm(cpx(a, 1), (mod + 1) / 2).a, x1 = mod - x0;
    	if(x0 > x1) swap(x0, x1);
    	cout << x0 << " " << x1 << '
    ';
    }
    signed main(){
    	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    	int T; cin >> T;
    	while(T--) Main();
    	return 0;
    }
    
    • (tps): cpx 里面的 b 可以 % mod 是因为最终虚部为 0 剩下的 b 在做乘法的时候需要 % mod
  • 相关阅读:
    【转】编写高质量代码改善C#程序的157个建议——建议61:避免在finally内撰写无效代码
    【转】编写高质量代码改善C#程序的157个建议——建议60:重新引发异常时使用Inner Exception
    【转】编写高质量代码改善C#程序的157个建议——建议59:不要在不恰当的场合下引发异常
    【转】编写高质量代码改善C#程序的157个建议——建议58:用抛出异常代替返回错误代码
    【转】编写高质量代码改善C#程序的157个建议——建议57:实现ISerializable的子类型应负责父类的序列化
    elasticsearch从入门到出门-02-简单的CRUD
    elasticsearch从入门到出门-01windows上安装使用
    Idea 使用的技巧和设置
    Ubuntu16.04 安装搜狗输入法
    Ubuntu 下安装JDK1.8
  • 原文地址:https://www.cnblogs.com/XiaoVsun/p/13059958.html
Copyright © 2020-2023  润新知