中国剩余定理及扩展

中国剩余定理

定义: 中国剩余定理其实就是多个线性同于方程的方程组;举个例子,《孙子算经》中曾提到过这样一个问题: 有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二,问物几何。 其实这就是一个经典的中国剩余定理的例子。

算法流程:

计算所有模的积n -----> 对于第 i 个方程:计算mi = n/ni -------> 计算mi在模ni的意义下的逆元1/mi -------> 计算ci = mi* (mi^-1)(不要对ni取模)得:

\[方程为一解为: ans = \sum_{i=1}^{k}a_ic_i (mod\ n)
\]

代码实现:

//式中m[i]为除数必须两两互质才能这么算,否则请看excrt算法
//式中b[i]是除数, a[i]是余数
void exgcd(int a,int b,int &x,int &y)
{
    if(b==0){ x=1; y=0; return;}
    exgcd(b,a%b,x,y);
    int tp=x;
    x=y; y=tp-a/b*y;
}

int china()
{
    int ans=0,lcm=1,x,y;
    for(int i=1;i<=k;++i) lcm*=b[i];     //计算n
    for(int i=1;i<=k;++i)
    {
        int tp=lcm/b[i];                    //求m[i]
        exgcd(tp,b[i],x,y);                 
        x=(x%b[i]+b[i])%b[i];               //x要为最小非负整数解
        ans=(ans+tp*x*a[i])%lcm;            //ai*ci ci=tp*x
    }
    return (ans+lcm)%lcm;
}

扩展中国剩余定理

它与中国剩余定理不同之处在于:中国剩余定理中所有的模一定是两两互素的,二在excrt中:所有的模不一定两两互素。

思路为求解k次exgcd即可:

//a[i]和b[i]分别是余数和除数

LL mul(LL a, LL b, LL mod){
    LL ans = 0 % mod;
    while(b){
        if(b & 1)   ans = (ans + a) % mod;
        a = (a + a) % mod;
        b >>= 1;
    }
    return ans % mod;
}
LL exgcd(LL a, LL b, LL &x ,LL &y)
{
    if(b == 0)  {x = 1; y = 0; return a;}
    LL gcd = exgcd(b, a % b, x, y);
    LL tp = x; x = y; y = tp - a / b * y;
    return gcd;
}
LL excrt()
{
    LL m = b[1], ans = a[1];            //第1个的答案,然后往下递推
    for(int i = 2; i <= n; i++)
    {
        LL c = (a[i] - ans % b[i] + b[i]) % b[i];
        LL gcd = exgcd(m, b[i], x, y);
        if(c % gcd != 0)    return -1;      //无解
        x = mul(x, c/gcd, b[i]);
        ans += x * m;
        m *= b[i] / gcd;
        ans = (ans % m + m) % m;
    }
    return (ans % m + m) % m;
}

洛谷excrt模板题:

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
const int maxn = 1e5 + 10;
const int inf = 0x3f3f3f3f;
int gcd(int a,int b)    { return b == 0 ? a : gcd(b, a % b); }

LL n, a[maxn], b[maxn];
LL x, y;
LL mul(LL a, LL b, LL mod){
    LL ans = 0 % mod;
    while(b){
        if(b & 1)   ans = (ans + a) % mod;
        a = (a + a) % mod;
        b >>= 1;
    }
    return ans % mod;
}
LL exgcd(LL a, LL b, LL &x ,LL &y)
{
    if(b == 0)  {x = 1; y = 0; return a;}
    LL gcd = exgcd(b, a % b, x, y);
    LL tp = x; x = y; y = tp - a / b * y;
    return gcd;
}
LL excrt()
{
    LL m = b[1], ans = a[1];            //第1个的答案,然后往下递推
    for(int i = 2; i <= n; i++)
    {
        LL c = (a[i] - ans % b[i] + b[i]) % b[i];
        LL gcd = exgcd(m, b[i], x, y);
        if(c % gcd != 0)    return -1;      //无解
        x = mul(x, c/gcd, b[i]);
        ans += x * m;
        m *= b[i] / gcd;
        ans = (ans % m + m) % m;
    }
    return (ans % m + m) % m;
}

int main()
{
    scanf("%lld", &n);
    for(int i = 1; i <= n; ++i){
        scanf("%lld %lld", &b[i], &a[i]);       //除数和余数
    }
    printf("%lld\n", excrt());
    system("pause");
}

原文链接: https://www.cnblogs.com/StungYep/p/12252598.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    中国剩余定理及扩展

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/326748

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年3月1日 下午3:57
下一篇 2023年3月1日 下午3:57

相关推荐