现在的位置: 首页 > 综合 > 正文

[数论]数论的基础知识——最大公约数、最小公倍数

2017年11月12日 ⁄ 综合 ⁄ 共 3062字 ⁄ 字号 评论关闭

一、整除

一个整数a能被另一个整数d整除,记作d|a,意味着存在某个整数k,有 a=kd

整除的性质:

(1)    如果 d|a 则对于任意整数k d|ka

(2)    如果 d|a d|b,则d|(a±b)

(3)    如果 b|a a|b,则a=b

整除的特殊例子:

l         2能整除a的最末位,则 2|a;若4能整除a的最末两位,则 4|a;若8能整除a的最末三位,则 8|a ……

l         5能整除a的最末位,则5|a;若25能整除a的最末两位,则25|a;若125能整除a的最末位,则5|a;……

l         3能整除a的各位数字之和,则3|a;若9能整除a的个位数字之和则9|a

l         11能整除a的偶数位数字之和与奇数位数字之和的差,则11|a

 

 

二、最大公约数

如果 d|a 并且d>0,则我们说da的约数(Divisor)。一个整数a的约数最小为1,最大为|a|。每个整数a都可以被其平凡约数1a整除。a的平凡约数也称为a的因子(Factor)
   
如果da的约数并且也是b的约数,则dab的公约数(Common Divisor)1是任意两个整数的公约数。最大公约数(Greatest Common Divisor)指所有公约数中最大的那个,ab的最大公约数记作gcd(a,b)。定义gcd(0,0)=0 gcd(0,N)=N
   
最大公约数的一些性质

(1)    gcd(a,ka) = |a|

(2)    对任意整数ab,如果 d|a d|b,则d|gcd(a,b)

(3)    对所有整数ab以及任意非负数ngcd(an,bn)=n gcd(a,b)

(4)    对所有正整数dab,如果d|ab并且gcd(a,d)=1,d|b

(5)    如果qra除以b的商和余数,即a=b×q+r,则gcd(a,b)=gcd(b,r)

 

三、最小公倍数

如果d|a 则我们称ad的倍数(Multiple)

如果ma的倍数并且也是b的倍数,则mab的公倍数(Common Multiple)。任意两个整数的公倍数有无限多个,其中最小的那个称为最小公倍数(Least Common Multiple)ab的最小公倍数记作lcm(a,b) lcm(0,0)=0 lcm(0,N)=0

公倍数的性质:

       lcm(a,b)=a×b / gcd(a,b)

 

四、算法分析及实现

1.最大公约数的实现

公约数算法的实现其实是直接使用的上面公约数性质中的第5条,关键是要做到深刻理解为什么该性质能用辗转相除来实现。即为什么 a=b×q+r => gcd(a,b)=gcd(b,r)

       首先看一个例子

        gcd(1001,767)

     = gcd(767,234)

       = gcd(234,65)

       = gcd(65,39)

       = gcd(39,26)

       = gcd(26,13)

       = gcd(13,0)

       = 13

那么为什么可以这样呢?

首先,我们要对上面的整除性质(2)做一个推论:

如果d|(a+b),且d|a,则d|b

证明: 利用整除的性质 d|a a=kd ,有:

              d|(a+b) (a+b)=kd

                     d|a, 所以 存在一个整数k1,有 a=k1d

                (a+b)=kd

                     k1d + b = kd

                     b = kd-k1d

              kk1都是整数,所以k-k1也是一个整数,因此 d|b

证毕。

 

那么根据以上推论

gcd(b,r)=m

则有 m|b m|r

假设q是一整数则 m|(q×b)

那么 m|( q×b+r )

a=q×b+r

于是,m|a

       也就是说,若a能表示成 q×b+r 的形式,则gcd(a,b)=gcd(b,r)

       那么为什么要辗转相除到b=0的时候a就是最小公约数呢?

因为若a能表示为 q×b+r 的形式,则有gcd(a,b)=gcd(b,r),且若b也能继续拆解,则能一直如此辗转下去,一直到gcd(0,rn)时,无法继续拆解,则说明rn已经是所能得到的最大公约数。如下面的例子:

gcd(125,25) 显然25|125,所以125 mod 25 = 0,也就是说会被拆解成25×5+0的形式,那么此时,他们的最大公约数显然就是除数了。

 

2.最小公倍数的实现

       由于我们已经有最小公倍数的性质 lcm(a,b)=a×b / gcd(a,b),所以直接利用这个性质就可以得到最小公倍数

 

 

五、实现代码(关键代码)

     // (递归版)

int gcd( int a, int b )

     {

         // 如果b0a就是ab的最大公约数

         if ( b==0 )

              return a;

 

         // 否则,继续辗转相除,递归调用自身,以获得最大公约数

         return gcd( b, a%b );

     }

 

     // 当然,递归调用时由于参数的传递会导致堆栈的过度参与,因此我们还可以使用迭代形式

     // 来实现这个算法以提高时间和空间效率。虽然效果是有限的,算法复杂度仍然相同,但是

     // 完善本身就是我们追求的目标,所以也将这个代码列印如下

     int gcd2( int a, int b)

     {

         // 这里必须引入一个临时变量,否则 a=b, b=a%b 的话,由于a=b在前,那么进行b=a%b运算

// 时,由于a==b,所以结果必然为0

         for ( int Temp=b; b!=0; Temp=a,a=b, b=Temp%b );

 

         return a;

     }

 

     int lcm( int a, int b )

     {

         if ( a*b==0 )          // 如果其中一个数是0,那么最小公倍数是0

              return 0;

 

// 这里利用了最小公倍数和最大公约数的性质来求取最小公倍数

         return a*b / gcd(a,b);

     }

 

 

两个最小公倍数版本的效率比较(参考):

运行平台:    Athlon 64X2 Dual 4200+ 2GB内存

编译环境:    Visual Studio 2003.net,Windows32 Console Project, Debug Version(缺省配置)

测试代码:

     clock_t clk=clock();

     unsigned int Count=99999999;

     while( (Count--)!=0 )

     {

        gcd(1001,767);               // 此处在进行算法2测试时换成gcd2

     }

     clk = clock()-clk;

 

测试结果   gcd      31563,31500

              gcd2     15906,15891

 

从结果可以看出,迭代版比递归版高效近一倍,估计是由于在函数调用过程还要进行额外的参数入栈,指令地址跳转(Jump)操作,然后在进入函数时,还有将参数出栈,返回值操作等等~由于算法本身耗时很少,所以这些琐碎的语言本身的内部操作所带来的成本也就显得格外的高。这在较复杂的算法中体现并不会那么明显,但若迭代版比较容易实现,那么还是尽量使用迭代版来的实在。

抱歉!评论已关闭.