对于二次剩余式 $$x^2\equiv n\pmod p$$ 其中$p$为奇素数,且$p \nmid n$,求$x$。

Cipolla算法

$Cipolla$和之前就写过的三次剩余是同一个思路,所以我在写完$Cipolla$后感觉好像什么都没变。

构造一个环

$$R=\frac{\mathbb{Z}_p[x]}{x^2-a}=\set{\alpha+\beta Y | \alpha,\beta,\in\mathbb{Z}_p,Y^2=a}$$

可知,

对于$z\in R$,即$z=\alpha+\beta Y$,有$z^{p-1}\equiv 1\pmod{p}$

如果$z^{\frac{p-1}{2}}=\beta_0 Y$,则有$(\beta_0 Y)^2\equiv\beta_0^2a\equiv1\pmod{p}$,即$\sqrt{a}\equiv\beta_0^{-1}\pmod{p}$

代码实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
mt19937 rng(__builtin_ia32_rdtsc());
inline ll randint(ll l, ll r) {
  return uniform_int_distribution<ll>(l, r)(rng);
}

ll qpow(ll x, ll n, ll mod) {
  ll res = 1LL;
  for (x %= mod; n > 0LL; n >>= 1, x = x * x % mod) {
    if (n & 1LL) res = res * x % mod;
  }
  return (res + mod) % mod;
}

struct R {
  ll a, p, x, y;

  R(ll _a, ll _p) : a(_a), p(_p) { x = 1LL, y = 0LL; }
  void rand() {
    x = randint(0, p - 1);
    y = randint(0, p - 1);
  }
  R &operator*=(const R &rhs) {
    ll _x = (x * rhs.x + y * rhs.y % p * a) % p;
    ll _y = (x * rhs.y + y * rhs.x) % p;
    x = _x, y = _y;
    return *this;
  }

  void pow(ll n) {
    R res(a, p), b = *this;
    for (; n; n >>= 1, b *= b) {
      if (n & 1LL) res *= b;
    }
    x = res.x, y = res.y;
  }
};

ll Cipolla(ll a, ll p) {
  a = (a % p + p) % p;
  if (a == 0) return 0LL;

  if (qpow(a, (p - 1) / 2, p) != 1LL) return -1LL;
  // No Solution

  if (p % 4 == 3) return qpow(a, (p + 1) / 4, p);

  R t(a, p);
  while (true) {
    t.rand(), t.pow((p - 1) / 2);
    if (t.x == 0 && t.y != 0) {
      return qpow(t.y, p - 2, p);
    }
  }

  assert(false);
  return -1;
}

Tonelli-Shanks算法

(来自柯召的《初等数论》)

  • $p\mod 4=3$

由于$n^{\frac{p-1}{2}}\equiv 1\pmod p$,得到$(\pm n^{\frac{p+1}{4}})^2\equiv n\pmod p$。


  • $p\mod 8=5$

先求$n=-1$的解。

由威尔逊定理有$-1\equiv (p-1)! \pmod p$,对于右侧有:

$$1\times2\times\cdots\times(\frac{p-1}{2})\times(p-\frac{p-1}{2})\times\cdots\times(p-1)\equiv\left((\frac{p-1}{2})!\right)^2\pmod p$$

$4\mid (p-1)$,所以负号没了。

同样的,由于$n^{\frac{p-1}{2}}\equiv 1\pmod p$,我们得到

$n$适合$n^{\frac{p-1}{4}}\equiv 1\pmod p$或$n^{\frac{p-1}{4}}\equiv-1\pmod p$

  1. $n^{\frac{p-1}{4}}\equiv 1\pmod p$,有$(n^{\frac{p+3}{8}})^2\equiv n\pmod p$
  2. $n^{\frac{p-1}{4}}\equiv-1\pmod p$,有$((\frac{p-1}{2})!\times n^{\frac{p+3}{8}})^2\equiv n\pmod p$

  • $p\mod 8=1$

设$p\equiv 1\pmod 8,(\frac{n}{p})=1,(\frac{N}{p})=-1$,则同余式有解 $$\pm n^{\frac{h+1}{2}}N^{s_k}$$ 其中$p=2^kh+1,2\nmid h,s_k\geq0$是某个整数。

证明:设$p=2^kh+1,k\geq3,2\nmid h$。

由于$(\frac{n}{p})=1, (\frac{N}{p})=-1$,我们得出

$$n^{2^{k-1}h}\equiv 1\pmod p$$ $$N^{2^{k-1}h}\equiv-1\pmod p$$

因此下面的两个同余式有且只有一个成立

$$n^{2^{k-2}h}\equiv 1\pmod p$$ $$n^{2^{k-2}h}\equiv-1\pmod p$$

故有非负整数$s_2=hf$($f=0$或$1$)使

$$n^{2^{k-2}h}\times N^{2^{k-1}s_2}\equiv1\pmod p$$

成立。

于是下面两个同余式有且只有一个成立

$$n^{2^{k-3}h}\times N^{2^{k-2}s_2}\equiv 1\pmod p$$ $$n^{2^{k-3}h}\times N^{2^{k-2}s_2}\equiv-1\pmod p$$

故又有非负的$s_3=s_2+2hf_1$($f_1=0$或$1$)满足下式

$$n^{2^{k-3}h}\times N^{2^{k-2}s_3}\equiv1\pmod p$$

因为$k$是有限整数,故必有一非负的$s_k$使得

$$n^h\times N^{2s_k}\equiv1\pmod p$$

$$n^{h+1}N^{2s_k}\equiv n\pmod p$$

$$\left(n^{\frac{h+1}{2}}N^{s_k}\right)^2\equiv n\pmod p$$


代码实现

时间复杂度,平均$\mathcal{O}(\log p)$,最坏$\mathcal{O}(\log^2 p)$。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
ll qpow(ll x, ll n, ll mod) {
  ll res = 1LL;
  for (x %= mod; n > 0LL; n >>= 1, x = x * x % mod) {
    if (n & 1LL) res = res * x % mod;
  }
  return (res + mod) % mod;
}

ll Tonelli_Shanks(ll a, ll p) {
  a = (a % p + p) % p;
  if (a == 0) return 0LL;

  if (qpow(a, (p - 1) / 2, p) != 1LL) return -1LL;
  // No Solution 

  if (p % 4 == 3) return qpow(a, (p + 1) / 4, p);

  ll k = __builtin_ctzll(p - 1), h = p >> k, N = 2;
  // p = 1 + h * 2^k
  while (qpow(N, (p - 1) / 2, p) == 1) N++;
  // find a non-square mod p

  ll x = qpow(a, (h + 1) / 2, p);
  ll g = qpow(N, h, p);
  ll b = qpow(a, h, p);

  for (ll m = 0; ; k = m) {
    ll t = b;
    for (m = 0; m < k && t != 1LL; m++) {
      t = t * t % p;
    }

    if (m == 0) return x;
    ll gs = qpow(g, 1 << (k - m - 1), p);

    g = gs * gs % p;
    b = b * g % p;
    x = x * gs % p;
  }

  assert(false);
  return -1;
}