算法16---数论5---最大公约数,最小公倍数
有一个难点就是stein算法的证明;
首先引进一个符号:g_c_d是greatest common divisor(最大公约数)的缩写,g_c_d( x,y ) 表示x和y的最大公约数。然后有一个事实需要了解:一个奇数的所有约数都是奇数。这个很容易,下面我们要用到。
来研究一下最大公约数的性质,我们发现有 g_c_d( k*x,k*y ) = k*g_c_d( x,y ) 这么一个非常好的性质(证明我就省去了)。说他好是因为他非常符合我们化小的思想。我们试取 k=2 ,则有 g_c_d( 2x,2y ) = 2 * g_c_d( x,y )。这使我们很快联想到将两个偶数化小的方法。那么一奇一个偶以及两个奇数的情况我们如何化小呢?
先来看看一奇一偶的情况: 设有2x和y两个数,其中y为奇数。因为y的所有约数都是奇数,所以 a = g_c_d( 2x,y ) 是奇数。根据2x是个偶数不难联想到,a应该是x的约数。我们来证明一下:(2x)%a=0,设2x=n*a,因为a是奇数,2x是偶数,则必有n是偶数。又因为 x=(n/2)*a,所以 x%a=0,即a是x的约数。因为a也是y的约数,所以a是x和y的公约数,有 g_c_d( 2x,y ) <= g_c_d( x,y )。因为g_c_d( x,y )明显是2x和y的公约数,又有g_c_d( x,y ) <= g_c_d( 2x,y ),所以 g_c_d( 2x,y ) = g_c_d( x,y )。至此,我们得出了一奇一偶时化小的方法。
再来看看两个奇数的情况:设有两个奇数x和y,似乎x和y直接向小转化没有什么太好的办法,我们可以绕个道,把x和y向偶数靠拢去化小。不妨设x>y,我们注意到x+y和x-y是两个偶数,则有 g_c_d( x+y,x-y ) = 2 * g_c_d( (x+y)/2,(x-y)/2 ),那么 g_c_d( x,y ) 与 g_c_d( x+y,x-y ) 以及 g_c_d( (x+y)/2,(x-y)/2 ) 之间是不是有某种联系呢?为了方便我设 m=(x+y)/2 ,n=(x-y)/2 ,容易发现 m+n=x ,m-n=y 。设 a = g_c_d( m,n ),则 m%a=0,n%a=0 ,所以 (m+n)%a=0,(m-n)%a=0 ,即 x%a=0 ,y%a=0 ,所以a是x和y的公约数,有 g_c_d( m,n )<= g_c_d(x,y)。再设 b = g_c_d( x,y )肯定为奇数,则 x%b=0,y%b=0 ,所以 (x+y)%b=0 ,(x-y)%b=0 ,又因为x+y和x-y都是偶数,跟前面一奇一偶时证明a是x的约数的方法相同,有 ((x+y)/2)%b=0,((x-y)/2)%b=0 ,即 m%b=0 ,n%b=0 ,所以b是m和n的公约数,有 g_c_d( x,y ) <= g_c_d( m,n )。所以 g_c_d( x,y ) = g_c_d( m,n ) = g_c_d( (x+y)/2,(x-y)/2 )。
我们来整理一下,对两个正整数 x>y :
1.均为偶数 g_c_d( x,y ) =2g_c_d( x/2,y/2 );
2.均为奇数 g_c_d( x,y ) = g_c_d( (x+y)/2,(x-y)/2 );
2.x奇y偶 g_c_d( x,y ) = g_c_d( x,y/2 );
3.x偶y奇 g_c_d( x,y ) = g_c_d( x/2,y ) 或 g_c_d( x,y )=g_c_d( y,x/2 );
补充说明;
最大公约数还有一种方法,就是用欧几里得算法求最大公约数;
举例说明:
求gcd(30,21)
采用欧几里得算法:
伪代码:
euclid(a,b)
if b==0
return a;
else return euclid(b,a mod b);
example:
euclid(30,21)=euclid(21,9)=euclid(9,3)=euclid(3,0)=3
1 /* 2 题目:最大公约数 3 author taoliu——alex 2016.10 number4 4 5 主要实现: 6 求最大公约数 7 8 我们主要采用两种方法来实现,一种是最常用的辗转相除法; 9 另一种采用stein 算法 10 第一种适合比较小的数字之间的计算;当时如果数据过大,就不适合了; 11 stein算法就采用的是整数的以为和普通的加减方法实现的。 12 13 14 再补充一种使用欧几里得算法求最大公约数的方法 15 16 */ 17 18 19 20 21 22 #include <stdio.h> 23 24 25 26 /* 27 辗转相除法; 28 */ 29 int gcd(int a,int b) 30 { 31 int m; 32 int n; 33 if (a>b) 34 { 35 m=a; 36 n=b; 37 } 38 else 39 { 40 m=b; 41 n=a; 42 } 43 int r=m%n; 44 while(r!=0) 45 { 46 m=n; 47 n=r; 48 r=m%n; 49 } 50 return n; 51 } 52 53 54 /* 55 方法2:stein算法; 56 假设计算a和b两个数的最大公约数; 57 (1)首先判断a或者b的值,如果a=0,b就是最大公约数,相反,a就是最大公约数;如果a和b都不为0;就执行下一步; 58 (2)完成a1=a,b1=b,c1=1的赋值; 59 (3)判断an,bn是否为偶数,若都为偶数,则使a(n+1)=an/2,b(n+1)=bn/2,c(n+1)=cn*2;如果判断an和bn中包含一个技术,则执行(4)(5)(6); 60 (4)若an是偶数,bn是奇数,则完成a(n+1)=an/2,b(n+1)=bn,c(n+1)=cn的赋值; 61 (5)若an是奇数,bn是偶数,则完成a(n+1)=an,b(n+1)=bn/2,c(n+1)=cn的赋值; 62 (6)若an,bn是奇数,,则完成a(n+1)=|an-bn|,b(n+1)=min(an,bn),c(n+1)=cn的赋值; 63 (7)n累加1,跳转到第(3)步进行下一轮运算。 64 65 */ 66 67 int gcd2(int a,int b) 68 { 69 int m,n; 70 if (a>b) //还是使用m保存较大的数; 71 { 72 m=a; 73 n=b; 74 } 75 else 76 { 77 m=b; 78 n=a; 79 } 80 81 if (n==0) 82 { 83 return m; 84 } 85 if (m%2==0&&n%2==0) 86 { 87 return gcd2(m/2,n/2); 88 } 89 if (m%2==0) 90 { 91 return gcd2(m/2,n); 92 } 93 if (n%2==0) 94 { 95 return gcd2(m,n/2); 96 } 97 return gcd((m+n)/2,(m-n)/2); 98 } 99 100 101 //欧几里得算法 102 int euclid(int a,int b) 103 { 104 int m,n; 105 if (a>b) //还是使用m保存较大的数; 106 { 107 m=a; 108 n=b; 109 } 110 else 111 { 112 m=b; 113 n=a; 114 } 115 if (n==0) 116 { 117 return m; 118 } 119 else 120 return euclid(b,a%b); 121 } 122 123 124 int main() 125 { 126 int ans1=gcd(6,4); 127 printf("%d ",ans1 ); 128 int ans2=gcd2(9,3); 129 printf("%d ",ans2 ); 130 int ans3=gcd2(12,9); 131 printf("%d ",ans3 ); 132 return 0; 133 }
最小公倍数源代码
1 /* 2 题目:最小公倍数 3 author taoliu——alex 2016.10 number4 4 5 主要实现: 6 求最小公倍数 7 8 9 */ 10 #include <stdio.h> 11 12 13 14 int gcd(int a,int b) 15 { 16 int m; 17 int n; 18 if (a>b) 19 { 20 m=a; 21 n=b; 22 } 23 else 24 { 25 m=b; 26 n=a; 27 } 28 int r=m%n; 29 while(r!=0) 30 { 31 m=n; 32 n=r; 33 r=m%n; 34 } 35 return n; 36 } 37 38 39 int lcm(int a,int b) 40 { 41 int c=gcd(a,b); 42 int d=(a*b)/c; 43 return d; 44 45 } 46 47 48 49 int main() 50 { 51 int ans=lcm(3,4); 52 printf("%d ", ans); 53 return 0; 54 }