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

中国剩余定理及其扩展

时间:2023-08-15 22:31:36浏览次数:37  
标签:剩余 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/7094851

相关文章

  • vscode 导出导入所有扩展
    vscode导出导入所有扩展导出全部扩展在vscode中打开一个终端在终端中进入D盘cdD://在终端中输入code--list-extensions>extensions.txt在D盘中找到extensions.txt文件,发到另一台电脑上导入全部扩展把extensions.txt放到D盘在vscode中打开终端,进入到......
  • 再次扩展兼容性的车牌验证正则
    在普通车牌上兼容了新能源车牌和应急类型的车牌 还有其他一堆乱七八糟的类型。。。现在和车牌真乱下面是正则^(([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-HJ-NP-Z](([0-9]{5}[ADFCGHXB])|([ADCFGHXB]([A-HJ-NP-Z0-9])[0-9]{4})))|([京津沪......
  • 如何在本地给 vue-router4 扩展功能?
    背景前段时间给基于vue3的H5项目做一些优化,该项目经常会有一些页面间的通信,譬如选择地址、乘机人等信息后回填到上一个页面。对于这种简单、频繁的通信,实在不想搞成重火力(eg:pinia)。最好让使用者用完即走,不用操心除业务逻辑之外的任何事情。路由控制页面通信既然是页面间的......
  • 关于 SAP Fiori Elements 应用的 ResponsiveTableColumnsExtension 扩展
    笔者这篇教程介绍了如何在SAPFioriElements应用的manifest.json里注册Extensionfragment,从而给ListReport应用的Table区域新增自定义列:10.如何通过扩展(Extension)的方式给SAPFioriElementsListReport的表格新增列请大家注意下图高亮的扩展:ResponsiveTabl......
  • IIS 添加MIME扩展类型及常用的MIME类型列表
    经常用IIS作为下载服务器的时候有时传上去的文件比如example.mp4文件名上传后,但是用http打开的时候确显示为404文件不存在。其实是IIS对文件的一种保护,不在IIS指定的MIME类型里的文件不会被操作。常见的有mp4/flv/iso/7z/apk等扩展名的文件,iis本身是没有指定......
  • 欧拉定理 & 扩展欧拉定理
    观前提醒:「文章仅供学习和参考,如有问题请在评论区提出」目录前置剩余类(同余类)完全剩余系(完系)简化剩余系(缩系)欧拉函数欧拉定理扩展欧拉定理参考资料前置剩余类(同余类)给定一个正整数\(n\),把所有的整数根据模\(n\)的余数\(r\in[0,n-1]\)分为\(n\)类,每一类就可以......
  • 威尔逊定理
    威尔逊定理:若p为素数,则p可以整除(p-1)!+1。用同余方程表示为:(p-1)! ≡-1(modp)证明如下充分性:当p=1时,(p-1)! ≡0(modp)当p=4时,(p-1)! ≡2(modp)当p>4时,当p为完全平方数时,设k²=p,探讨2k和p的大小,因为k²-2k在k>2的时候永远大于0,所以p>2k>k,所以(p-1)!≡ 1*2*.........
  • LeetCode 周赛上分之旅 #39 结合中心扩展的单调栈贪心问题
    ⭐️本文已收录到AndroidFamily,技术和职场问题,请关注公众号[彭旭锐]和BaguTreePro知识星球提问。学习数据结构与算法的关键在于掌握问题背后的算法思维框架,你的思考越抽象,它能覆盖的问题域就越广,理解难度也更复杂。在这个专栏里,小彭与你分享每场LeetCode周赛的解题报告,一......
  • 如何通过扩展(Extension)的方式给 SAP Fiori Elements List Report 的表格新增列试读
    本教程之前的步骤,我们已经详细学习了SAPFioriElements应用里类型为ListReport的创建步骤,并且介绍了使用安装在VisualStudioCode里的SAPFioriTools扩展提供的向导,生成FioriElements应用的本地项目结构:5.动手开发第一个SAPFioriElements应用6.知其然......
  • Dubbo高手之路2,6种扩展机制详解
    大家好,我是哪吒。上一篇分享了Java面试被问到Dubbo,怎么回答可以得高分?今天详细的分解一下Dubbo的扩展机制,实现快速入门,丰富个人简历,提高面试level,给自己增加一点谈资,秒变面试小达人,BAT不是梦。三分钟你将学会:Dubbo的自适应扩展机制Dubbo的SPI扩展机制Dubbo的自定义扩展点机制Dubbo......