首页 > 其他分享 >中国剩余定理及其扩展

中国剩余定理及其扩展

时间:2023-08-04 23:36:21浏览次数:56  
标签:剩余 int res 定理 扩展 k1 m1 m2 LL

$$ \left { \begin{aligned} x_1 = a_1 \ (mod \ m_1) \ x_2 = a_2 \ (mod \ m_2) \ . \qquad \qquad \ . \qquad \qquad \ . \qquad \qquad\ x_n = a_k \ (mod \ m_k) \end{aligned} \right . $$

中国剩余定理

算法流程

  1. 计算所有模数的积 M;
  2. 对于第 i 个方程: a. 计算 $n_i \ = \ \frac{M}{m_i}$; b. 计算 $n_i$ 在模 $m_i$ 意义下的逆元 $n_i^{-1}$;
  3. 方程组的唯一解为: $x = \sum_{i = 1}^k (n_i * n_i^{-1} * a_i) \ (mod \ M)$。

代码实现

#include <iostream>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 15;
int A[N], B[N];

LL exgcd(LL a, LL b, LL &x, LL &y)
{
    if (!b)
    {
        x = 1;
        y = 0;
        return a;
    }
    LL gcd = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return gcd;
}
int main()
{
    int n;
    cin >> n;
    
    // 第一步: 计算所有模数的积M
    LL M = 1;
    for (int i = 0; i < n; ++ i)
    {
        cin >> B[i] >> A[i];
        M *= B[i];
    }

    LL res = 0;

    for (int i = 0; i < n; ++ i)
    {
        
        // LL ni = M / B[i]; 
        // LL ti = qpow(ni, B[i] - 2, B[i]); // B[i] - 2为负数qpow无法处理 题目中只说到任意两个B[i]互质,但没说一定是质数,所以不满足费马小定理的条件
        LL ni = M / B[i], ti, x; // 第二步a: 计算ni
        exgcd(ni, B[i], ti, x); // 第二步b: 计算ni的逆元ti
        
        res = (res + ni * ti % M * A[i] % M) % M; //第三步: 计算答案
    }

    cout << (res % M + M) % M << endl; // 保证M是大于0的最小值
    
    return 0;
}

扩展中国剩余定理

当满足中国剩余定理条件时也可以使用下面的计算方法计算,因为下面的方法没有什么限定条件 中国剩余定理要求 $m_1, \ m_2, \ ... \ m_n$ 必须是两两互质的,如果不满足这个条件时,就需要使用其它方法来做了 我记得书上有种做法,但是记不清怎么搞的了,待定。

推导过程

问题和对应的推导过程 网上找的推导过程 手写推导过程 简单地说就是对于多个式子,我们每次只看两个式子,通过数学推导将其等价变形为一个式子,逐渐将所有式子合并最终得到x的解

需要注意的问题

图1的4式是减法,但是扩展欧几里得计算的是加法( $k_1 \ a_1 + k_2 \ a_2 = m_2 - m_1$ ),出现的问题只不过是其实我们实际计算的其实是k2',而k2' = -k2,但是这并不会对我们的后续操作产生什么影响,因为我们用的是k1,后面并没有用到k2,而无论是加法还是减法,k1求解出来的值都是一样的。

代码实现

/**
 * 给定 2n 个整数
 * a1,a2,…,an 和 m1,m2,…,mn,
 * 求一个最小的非负整数 x,满足∀i∈[1,n],x≡ai(mod mi)
 * 其实代码只需要按照我们的推导过程直译即可
 */

#include <iostream>

using namespace std;

typedef long long LL;
LL exgcd(LL a, LL b, LL &x, LL &y)
{
    if (!b)
    {
        x = 1, y = 0;
        return a;
    }
    LL d = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}

int main()
{
    int n;
    cin >> n;
    
    LL m1, a1; // 因为一次合并后m1存储的就是m1和m2的最小公倍数了,是有可能会爆int的
    cin >> m1 >> a1;
    
    LL res;
    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;
            break;
        }
        
        k1 *= (a2 - a1) / d; // 正解中对k1还有其它处理
        /**
         * 作用是保证k1为最小非负值
         * 可是为什么需要保持k1为非负值?
         * 这里我忽略了取余的另一个作用就是缩小数据
         * 首先我们必须明确的一点是根据推导过程可知,所有的 k = k1 (mod (m2 / d)),这样的k都是k1的一个解,只需要k2对应变化即可
         * 在下面的步骤中,我们计算res和a1都需要用到k1,本题数据比较极限,如果k1过大,会导致下面的计算结果溢出,所以这里需要取k1的最小非负整数解
         */
        k1 = (k1 % (m2/d) + m2/d) % (m2/d);
        
        res = k1 * m1 + a1;
        
        a1 = m1 * k1 + a1;
        m1 = m1 / d * m2;
    }
    
    /**
     * 最终合并结果就剩下了一个式子 x = a1 (mod m1)
     * 题目要求求解最小的非负整数解
     * 所以可以按照下面的方法做,需要注意res为-1的无解情况
     * 那么为什么正解不可能是-1???????????????
     */
     /**
      * 需要特判-1的样例
      * 5
      * 35 30
      * 22 2
      * 16 5
      * 28 23
      * 32 18
      * 
      * 实际输出:769
      * 正确输出:-1
      */
    if (res != -1) res = (res % m1 + m1) % m1;
    
    cout << res << endl;
}

