zoukankan      html  css  js  c++  java
  • 2. 最大公约数,最小公倍数,拓展欧几里得

    最大公约数(Greatest Common Divisor)

    引题
    输入两个自然数x、y,求它们的最大公约数。
    对于两个整数x和y,如果x/d个y/余数都为0,则d称为x和y的公约数,其中最大的称为x和y的最大公约数(Greatest Common Divisor)。举个例子,35和14的最大公约数gcd(35,14)为7.因为35的约数为{1,5,7,35},14的约数为{1,2,7,14},二者的公约数{1,7}中最大的是7。

    输入:输入x、y,用1个空格隔开,占1行。
    输出:输出最大公约数,占1行。
    限制1<=x,y<=1091<=x,y<=10^9
    提示:对于整数x、y,如果x>=y,则x与y的最大公约数等于y与x%y的最大公约数。这里x%y表示x除以y之后的余数。
    输入示例:147 105
    输出实例:21

    1. 初学者的思维:

    将x和y中较小的一方用作n,让d从n自减至1,检查其是否能同时整除x和y,如果能返回当时的d。
    伪代码

    gcd(x,y)
    	n = (x与y中较小的一个)
    	for d从n到1
    		if d是x和y的约数
    			return d
    

    这个算法虽然能正确地输出结果,但最坏情况下要进行n次除法,无法在规定时间内处理完较大的数据。

    2. 欧几里得算法(Euclidean algorithm,辗转相除法)

    定理:当x>=y时,gcd(x,y)等于gcd(y,x除以y之后的余数)。
    运用这条定理,可以快速求解x与y最大公约数的算法。

    3. 定理证明

    设d为a和b的公约数,则有:(l,m为常数)
    a=lda=l * d………………………………(1)
    b=mdb=m * d………………………………(2)
    设r=a%b,则有:(k为常数)
    a=kb+ra=k * b+r……………………………(3)
    将(1)(2)代入(3)得:
    ld=kmd+rl * d=k*m*d+r
    上式化简得:
    r=(lkm)dr=(l-k*m)*d……………………(4)
    所以由(4)易得:d为r的约数。
    又由(2)易得:d为b的约数。
    综上两点,易得:d为b和r的公约数。
    即证明:a与b的公约数,与,b和r(r=a%b)的公约数相等。
    因此,a和b的公约数集合等于b和r的公约数集合,二者的最大公约数自然也相等。

    4. 举例and时间复杂度

    74与54的最大公约数:
    gcd(74,54)=gcd(54,74%54)=gcd(54,20)=gcd(20,54%20)=gcd(20,14)=gcd(14,20%14)=gcd(14,6)=gcd(6,14%6)=gcd(6,2)=gcd(2,6%2)=gcd(2,0)=2gcd(74,54)\=gcd(54,74\%54)=gcd(54,20)\=gcd(20,54\%20)=gcd(20,14)\=gcd(14,20\%14)=gcd(14,6)\=gcd(6,14\%6)=gcd(6,2)\=gcd(2,6\%2)=gcd(2,0)\=2
    时间复杂度大致为O(logb)。

    5. 用递归函数求最大公约数
    #include <iostream>
    using namespace std;
    
    int gcd(int x,int y)
    {
    	return y ? gcd(y,x%y) : x;
    }
    
    int main()
    {
    	int a,b;
    	cin >> a >> b;
    	cout << gcd(a,b) << endl;
    }
    
    6. 用循环求最大公约数
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    int gcd(int x,int y)
    {
    	int r;
    	if(x<y) swap(x,y);//保证x>y
    	while(y>0)
    	{
    		r = x%y;
    		x = y;
    		y = r;
    	}
    	return x;
    }
    
    int main()
    {
    	int a,b;
    	cin >> a >> b;
    	cout << gcd(a,b) << endl;
    }
    

    最小公倍数(Least Common Multiple,LCM)

    1. 求最小公倍数的公式

    求给定n个整数的最小公倍数(Least Common Multiple,LCM)。这里可以先用欧几里得算法求出最大公约数,然后用最大公约数求出两个数的最小公倍数。

    公式:
    已知:
    gcd(a, b),是求a和b的最大公约数
    lcm(a, b),是求a和b的最小公倍数

    易得:
    ab=gcd(a,b)lcm(a,b)a*b = gcd(a,b) * lcm(a,b)
    lcm(a,b)=ab/gcd(a,b)lcm(a,b) = a * b / gcd(a,b)
    (注意,这样写法有可能会错,因为a * b可能因为太大 超出int 或者超出 long long)
    所以推荐写成 : lcm(a,b)=a/gcd(a,b)blcm(a,b) = a / gcd(a,b) * b

    2. 公式证明
    1. 求a和b的gcd(a,b)gcd(a,b)
    2. 易得agcd(a,b)frac{a}{gcd(a,b)}bgcd(a,b)frac{b}{gcd(a,b)} 这两个数互质,即gcd(agcd(a,b),bgcd(a,b))=1gcd(frac{a}{gcd(a,b)},frac{b}{gcd(a,b)}) =1
    3. 所以,易知lcm(a,b)=gcd(a,b)(agcd(a,b))(bgcd(a,b))lcm(a,b)=gcd(a,b)*(frac{a}{gcd(a,b)})*(frac{b}{gcd(a,b)})
      化简:lcm(a,b)=abgcd(a,b)lcm(a,b)=frac{a*b}{gcd(a,b)}
      ab=gcd(a,b)lcm(a,b)a*b=gcd(a,b)*lcm(a,b)
    3. 公式拓展

    gcd(ka,kb)=kgcd(a,b)gcd(ka, kb) = k * gcd(a, b)

    lcm(ka,kb)=klcm(a,b)lcm(ka, kb) = k * lcm(a, b)

    lcm(Sa,Sb)=Sgcd(a,b)lcm(frac{S}{a}, frac{S}{b}) = frac{S}{gcd(a, b)}

    拓展欧几里得(Extended Euclid Algorithm)

    任务:
    对于给定的两个整数aba、b,求ax+by=gcd(a,b)ax+by=gcd(a,b)的解(x,y)(x,y)

    说明:

    1. 当b=0时,有x=1,y=0时成立。

    2. 当b>0时,在欧几里得算法的基础上,已知:gcd(a,b)=gcd(b,a%b)gcd(a,b)=gcd(b,a\%b)
      先递归求出x’,y’满足:gcd(a,b)=gcd(b,a%b)=bx+(a%b)ygcd(a,b)=gcd(b,a\%b)=bx&#x27;+(a\%b)y&#x27;

    3. 然后可以回推,我们将上式化简得:
      gcd(a,b)=bx+(a%b)y=bx+(aa/bb)ygcd(a,b)=bx&#x27;+(a\%b)y&#x27;=bx&#x27;+(a-lfloor{a/b} floor*b)y&#x27;
      gcd(a,b)=bx+aya/bbygcd(a,b)=bx&#x27;+ay&#x27;-lfloor{a/b} floor*by&#x27;
      其中,a/blfloor{a/b} floor除法指整除。

    4. 把含b的因式提取一个b,可得:
      gcd(a,b)=ay+b(xa/by)gcd(a,b)=ay&#x27;+b(x&#x27;-lfloor{a/b} floor*y&#x27;)
      又已知:gcd(a,b)=ax+bygcd(a,b)=ax+by
      x=yx=y&#x27;y=xa/byy=x&#x27;-lfloor{a/b} floor*y&#x27;

    5. 上述思想是递归定义的,不断地利用gcd(a,b) =gcd(b,a%b),到b=0(y的系数为0)时,有x=1、y=0时成立。

    6. 再根据公式gcd(b,a%b)=gcd(a,b)gcd(b,a\%b)=gcd(a,b)不断回推,并且每次回推因此,其对应的x,y值都要按x=yx=y&#x27;y=xa/byy=x&#x27;-lfloor{a/b} floor*y&#x27;的关系做改变。根据解之间的关系,最终可以得到方程ax+by=gcd(a,b)ax+by =gcd(a, b)的解。

    接口:
    int extend_gcd(int a,int b,int &x,int &y);
    复杂度:O(logN),其中N和a,b同阶
    输入:a,b两个整数
    输出:a和b的最大公约数和ax+by=gcd(a,b)的一组解(x,y)

    核心代码:(好理解,但代码麻烦版)

    int extend_gcd( int a, int b, int &x, int &y )//函数返回a,b的最大公约数
    {
    	if( b==0 )
    	{
    		x = 1;
    		y = 0;
    		return a;
    	}
    	else
    	{
    		int r = extend_gcd(b,a%b,x,y);
    		int temp_x=x, temp_y=y;//这里的x,y已经是被上一行代码修改过的x,y(因为传参是传的x,y的引用)
    							   //temp_x,temp_y分别对应“说明”的x',y'
    		x = temp_y;//对应“说明”荧光部分的 x=y'
    		y = temp_x-(a/b)*temp_y;//对应“说明”荧光部分的 y=x'-a/b*y'
    		return r;
    	}
    }
    

    核心代码:(优化版)

    int extend_gcd( int a, int b, int &x, int &y )//函数返回a,b的最大公约数
    {
    	if( b==0 )
    	{
    		x = 1;
    		y = 0;
    		return a;
    	}
    	else
    	{
    		int r = extend_gcd(b,a%b,y,x);
    		y -= x*(a/b);
    		return r;
    	}
    }
    
  • 相关阅读:
    Netty解决粘包和拆包问题的四种方案
    springboot 1.4 CXF配置
    聊聊springboot2的embeded container的配置改动
    MFC函数——CWnd::OnCreate
    《开发专家 Visual C 开发入行真功夫》笔记
    MFC中UpdateData()函数的使用
    Visual Studio 2008 添加MScomm控件的方法
    Visual Studio 2008 调试运行Bug记录
    《VS2010/MFC编程入门教程》——读书笔记
    《C++程序设计教程——给予Visual Studio 2008》读书笔记3章
  • 原文地址:https://www.cnblogs.com/yuzilan/p/10626075.html
Copyright © 2011-2022 走看看