🔖 math数论扩展欧几里得算法中国剩余定理Baby Step Gaint Step

扩展欧几里得算法

问题描述

已知 a,b 为常整数,求方程 ax+by=gcd(a,b) 的一对整数解?

问题简析

{A=amodbB=b,构造方程

(1)AX+BY=gcd(A,B).

由于 gcd(a,b)=gcd(amodb,b)=gcd(A,B),所以

ax+by=(aab×b+ab×b)×x+by=(aab×b)×x+(by+ab×bx)=(a(modb))×x+b×(y+ab×x)=Ax+B×(y+ab×x)

因此有 {X=xY=y+ab×x{x=Xy=Yab×X

方程 (1) 的构造与辗转相除有异曲同工之妙,需要说明的是, gcd(x,0)=x。所以:

  1. b=0 时,我们得到一组解 {x=1y=0

  2. b0 时,套用辗转相除的框架求出 (X,Y),再得到 (x,y)

程序实现

1
2
3
4
5
6
7
8
9
10
11
typedef long long LL;
void extendEuclid(LL a, LL& x, LL b, LL &y, LL& d) { // d=gcd(a,b)
if (!b) {
d = a;
x = 1;
y = 0;
} else {
extendEuclid(b, y, a%b, x, d);
y -= (a / b) * x;
}
}

上述代码中,将 d = a 的执行顺序放在最前面是一个小技巧,因为这样就可以像下面这样调用函数,此时不需要预先将 gcd(a,b) 求出来,而是在整个算法中顺带求出的:

1
2
3
extendEuclid(a, x, b, d, d);
// or
extendEuclid(a, d, b, d, y);

值得一提的是,扩展欧几里得算法解出的一组解是所有解中|x|+|y| 最小的;

问题扩展

求方程 ax+by+c=0 有多少对整数解,满足 x[x1,x2], y[y1,y2]

由前文可知,当 gcd(a,b)c 时,方程有解。因为方程 ax+by=gcd(a,b) 的解乘上 cgcd(a,b) 就是此方程的一组解。由此,我们可以推出 ax+by=kgcd(a,b),所以,当 gcd(a,b)c 时,方程无解。

那有多少个解呢?

如果 (x0,y0),(x1,y1) 是方程的两个解,那么 a(x1x0)+b(y1y0)0.
a=agcd(a,b), b=bgcd(a,b),显然,gcd(a,b)=1
所以,(x1x0)b, (y0y1)a,并且 x1x0by0y1a

一般地,若 (x0,y0) 是方程 ax+by+c=0 一组解,那么 (x0+kb,y0ka) 也是一组解。其中,b=bgcd(a,b), a=agcd(a,b)k 为任意整数。

逆元

对于任意整数 A, N,如果存在一个整数 B 使得 AB1modC,那么,AB 称作模 C 意义下的互为逆元,记做 A1=BB1=A

求解逆元

  1. 如果 gcd(A,N)=1,根据欧拉定理,有 A1=Nφ(N)1,特别地,当 N 是素数时,φ(N)=N1。另外,由于 gcd(A,N)=1,构造方程 Ax+Ny=1,用扩展欧几里得算法也可求出 A 在模 N 意义下的逆元 A1=x.

  2. 如果 gcd(A,N)1,由前文可知,方程 Ax+Ny=1kgcd(A,N) 不存在整数解,故不存在逆元。

程序实现

  1. 欧拉定理求逆元。当 N 是素数时,φ(N)=N1A 的逆元为 AN2

    inv.euler.cpp 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    typedef long long LL;
    LL powerMod(LL A, LL x, LL mod) { // 返回 A^x%mod
    LL ans = 1;
    for(; x > 0; x >>= 1, A = A * A % mod) {
    if (x & 1) ans = ans * A % mod;
    }
    return ans;
    }
    LL inv(LL A, LL mod) {
    return powerMod(A, mod-2, mod);
    }
  2. 扩展欧几里得求逆元

    inv.euclid.cpp 
    1
    2
    3
    4
    5
    6
    7
    typedef long long LL;
    LL inv(LL A, LL mod) {
    LL x, y, d;
    extendEuclid(A, x, N, y, d);
    // 若 A 和 mod 不互质,则不存在逆元
    return d == 1 ? x : -1;
    }

中国剩余定理

若正整数 m1,m2,,mk 两两互素,且 0a1<m1,0a2<m2,,0ak<mk. 求下列同余方程组的整数解:

(2)xa1(modm1)xa2(modm2)xak(modmk)

问题简析

wi=jimj,显然有:

(3)gcd(wi,mj)={1,i=jmj,ij

构造方程 wixi+miyi=gcd(wi,mi)=1。那么,由方程组 (3) 有,

(4)wixi (modmj){1,i=j0,ij

结合方程组 (2) 和方程组 (4),进而有,

(5)aiwixi (modmj){ai,i=j0,ij

所以,x=i=1k(aiwixi) 为同余方程组 (2) 的解。更一般地,方程的解为

x=i=1k(aiwixi)+ki=1kmi

剩下的问题仅需利用扩展欧几里得算法求出 xi

程序实现

crt.cpp 
1
2
3
4
5
6
7
8
9
10
LL crt(int N, int* a, int* m) {
LL M = 1, ans = 0;
for (int i = 0; i < N; ++i) M *= m[i];
for (int i = 0; i < N; ++i) {
LL w = M / m[i], x, y, d;
extendEuclid(w, x, m[i], y, d);
ans = (ans + x * w * a[i]) % M;
}
return ans;
}

Baby Step Gaint Step

求一个整数 x[0,C1] 使得 AxB(modC)。其中,C 为素数。

问题简析

注意到 C 是一个素数,那么对于任意 X 都存在一个模 C 意义下的逆元 X1,使得 XX11modC。取任意正整数 m,如果事先知道所有的 Ax0modC,(0x0<m) 的值,如何判断是否存在一个 x 满足 kmx=x0+km<(k+1)m,使得 Ax=Ax0+kmBmodC 成立呢?

注意到,令方程两边同乘以 Akm 的逆元 Akm,得到 Ax0BAkmmodC。所以,我们仅需将映射关系 x0f(x0)=Ax0modC,(0x0<m) 存进哈希表或平衡树中,再从 0m 枚举 k 的值,对于每个 k 检查哈希表或平衡树中是否存在 BAkmmodC,若存在即可取得对应的 x0 的值。

m 应该取多少合适呢?注意到这样做的时间复杂度是 O(m+Cm)[1],不难证明,当 mC 时复杂度最优。

程序实现

bsgs.cpp 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
typedef long long LL;
unordered_map<LL, int> emp;
int bsgs(LL A, LL B, LL C) {
emp.clear();
int M = ceil(sqrt(1.0 * C));
LL AA = 1;
for (int x0 = 0; x0 < M; ++x0) {
if (!emp.count(AA)) emp[AA] = x0;
AA = AA * A % C;
}
LL Am = inv(powerMod(A, M, C), C);
for (int k = 0; k < M; ++k) {
if (emp.count(B)) return emp[B] + k * M;
B = B * Am % C;
}
return -1;
}

Extend Baby Step Gaint Step

求一个整数 x[0,C1] 使得 AxB(modC)。其中,C 为合数。

问题简析

在分析之前,先看一个定理:若 kAkBmodkC,则有,ABmodC.
这个定理不难证明,事实上,仅需构造方程 kAkB=kkC,在等式两边同除以 k 即可得证。

再回过头来看普通版的 Baby Step Gaint Step 是如何工作的。注意到, Gaint Step 能够成功走出去的前提是 Am 存在模 C 意义下的逆元 Am。由前文可知,Am 存在的充要条件为 gcd(Am,C)=1,此时必有 gcd(A,C)=1 [2]。设 Gt=gcd(At,C),C=CGt,D=AtGt,根据上面的定理,不难想到,我们只要找出某个整数 0t<C 满足 Gt=Gt+1,则上述方程可转化为

Ax=Gt×AtGt×Axt=Gt×D×AxtC=Gt×CGt=Gt×C

不难发现,DC 是互质的;因为 Gt=Gt+1,所以 AC 也是互质的 [3]

接下来讨论 BGt 的关系:

  • GtB,结合前面给出的定理,有

    AxBmodCDAxtBGtmodC

    又因为 DC 是互质的,所以存在模 C 意义下的逆元 D1;进一步将同余方程化为

    Axt(D1BGt)modC

    由于 AC 是互质的,根据前面的分析,直接套用普通版的 Baby Step Gaint Step 即可解决,这部分算出的答案加上 t 就是原同余方程的解。

  • GtB,由于 AxmodC=(Gt×D×Axt)mod(Gt×C)=KGt,所以无解。

程序实现

ebsgs.cpp 
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
typedef long long LL;
unordered_map<LL, int> emp;
int ebsgs(LL A, LL B, LL C) {
LL D = 1, cnt = 0;
for (LL G; (G = gcd(A, C)) != 1; cnt += 1) {
if (B % G) return -1;
B /= G;
C /= G;
D = D * A / G % C;
if (D == B) return cnt;
}
emp.clear();
int M = ceil(sqrt(1.0 * C));
LL AA = 1;
for (int x0 = 0; x0 < M; ++x0) {
if (!emp.count(AA)) emp[AA] = x0;
AA = AA * A % C;
}
B = B * inv(D, C) % C;
LL Am = inv(powerMod(A, M, C), C);
for (int k = 0; k < M; ++k) {
if (emp.count(B)) return emp[B] + cnt + k * M;
B = B * Am % C;
}
return -1;
}

练习

#ProblemCategorySolution / Code
151Nod/X^A Mod PBSGSsolution.cpp
251Nod/X^A Mod B扩展BSGSsolution.cpp
3SPJ/MOD扩展 BSGSsolution.cpp
4UVa/Code Feat中国剩余定理solution.cpp

小结

拖沓了很久,终于耐下性子整理了一下。
鉴于本人能力有限,有误之处欢迎指正。

  •  [1]: 

    使用哈希表,查询均摊 O(1)

  •  [2]: 

    gcd(A,C)1,则 gcd(Am,C)gcd(A,C)>1

  •  [3]: 

    Gt=Gt+1 说明 gcd(CGt,A)=1

© 2017-2025 光和尘有花满渚、有酒盈瓯

Comments