模线性方程组

韩信点兵

问题:给定了n组除数m[i]和余数r[i],通过这n组(m[i],r[i])求解一个x,使得x mod m[i] = r[i]

解:

  一开始就直接求解多个方程组的解,比较困难,所以我们从 n = 2 开始递推

  已知:

x mod m[1] = r[1]
x mod m[2] = r[2]

  所以存在两个数, k[1], k[2]

x = k[1]*m[1] + r[1]
x = k[2]*m[2] + r[2]

  因为值相同,所以:

        k[1]*m[1] + r[1] = k[2]*m[2] + r[2]
=>    k[1]*m[1] - k[2]*m[2] = r[2] - r[1]

  令 A = m[1], B = m[2], C = r[2] - r[1], x = k[1], y = k[2]  则上式变为 Ax + By = C

  则是扩展欧几里德,可以看我之前的一篇博客http://www.cnblogs.com/ygdblogs/p/5476395.html

 

  由于每两个方程可以合并成一个,连续操作,转化为一个方程,即可求解多个方程组的解

  伪代码:

M = m[1], R = r[1]
For i = 2 .. N 
    d = gcd(M, m[i])
    c = r[i] - R
    If (c mod d) Then    // 无解的情况
        Return -1
    End If
    (k1, k2) = extend_gcd(M / d, m[i] / d)    // 扩展欧几里德计算k1,k2
    k1 = (c / d * k1) mod (m[i] / d)    // 扩展解系
    R = R + k1 * M        // 计算x = m[1] * k[1] + r[1]
    M = M / d * m[i]     // 求解lcm(M, m[i])
    R %= M             // 求解合并后的新R,同时让R最小
End For        
If (R < 0) Then 
    R = R + M
End If
Return R

  源码:

#include<stdio.h>
#include<iostream>
typedef long long ll;
ll m[1010], r[1010], x, y;

ll gcd(ll a,ll b){
    if(a%b == 0) return b;
    return gcd(b, a%b);
}

void extend_gcd(ll a,ll b){
    if(b==0){
        x=1;
        y=0;
    }
    else{
        extend_gcd(b,a%b);
        ll t=x;
        x=y;
        y=t-(a/b)*x;
    }
}

int main(){
    int n;
    ll a, b, c, ans, r1;
    scanf("%d",&n);
    for(int i =0; i<n; i++)
        scanf("%lld%lld",&m[i],&r[i]);
    a = m[0], r1 = r[0];
    for(int i=1; i<n; i++){
        b = m[i];
        c = r[i] - r1;
        ll d = gcd(a, b);
        if(c%d){
            ans=-1;
            break;
        }
        else{
            ll a1 = a/d;
            ll b1 = b/d;
            ll c1 = c/d;
            extend_gcd(a1,b1);
            ll x1 = (x*c1)%b1;
            if(x1< 0)
              x1 += b1;
            r1 = a*x1 + r1;
            a = a1*b;
            ans = r1;
        }
    }
    printf("%lld\n",ans);
    return 0;
}
View Code

 

posted @ 2016-05-18 00:21  TensionRidden  阅读(189)  评论(0编辑  收藏  举报