二次剩余的判定及Cipolla算法
二次剩余
是奇素数。所有的运算都是在群中的运算。方程问是否有解,以及解是什么?若有解,就是模的二次剩余;若无解,则就是模的非二次剩余。
-
,显然只有唯一解.
-
,有解等价于;无解等价于.
群恰好有一半的元素是二次剩余,一半的元素不是二次剩余。当元素是二次剩余是,解有且只有两个,且,即解
因此,验证是否是二次剩余可以用快速模幂,复杂度.
对于二次剩余,求解x使用[Cipolla]{.underline}(洋葱? 奇波拉?)算法。
Cipolla(洋葱?)算法
这是一个随机性算法,复杂度是
这个算法是在域上进行运算的,在这个域上做乘法、幂运算,然后解的那个表达式算出来之后,一定是属于域的,是的扩充。
-
使用随机的方法,找到一个满足满足不是二次剩余。需要检验是不是二次剩余的期望次数是.每次检验是.
-
定义域上的*“虚部单位”*为,其中,且.
-
的其中一个**解是*注意,虽然运算是上的,但是右边的那个式子算出来的结果刚好是"虚部"*为0的元素。原因是与这两个方程都有且仅有两个解。而前面方程的解显然是后面两个方程的解,所以前面方程的解。所以前面这个方程和后面这个方程的解是完全一样的。而对于后面这个方程代入后面的方程去验证(为了计算简洁,先证明了一些性质),会发现它的确是解,于是这个式子就是一开始方程的解。
证明
判别准则的证明
,有解等价于;无解等价于.
以上这个命题的证明,主要分为——
这个的证明主要是需要用到是个循环群,每个元素都可以表示成生成元的幂次的形式。
首先,明确一点,因为,再运用根据二次探测定理,容易得到取值只能是,即或.
- 有解,即是二次剩余,则算出来是,这个将换成,再根据费马小定理易得。
- 无解,即不是二次剩余,容易推出表示成生成元的幂次的形式()中的只能是奇数(否则,取就可以导出)。将表示成的形式,然后将代入容易得到.因为是生成元,所以阶是,指数小于时,算出来都不是单位元.所以(别忘了只能取).
虽然以上每一点都只进行了单向推导,但是以上两点综合起来,就说明了“,有解等价于;无解等价于.”
解的证明
算法构造的解,运算是在,其中.
对于
并且定义了,
定义完了之后,可以通过基本的代数知识,证明是一个域。
原方程是在在域内,而将方程的研究域改为之后。
无论是原域还是新域,解的个数至多只有两个。(这个是拉格朗日定理得出来的,暂时不懂朗格朗日定理,搁置,记结论)。因此,a是二次剩余的情况下,原方程有解,这两个解显然是新域下的解。又因为新域下没有更多解,所以新域下的解就是原域下的解。
因此,证明就只剩下验证符合方程即可。
直接代入计算就好了。只是由于为了计算比较简洁,有条理,不会算不下去,先给出了不少性质(定理)。这个别的博客基本都有,就不复言了。
下面为模板:
域乘法与幂运算模板
应当放在**mod_sys(见快速乘幂)**的模板前面。
// 二次剩余模板,注意,各个函数都假设p^2即mod^2不会爆ll
// 域F_p的拓展域F_{p^2}
struct F_p2_node{ll a,b;}; // F_p2_node所有的运算都应该通过F_p2_sys来管理调用。
// 预设所有传入参数都是合法的。
struct F_p2_sys{
ll p,w2;
typedef F_p2_node nd;
nd mlt(const nd&x, const nd&y) const {
nd ans;
//(x.a+x.bw)(y.a+y.bw)
ans.a = (x.a*y.a%p+x.b*y.b%p*w2%p)%p;
ans.b = (x.b*y.a%p+x.a*y.b%p)%p;
return ans;
}
// pre n>=0 非递归版 不可改成引用!
nd pow(nd a, ll n) const {
if (n == 0) {return nd{1,0};}
// 始终维持要求的数可以表示为(a)^n*t
nd t{1,0};
while (n > 1)
{
if (n&1) t = mlt(t,a);
n >>= 1; a = mlt(a,a);
}
return mlt(t,a);
}
};
Cipolla洋葱算法实现模板
需要使用先用快速乘幂的模板中的mod_sys,实际上,这个模板只是给那个mod_sys增加了成员函数。
struct mod_sys{
// template code from 快速乘幂 见前面章节
some code....
// 预设a \not\equiv 0 (mod mod) 返回a是否是二次剩余
// 预设mod是奇素数
// 预设p^2不会爆long long,使用pow 若爆则改用pow_v2或者__int128
bool is_quadratic_residue(ll a) {
return 1 == pow(a,mod-1>>1);
}
// 解方程x^2 = a (mod mod) p = mod 是奇质数
// 返回是否有解。如果有x0,x1(x0<=x1)存储解 仅解是0的时候取等号
// 洋葱算法。
// 预设p^2不会爆long long,使用pow 若爆则改用pow_v2或者__int128
bool sqrt(ll a,ll& x0, ll& x1) { // 需要一个随机数生成器
a = to_std(a);
if (a == 0) {x0 = x1 = 0; return true;}
if (!is_quadratic_residue(a)) return false;
uniform_int_distribution<> dis(1, mod-1);
ll b,w2;
while(true) {
b = dis(gen);
w2 = to_std((b*b)%mod-a);
if (!is_quadratic_residue(w2)) break;
}
// x = (b+w)^{(p+1)/2}
F_p2_sys fp2{mod,w2}; // p,w2
F_p2_node t{b,1}; // a,b(means a+bw)
auto x = fp2.pow(t,mod+1>>1);
x0 = to_std(x.a); // assert(x.b==0);
x1 = mod-x.a;
if (x0 > x1) swap(x0,x1);
return true;
}
};

浙公网安备 33010602011771号