• 初等数论与算法(一):整除、最大公约数、最小公倍数、辗转相除法、裴蜀定理


    镇楼图

    Pixiv:torino

    注:有关程序部分均用C with STL描述;部分有数学证明,我个人认为过于简单或不常用的结论我不会进行证明,可以at我,我可以提供证明



    一、整除、gcd与lcm

    整除性

    关于整数的加法、减法、乘法其运算都会保证结果都还在整数范畴呢,但唯独除法会产生其他结果

    整除性是指b能整除a,即\(∃q∈Z,a=bq\),记作\(b~|~a\)\(b≠0\)

    其中\(b\)称为\(a\)的因数,\(a\)\(b\)的倍数

    \(\not ∃q∈Z\),即\(b\)不整除\(a\),记作\(b~|\not~~a\)

    整除性的性质

    ■若\(c~| ~b,b~| ~a\),则\(c~|~a\)(传递性)

    ■若\(b~|~a\),则\(∃k∈Z^*,kb~|~ka\)

    ■若\(kb~|~ka\),则\(b~|~a\)

    ■若\(c~|~a,c~|~b\),则\(m,n∈Z,c~|~ma+nb\)

    ■若\(b~|~a,a≠0\),则\(|b|≤|a|\)

    ■若\(b~|~a,a≠0\),则\(\frac{a}{b}~|~a\)

    ■若\(b~|~0\),则\(∀q∈Z\)

    余数

    余数定义:若\(∀a,b∈Z,b>0\),则\(∃!q,r∈Z,a=bq+r\)\(r∈[0,b)\)

    其中\(r=<a>_b\)称为\(a\)整除\(b\)的非负最小剩余(余数)即\(r=a\%b\),当除数一致时可省略

    \(<a_1+a_2>=<<a_1>+<a_2>>\)

    \(<a_1a_2>=<<a_1><a_2>>\)

    最大公因数

    公因数定义:假设有一组正整数\(a_1,a_2,...,a_n\),若\(d~|~a_1,d~|~a_2,...,d~|~a_n\)则称\(d\)\(a_1,a_2,...,,a_n\)的公因数

    最大公因数定义:假设有一组正整数\(a_1,a_2,...,a_n\),若\(d_1,d_2,...,d_n\)是这一组数的公因数,则\((a_1,a_2,...,a_n)=max{d_1,d_2,...,d_n}\)称为最大公因数,简记为\(m\)

    \(m~|~a_i\)

    \(1≤m≤min(a_i)\)

    \(d_i~|~m\)(最大公约数是其他公约数的约数)

    \((a,b)≥n\),当且仅当\(\begin{cases}n~|~a \\ n~|~b \end{cases}\)(证明要用到的重要结论!!!)

    这个性质不难理解,由公因数的定义可得,因为n是公因数,那么肯定是要小于等于最大公因数的。但这里存在一个转换思路的过程。也就是如果要证明有关不等式的问题,可以将这个问题转成整除问题

    ■若\(m~|~a,m~|~b\),则\(m~|~(a,b)\)

    \((a,b)~|~a,(a,b)~|~b\)

    ■若\(a=bq+r\)即满足余数定义且\(a,b∈N_+\),则\((a,b)~=~(b,r)=r_n\)(欧几里得算法)

    证明:

    ​ 要证明\((a,b)~=~(b,r)\)即证明\(\begin{cases} (a,b)≤(b,r) \\ (b,r)≤(a,b) \end{cases}\)

    ​ 这里只说明其中一条其余可自证

    ​ 由整除性第二条性质得要证明\((a,b)≤(b,r)\)即证明\(\begin{cases}(a,b)≤b \\ (a,b)≤r \end{cases}\)

    \(∵(a,b)~|~b\)

    \(∴(a,b)≤b\)

    \(∵r=a-bq\)

    \(∴(a,b)~|~(a-bq)\)(整除性第四条性质,已略部分步骤)

    \(∴(a,b)≤(b,r)\)

    ​ 同理可得\((b,r)≤(a,b)\)

    \(∴(a,b)=(b,r)\)

    比如要求解\((a,b)\)可以‘先求解\((b,r)\)直到最后\(r\)变小至0即可,在后面详细描述了欧几里得算法的实现即到最后会得到一个\((x,0)\)的一组数,那么\(x=(a,b)\)

    ■若\(a,b∈N_+\),则\((a,b)=ma+nb\)

    证明:

    ​ 本质上是欧几里得算法的逆过程

    \(a = bq_1+r_1\)

    \(b = r_1q_2 + r_2\)

    \(...\)

    ​ 可以用一个四元组表示这四个数

    \(<a,b,r_1,q_1>→<b,r_1,r_2,q_2>→...→<r_{n-1},r_n,r_{n+1},q_{n+1}>\)

    \(r_{n+1} = 0\)

    ​ 其中定义\(a,b,r_i\)前的系数统一定为\(C\)

    (注意引入的\(C\)是一个表示任意的常数,C和C不一定相等)

    \(r_{n-1}=Cr_n\)

    \(r_{n-2}=Cr_n+r_n\)

    \(r_{n-3}=Cr_n+r_{n-1}\)

    \(r_{n-4}=Cr_{n}+Cr_{n-1}+r_{n-2}=Cr_n\)

    ​ 简单来说就是不管是第几项都可以通过不断回代得到\(Cr_n\)至于\(C\)为多少需要计算

    ​ 那么在第一项就可以这么表达

    \(a=Cb+r_1=Cb+Cr_n\)\(r_n=Ca+Cb=ma+nb\)

    ​ 由此得证

    ​ 具体算法求解m、n贴在了后面

    ■绝对最小剩余:若满足余数定义\(a=bq+r\)\(|r|≤\frac{b}{2}\),此时\(|r|\)称为绝对最小剩余

    在欧几里得算法上运用绝对最小剩余在某些情况下可以优化次数

    但首先要将一个非负最小剩余处理为绝对最小剩余

    \(\begin{cases}r,r ≤ b/2,a=qb+r \\ r-b,b/2<r<b,r=(q+1)b+r-b \end{cases}\)

    简单来说就是如果发现不满足绝对最小剩余,可以q+1后r再减去b从而得到一个满足绝对最小剩余的余数(这里不作证明)

    具体算法参考后面,只是多了一个判断就能优化

    欧几里得算法(辗转相除法)

    问题描述:已知性质\((a,b)=(b,r)\),如何求解正整数\(a\)\(b\)的最大公因数?

    数学解决

    \((a,b)=(b,r_1)=(r_1,r_2)=...=(r_{n-1},r_{n})=(r_n,0)=r_n\)

    \(a=bq_1+r_1\)

    \(b=r_1q_2+r_2\)

    ...

    \(r_{n-1}=r_nq_{n+1}+r_{n+1}\)

    \(∵r_{n+1}=0\)

    \(∴r_{n}=(r_n,0)=(r_{n-1},r_n)=...=(a,b)\)

    程序解决

    //假设a>b,即a为被除数,b为除数
    int gcd(int a,int b){
        return (b) ? gcd(b,a%b) : a ;
    }
    

    绝对最小剩余优化欧几里得算法

    绝对最小剩余在某些情况下可以优化近一半的次数

    比如gcd(288,158)

    简单来说就是发现不满足绝对最小剩余的条件

    ■q变为q+1

    ■r变为r-b

    (但是传参时为正数所以具体实现上r要变成b-r)

    int gcd(int a, int b) {
        int r = a % b;
        if (r > b / 2)r = (b-r);
        return (r) ? gcd(b, r) : b;
    }
    

    推广:求解多个数的最大公约数

    上面已经知晓了欧几里得算法求解两个数的最大公约数,如果要求解多个,其实也相当简单

    (1)定理说明

    假设有n个数,\(a_1,a_2,...a_n\)

    \((a_1,a_2)=d_2\)

    \((d_2,a_3)=d_3\)

    \(...\)

    $(d_{n-1},a_n)=d_n $

    \(∴(a_1,a_2,...,a_n)=(d_{n-1},a_n)=d_n\)

    数学证明略, 比较简单,可以用数学归纳法证明

    同理可以推广得到裴蜀定理

    \((a_1,a_2,...,a_n)=∑x_ia_i\)

    (3)程序求解

    //已知int gcd(int a,int b);
    //备注:保证n>=2且传入参数均正整数,不作异常判断
    int gcd(vector<int> n) {
        int len = n.size();
        int d = gcd(n[0], n[1]);
        for (int i = 2; i < len; i++) {
            d = gcd(d, n[i]);
        }
        return d;
    }
    

    裴蜀定理引入(又称扩展欧几里得算法)

    先来个具体案例,假设要求a=288,b=158的公因数,先尝试用欧几里得算法得到具体过程

    /*
    288=1*158+130
    158=1*130+28
    130=4*28+18
    28=1*18+10
    18=1*10+8
    10=8*1+2
    8=2*4
    此时(a,b)=(r(n),0)=2
    */
    

    裴蜀定理相当于回代的过程

    简单来说就是除数当作上一情况的余数做回代
    如2是最后情况的除数,确实是倒数第二步的余数,同理可得其他情况
    因此抽象成递推式得到

    当前情况:\(r_{i-1}=r_iq_{i+1}+r_{i+1}\)

    上一情况:\(r_{i-2}=r_{i-1}q_{i}+r_i\)

    当前情况的除数是\(r_i\)

    由上一情况转换得

    \(r_{i}=r_{i-2}-q_{i}r_{i-1}\)

    根据欧几里得算法我们是知道传入的前两个数的

    即已知当前情况的\(r_{i-1},r_{i}\)和上一情况的\(r_{i-2},r_{i-1}\)

    所以也就剩下\(q_i\)未知,因此可以求得

    \(q_{i}=r_{i-2}/r_{i-1}\)(整除)

    因此只要知道最基本的情况,不断求解\(q_i\)即可回到第一步

    但现在还少点东西,之后算法的实现还会用到上述证明过程,先看转换的具体的过程

    /*
    2=10-8*1			(1)2是最初的除数
    
     =10-(18-10*1)*1		(2)转8
     =2*10-18
     
     =2*(28-18)-18			(3)转10
     =2*28-3*18
     
     =2*28-3*(130-4*28)		(4)转18
     =14*28-3*130
     
     =14*158-17*130			(5)转28
     
     =31*158-17*288			(6)转130
    
    ∴(a,b)=31b-17a
    */
    

    反推来看,设系数\(x,y\)满足\(ax+by=(a,b)\)

    扩展欧几里得算法即求解最大公约数的同时带出系数

    裴蜀定理证明与算法实现

    现在考虑一般的递推的具体情况

    基本情况:

    \((r_n,0)\)时,等式为\((a,b)=x_{n}r_{n}+y_n*0\)

    此时\(x=1,y=0\)(理论上y可以为任何值,但为了方便,约定为0)

    而回代的过程在于将当前情况的除数回代成为上一情况的余数

    根据之前推导的\(r_{i}=r_{i-2}-q_{i}r_{i-1}\)\(q_{i}=r_{i-2}/r_{i-1}\)(整除)

    \((a,b)=x_n(r_{n-2}-r_{n-1}q_n)\)

    接下来只要一步一步推导即可,从\(r_n\)一直反推到\(b\)

    递推情况:

    已知在\((r_{i-1},r_{i})\)\((a,b)=x_{i-1}r_{i-2}+y_{i-1}r_{i-1}\)

    根据推导对\(r_{i-1}\)展开

    \((r_{i-2},r_{i-1})\)\((a,b)=x_{i-1}r_{i-2}+y_{i-1}(r_{i-3}-⌊\frac{r_{i-3}}{r_{i-2}}⌋r_{i-2})=y_{i-1}r_{i-3}+(x_{i-1}-⌊\frac{r_{i-3}}{r_{i-2}}⌋)r_{i-2}\)

    再简化成系数的变化(重要结论!!!)

    \(x_{i-2}=y_{i-1} \\ y_{i-2}=x_{i-1}-⌊\frac{r_{i-3}}{r_{i-2}}⌋y_{i-1}\)

    得到了这个结论后就可以来实现算法了

    int exgcd(int a, int b, int &x, int &y) {
        //递归,当b=0时即裴蜀定理的初始情况
        //大体功能不变,依然返回(a,b)
        if (!b) {
            x = 1;
            y = 0;//y值说过可以随意改
            return a;
        }
        int gcd = exgcd(b, a%b, x, y);
        int t = y;
        y = x - a / b * y;
        x = t;
        //简单来说就是求出(a,b)以及更新系数
        //更新前后的顺序自己需要捋顺
        //这属于程序问题而不是数学问题
        return gcd;
        //细节备注:算法是要先知道初始的x,y才能继续更新
        //因此先定义gcd达到初始情况定义x,y
    }
    

    最小公倍数

    公倍数与公因数是相互对偶的概念,之间存在联系

    公倍数定义:假设有一组正整数\(a_1,a_2,...,a_n\),若\(a_1~|~m,a_2~|~m,...,a_n~|~m\)则称\(m\)\(a_1,a_2,...,,a_n\)的公倍数

    最小公倍数定义:假设有一组正整数\(a_1,a_2,...,a_n\),若\(m_1,m_2,...,m_n\)是这一组数的公倍数,则\([a_1,a_2,...,a_n]=min{m_1,m_2,...,m_n}\)称为最小公倍数,简记为\(n\)

    定理

    \(a_i~|~n\)

    \(n≥max(a_i)≥1\)

    \(n~|~m_i\)(其他公倍数是最小公倍数的倍数)

    \([a,b]≤n\),当且仅当\(\begin{cases}a~|~n \\ b~|~n \end{cases}\)(证明要用到的重要结论!!!)

    ■若\(a~|~m,b~|~m\),则\([a,b]~|~m\)

    \([a,b](a,b)=ab\)(重要结论!!!)

    (2)数学证明

    ​ 首先将形式转变:\((a,b)=\frac{ab}{[a,b]}\)

    ​ 要证明这个即证明两方面\(\begin{cases}(a,b)≤\frac{ab}{[a,b]} \\ (a,b)≥\frac{ab}{[a,b]} \end{cases}\)

    ​ (之前证明过类似的)

    ​ 这里只证明其中一点,要证明\((a,b)≤\frac{ab}{[a,b]}\)即证明\(\begin{cases}\frac{ab}{[a,b]} ~|~a \\ \frac{ab}{[a,b]}~|~ b\end{cases}\)

    ​ 同样只证明一个

    ​ 由于这个式子非常特殊你可以轻易地写出

    \(a=(\frac{ab}{[a,b]})(\frac{[a,b]}{b})\)

    ​ 而且对于这个式子\(\frac{ab}{[a,b]}\)是整数(根据公倍数性质得到)

    \(\frac{[a,b]}{b}\)是整数(根据公倍数性质得到)

    \(∴{[a,b]} ~|~a\)

    ​ 同理可得...

    \(∴\frac{ab}{[a,b]}~|~a\)

    ​ 对于\((a,b)≥\frac{ab}{[a,b]}\)

    ​ 最大公因数的性质无法将不等式转换成一组整除

    ​ 但替换后\([a,b]≥\frac{ab}{(a,b)}\)最小公倍数可以将不等式转换一组整除\(\begin{cases}a~|~\frac{ab}{(a,b)} \\ b~|~\frac{ab}{(a,b)} \end{cases}\)

    ​ 同理可得...

    ​ 因此证得公式

    ​ 可以发现证明等于的问题基本就是等于转不等式,不等式再根据最大公因数或最小公倍数的特点转成整除问题即可。整除问题只要能找到整数和整的除数相乘得到整的被除数即可。因此转换成了最小的问题是不是整数的问题,这个不难证明

    (3)程序求解

    /*已知之前所写的函数*/
    int lcm(int a,int b){
        return a/gcd(a,b)*b;
    }
    

    推广:求解多个数的最小公倍数

    与之前类似

    //备注:保证n>=2且传入参数均正整数,不作异常判断
    int lcm(vector<int> n) {
        int len = n.size();
        int m = lcm(n[0], n[1]);
        for (int i = 2; i < len; i++) {
            m = lcm(d, n[i]);
        }
        return m;
    }
    

    推广:裴蜀定理

    同理最大公约数可以推广,自然其扩展的算法当然也可以,但计算时更加复杂,这里给出我个人的解答,不代表没有其他解法

    假设有一堆数\(n[0],n[1],...,n[N]\)

    推广的裴蜀定理就是\(∑_ix[i]n[i]=(n[0],n[1],...,n[N])\)

    如何求解各个系数呢?

    和推广的欧几里得算法一致,先算前两项,然后\(d\)作为参数计算后面的项

    \(d=(n[0],n[1])\)

    算出第一项并带出\(x[0],x[1]\)

    即式子是\(d=x[0]n[0]+x[1]n[1]\)

    然后将\(d\)看成一整项,求解\(d'=(d,n[2])\)

    最后也会求出系数,d的系数定为combine

    \(d'=combine*d+x[2]n[2]=combine*x[0]n[0]+combine*x[1]n[1]+x[2]n[2]\)

    那么combine会作用到之前所求的所有数之上

    ...

    //备注:保证n>=2且传入参数均正整数,不作异常判断
    int exgcd(vector<int> n, vector<int> &x) {
        //x[i]存储n[i]的系数
        //需要保证n.size()=x.size()
        int len = n.size();
        
        int d = exgcd(n[0], n[1], x[0], x[1]);
        int combine;
        //combine用于指定联合的系数最后x[j]要乘上的
        for (int i = 2; i < len; i++) {
            d = exgcd(d, n[i], combine, x[i]);
            for (int j = 0; j < i; j++) {
                x[j] *= combine;
            }
        }
        return d;
    }
    

    本章结束代码

    在每章结束后我会附上完整的代码你是可以直接运行的

    而代码会是本章讲到的一个内容的完整版

    //推广的裴蜀定理
    #include<iostream>
    #include<cmath>
    #include<vector>
    using namespace std;
    
    int exgcd(int a, int b, int &x, int &y) {
        if (!b) {
            x = 1;
            y = 0;
            return a;
        }
        int gcd = exgcd(b, a%b, x, y);
        int t = y;
        y = x - a / b * y;
        x = t;
        return gcd;
    }
    
    //备注:不作异常判断
    int exgcd(vector<int> n, vector<int> &x) {
        int len = n.size();
        
        int d = exgcd(n[0], n[1], x[0], x[1]);
        int combine;
        for (int i = 2; i < len; i++) {
            d = exgcd(d, n[i], combine, x[i]);
            for (int j = 0; j < i; j++) {
                x[j] *= combine;
            }
        }
        return d;
    }
    
    int main() {
        vector<int> n;
        n.push_back(288);
        n.push_back(158);
        n.push_back(77);
        vector<int> x(3, 0);
        cout << exgcd(n,x) << endl;
        cout << x[0] << "\t" << x[1] << "\t" << x[2] << endl;
        return 0;
    }
    
  • 相关阅读:
    SSM应用(五)--参数绑定
    SSM应用(四)--SpringMVC入门
    Oracle常用函数
    SSM应用(三)--Spring整合MyBatis
    SSM应用(二)--注解的使用
    SSM应用(一)--Spring入门
    MyBatis学习(十四)--逆向工程使用
    MyBatis学习(十三)--缓存
    MyBatis学习(十二)--懒加载
    MySQL常用函数
  • 原文地址:https://www.cnblogs.com/AlienfronNova/p/16093849.html
Copyright © 2020-2023  润新知