标签:剩余,int,res,定理,扩展,k1,m1,m2,LL
From: https://blog.51cto.com/u_14882565/6969998

相关文章

  • Lucas定理
    Lucas定理:主要是求$C_{n}^{m}$在模$p$情况下($mod\,p$)(一般$p$较小,而$n,m$较大的情况)公式:$C_{n}^{m}≡ C_{n\,mod\,p}^{m\,mod\,p}\timesC_{n/p}^{m/p} (mod\,p)$证明以后补吧就以这题来说明具体解法:题目LuoguP3807【模板】卢卡斯定理/Lucas定......
  • 扩展Django:实现自己的manage命令
    我们都用过Django的django-admin.py和manage.py。django-admin.py是一个命令行工具,可以执行一些管理任务,比如创建Django项目。而manage.py是在创建每个Djangoproject时自动添加在项目目录下的,只是对manage.py的一个简单包装,其功能是将Djangoproject放到sys.path目录中,同时设置DJ......
  • 如何导出 Visual Studio Code 的扩展应用,并离线安装?
    如何导出VisualStudioCode的扩展应用,并离线安装?warrior210已于2022-08-0810:37:51修改2262收藏5文章标签:vscodeide编辑器版权1.离线情形VisualStudioCode的扩展应用安装位置在文件夹.vscode/extensions下。不同平台,它位于:Windows%USERPROFILE%\.vscode\exte......
  • Spring 容器里 Bean 生命周期中可扩展的 SpringBoot 接口
    Gitee:Demo源码1.ApplicationContextInitializer这是整个spring容器在刷新之前初始化ConfigurableApplicationContext的回调接口。@Slf4jpublicclassTestApplicationContextInitializerimplementsApplicationContextInitializer{@Overridepublicvoidi......
  • 爷情节!终于可以离线安装浏览器扩展了~
    现在基本只用Chrome浏览器,身边很多同事也一样。上次装了一个视频录制扩展,可能是因为扩展文件太大,同步扩展花了好长时间。有时不想把账号登得导出都是,又想使用扩展。所以能下载到.crx文件就很重要。昨天听说bejson上线了crx下载地址解析功能,于是迫不及待地去体验了一下,一个字“牛鼻......
  • 【设计模式】装饰器模式Decorator:在基础组件上扩展新功能
    (目录)装饰器模式看上去和适配器模式、桥接模式很相似,都是使用组合方式来扩展原有类的,但其实本质上却相差甚远呢。简单来说,适配器模式侧重于转换,而装饰模式侧重于动态扩展;桥接模式侧重于横向宽度的扩展,而装饰模式侧重于纵向深度的扩展。原理装饰模式的原始定义是:允许动态地向......
  • WebRTC研究:RTP报头扩展
    RTPHeaderRTP协议中,RTPHeader(报头)包括固定报头(FixedHeader)与报头扩展(Headerextension,可选)。RTPFixedHeader结构如下,其中前12字节是每个RTP包必须包含的。01230123456789012345678......
  • flask闪现,请求扩展,g对象
    1闪现#一个请求---》假设出错了---》重定向到另一个地址---》把错误信息在另一个返回中看到错误信息放个位置----》另一个请求过来,去那个位置拿#把一些数据,放在某个位置---》后期可以去取出来----》取完不用删除,就没了defindex():s='xx错位了'returnredirect(......
  • 兰道定理
    定义竞赛图的比分序列是将竞赛图每个点的出度从小到大排列得到的序列。所谓兰道定理,即一个长度为\(n\)的序列\(\{s_i\},s_i\les_{i+1}\)是合法的比分序列当且仅当\(\forallk,\sum_{i=1}^ks_k\geC(k,2)\)进一步的一个竞赛图强连通的充要条件是:把它的所有顶点按照入度d从小到大......
  • 【js学习笔记二十二】...扩展运算符
     目录前言导语 代码部分 运行结果前言我是歌谣我有个兄弟巅峰的时候排名c站总榜19叫前端小歌谣曾经我花了三年的时间创作了他现在我要用五年的时间超越他今天又是接近兄弟的一天人生难免坎坷大不了从头再来歌谣的意志是永恒的放弃很容易但是坚持一定很酷导语歌谣歌谣......