同余问题常用定理证明

扩展欧几里得算法

扩展欧几里得定理:设 ab 不全为 0,则存在整数 xy,使得 ax+by=(a,b)

证明:设 d=(a,b),求 ax+by=d 的一组 (x,y)

欧几里得算法可知,bx+(amodb)y=d

bx+(aabb)y=day+b(xaby)=d

x=y,y=xaby

可以通过欧几里得算法递归迭代到 a=d,b=0 时可以得到此时 x=1,y=0,再利用上面推出的 x,yx,y 的关系进一步代入计算,得到 ax+by=(a,b) 的一组解。

利用归纳法,最后解的大小满足:|x|b,|y|a

int exgcd (int a, int b, int &x, int &y) {
    if (!b) {
        x = 1, y = 0;
        return a;
    }
    
    int d = exgcd (b, a % b, y, x);

    y -= (a / b) * x;

    return d;
}

欧拉定理 & 费马小定理

欧拉定理:若 (a,m)=1,则 aφ(m)1(modm)

证明:设 r1,r2,,rφ(m) 是模 m 的缩系,因为 (a,m)=1,所以 ar1,ar2,,rφ 也是模 m 的缩系。

r1r2rφ(m)ar1ar2arφ(m)aφ(m)r1r2rφ(m)

根据缩系性质,可约去 r1r2rφ(m),即得 aφ(m)1(modm)

费马小定理:若 p 为素数,则 ap11(modp)

证明:当 p 为素数 (a,p)=1,由于 φ(m1)=m1,带入欧拉定理可立即得到费马小定理。

线性同余方程

(a,m)=d,则一次同余方程:axb(modm),有解的充要条件是 db

设同余方程有解 x=c,则 m(acb),从而 d(acb),但 da,故 db

反过来,若 db,则同余方程与 adxbd(modmd) 同解,任取模 md 的一个完系 c1,,cmd,因 (ad,md)=1adc1bd,,adcmdbd 也是模 md 的完系,所以同余方程存在整数解,且形成模 md 的一个同余类。但 db 时,原同余方程(模 m)恰有 d 个解。

int x, y;
int d = exgcd (a, p, x, y);

if (b % d) {
    res = -1;
} else {
    x *= (b / d);
    res = (x % (p / d) + (p / d)) % (p / d);
}

中国剩余定理

中国剩余定理:设 m1,,mk 是两两互素的正整数,则对于任意整数 b1,,bk,一次同余方程组 xb1(modm1),,xbk(modmk) 必有解,且全部解是模 m1mk 的一个同余类。确切地说,同余方程组的解是 xM1M11b1++MkMk1bk(modm1mk)

其中 MiMi1 (1ik) 由条件 m1mkMimi,MiMi11(modmi) 决定。因为 (mi,Mi)=1,故有整数 Mi1,使得 MiMi11(modmi)。对 ji,有 mjMi,因此数 di=MiMi1,满足:di1(modmi),di0(modmj) (对 ji)。

易于验证,上面的解的”叠加“ b1d1++bkdk 满足同余方程组的解。

另一方面,如果 x,x 都是同余方程组的解,则 xx(modmi),1ik。由 m1,,mk 两两互素知,xx(modm1mk),因此同余方程组的解是模 m1mk 的一个同余类。

线性同余方程组

模数互质,使用中国剩余定理。

vector <int> A(n), B(n);
LL M = 1;
for (int i = 0; i < n; i ++ ) {
    cin >> A[i] >> B[i];
    M *= A[i];
}

LL res = 0;
for (int i = 0; i < n; i ++ ) {
    LL Mi = M / A[i];
    LL ti, x;
    exgcd (Mi, A[i], ti, x);
    res += B[i] * Mi * ti;
}

cout << (res % M + M) % M << "\n";

在模数不互质的情况下,设其中两个方程分别是 xa1(modm1),xa2(modm2)。转化为不定方程:x=m1p+a1=m2q+a2,其中 p,q 是整数,则有 m1pm2q=a2a2。由裴蜀定理,当 a2a1 不能被 (m1,m2) 整除时,无解;否则,扩展欧几里得定理得到一组可行解 (p,q),则这两个方程组的解是 xb(modM),其中 b=m1p+a,M=[m1,m2]。多个方程两两合并。

LL res = 0, m1, a1;
cin >> m1 >> a1;

for (int i = 0; i < n - 1; i ++ ) {
    LL m2, a2;
    cin >> m2 >> a2;

    LL k1, k2;
    LL d = exgcd (m1, m2, k1, k2);

    if ((a2 - a1) % d) {
        res = -1;
    }

    k1 *= (a2 - a1) / d;
    k1 = (k1 % (m2 / d) + (m2 / d)) % (m2 / d);

    LL m = abs (m1 / d * m2);
    a1 = k1 * m1 + a1;
    m1 = m;
}

if (res != -1) {
    res = (a1 % m1 + m1) % m1;
}

cout << res << "\n";
posted @   Lucius7  阅读(613)  评论(3编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
🔑