概念
\(\forall c \in N^+\),如果 \(\exists x^2 \equiv c \pmod p\),则称 \(c\) 是模 \(p\) 的二次剩余。
对于 \(p\) 是奇质数的情况,有简单的方法求出同余方程 \(x^2 \equiv c \pmod p\) 的解。
思路
结论:
-
当且仅当 \(c^{\frac{p - 1}{2}} = 1\) 时 \(c\) 是模 \(p\) 的二次剩余。
-
模 \(p\) 意义下的二次剩余有 \(\frac{p - 1}{2}\) 个。
证明可能是伪证,就不放了。
根据结论可以导出 Cipolla 算法。
首先需要找出一个同余系中的数 \(a\) 使得 \(a^2 - c\) 是非二次剩余。
对于同余方程 \(x^2 \equiv c \pmod p\),因为非二次剩余的数量在 \(\frac{p}{2}\) 左右,所以随机检验数的方式可以在期望 \(2\) 次的时间内找到一个 \(a\).
定义 \(i^2 \equiv a^2 - c \pmod p\),这里 \(i\) 不是同余系中的数,可以认为是虚数。
此时有结论:\((a + i)^{p + 1} \equiv c \pmod p\),并且 \((a + i)^{p + 1}\) 的虚部恰为 \(0\).
证明不太会,咕了。
直接写个复数类实现就行。
代码
#include <cstdio>
#include <ctime>
#include <cstdlib>
using namespace std;
#define swap(x, y) x ^= y ^= x ^= y
typedef long long ll;
int t, mod, c;
ll Ipw2;
struct cpx
{
ll real, imag;
cpx operator * (const cpx& rhs) const { return (cpx){(real * rhs.real + imag * rhs.imag % mod * Ipw2) % mod, (real * rhs.imag + imag * rhs.real) % mod}; }
} ;
int qpow(int base, int power)
{
int res = 1;
while (power)
{
if (power & 1) res = 1ll * res * base % mod;
base = 1ll * base * base % mod, power >>= 1;
}
return res;
}
cpx qpow(cpx base, int power)
{
cpx res = (cpx){1, 0};
while (power)
{
if (power & 1) res = res * base;
base = base * base, power >>= 1;
}
return res;
}
int main()
{
srand(time(0));
scanf("%d", &t);
while (t--)
{
scanf("%d%d", &c, &mod);
if (c == 0) { puts("0"); continue; }
if (qpow(c, (mod - 1) / 2) == mod - 1) { puts("Hola!"); continue; }
int a;
while (true)
{
a = (rand() % (mod - 1) + mod) % (mod - 1) + 1;
Ipw2 = (1ll * a * a - c + mod) % mod;
if (qpow(Ipw2, (mod - 1) / 2) == mod - 1)
{
cpx w = (cpx){a, 1};
w = qpow(w, (mod + 1) / 2);
ll r1 = w.real, r2 = mod - w.real;
if (r1 > r2) swap(r1, r2);
printf("%lld %lld\n", r1, r2);
break;
}
}
}
return 0;
}
标签:剩余,power,二次,int,res,cpx,base,mod
From: https://www.cnblogs.com/lingspace/p/er-ci-sheng-yu.html