欧几里得算法--二进制欧几里得算法--拓展欧几里得算法

首先说一下,欧几里得算法:求两个数的最大公约数

教材的写法是:

1 //输入要求a>=b>=0 
2 unsigned euclid(unsigned a,unsigned b){
3     if(b==0) return a;  
4     return euclid(b,a%b);
5 }

当然也可以改成非递归的迭代版本:

 1 int Gcd(int a, int b)
 2 {
 3     while(b != 0)
 4     {
 5       int r = b;
 6       b = a % b;
 7       a = r;
 8     }
 9     return a;
10 }

上个世纪60年代研究人员对这个古老的算法进行了改进,称为二进制欧几里得算法:

基于的原理是:

  1. 若 N 和 M 是偶数,gcd(N, M) = 2 gcd(N/2, M/2) ,
  2. 若 N 是偶数而 M 是奇数,那么 gcd(N, M) = gcd(N/2, M) ,
  3. 若 N 和 M 都是奇数,那么 (由于 N-M 是偶数) |N-M| < max(N,M) 。用 |N-M| 替换两者中的较大者   

    该算法称为 二进制 因为,与原来的不同,它不使用通常的除法而只使用除以2的。由于计算机数字总是用二进制系统表示,你不会对有一种特殊的 机器指令 以高效的方式实现 除以 2而感到惊讶。这称为 右移位,其中最右一位被舍弃,其他的位向右移动一位,而最左一位被设为0。

    另一个顺手拈来的操作是按位与 & 。对任意整数 N 来说,N & 1 是 1 或 0 。当且仅当 N 是奇数时它为 1 。按位与也在硬件中实现,例如,作为 机器指令 

递归版本:

 1 unsigned int gcd(unsigned int u, unsigned int v)
 2 {
 3     // simple cases (termination)
 4     if (u == v) return u;
 5     if (u == 0) return v;
 6     if (v == 0) return u;
 7 
 8     // look for factors of 2
 9     if (~u & 1) // u is even
10     {
11         if (v & 1)  return gcd(u >> 1, v);             // u is even,v is odd
12         else        return gcd(u >> 1, v >> 1) << 1;   // both u and v are even
13             
14     }
15     if (~v & 1)    return gcd(u, v >> 1);              // u is odd, v is even
16     
17     
18     // both u and v are odd,reduce larger argument
19     if (u > v)     return gcd((u - v) >> 1, v);
20 
21     return gcd((v - u) >> 1, u);
22 }
递归—二进制欧几里得算法

当然也可以改成迭代版本:

 1 unsigned int gcd2(unsigned int u, unsigned int v)
 2 {
 3   int shift;
 4 
 5   /* GCD(0,v) == v; GCD(u,0) == u, GCD(0,0) == 0 */
 6   if (u == 0) return v;
 7   if (v == 0) return u;
 8  
 9   /* Let shift := lg K, where K is the greatest power of 2
10         dividing both u and v. */
11   for (shift = 0; ((u | v) & 1) == 0; ++shift) {
12          u >>= 1;
13          v >>= 1;
14   }
15  
16   while ((u & 1) == 0)
17     u >>= 1;
18  
19   /* From here on, u is always odd. */
20   do {
21        /* remove all factors of 2 in v -- they are not common */
22        /*   note: v is not zero, so while will terminate */
23        while ((v & 1) == 0)  /* Loop X */
24            v >>= 1;
25 
26        /* Now u and v are both odd. Swap if necessary so u <= v,
27           then set v = v - u (which is even). For bignums, the
28           swapping is just pointer movement, and the subtraction
29           can be done in-place. */
30        if (u > v) {
31          unsigned int t = v; v = u; u = t;}  // Swap u and v.
32        v = v - u;                       // Here v >= u.
33      } while (v != 0);
34 
35   /* restore common factors of 2 */
36   return u << shift;
37 }
迭代—二进制欧几里得算法

拓展欧几里得的算法是:

        欧几里得定理告诉我们,只要能找到整数x和y,使得ax+by=d,则d就是a和b的最大公因子。而且如果d是a和b的最大公因子,则d一定可以表示为ax+by的形式。    

        运用下列函数的前提是gcd(a,b)=d,即d是a和b的最大公约数,则该函数可以计算得到ax+by=d中的x值。

 1 //要求是gcd(a,b)=d      
 2 int e_gcd(int a,int b,int &x,int &y){
 3     if(b==0){
 4         x=1;
 5         y=0;
 6         return a;
 7     }
 8     int ans=e_gcd(b,a%b,x,y);
 9     int temp=x;
10     x=y;
11     y=temp-a/b*y;
12     return ans;
13 } 

拓展欧几里得算法的一个运用是计算模n逆元:如果有ax=1(mod N),那么我们称x是a的模N逆元。

 1 //计算得到 “a模m逆元” 
 2 int cal(int a,int m){   
 3     int x,y;
 4     int gcd=e_gcd(a,m,x,y);
 5     if(1%gcd!=0)  return -1;
 6     x*=1/gcd;
 7     m=abs(m);
 8     int ans=x%m;
 9     if(ans<=0) ans+=m;
10     return ans;
11 }

其实,“a的模N逆元”,主要在RSA中计算解密密钥时用的较多。关于RSA的细节,这里不赘述,详情参加另外一篇博客。在计算RSA中加密时,需要用到快速幂取模算法,详见九月份的一篇博客。

贴一个应用:

ural  Idempotents

Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u

Description

The number x is called an idempotent modulo n if
xx = x (mod n)
Write the program to find all idempotents modulo n, where n is a product of two distinct primes p and q.

Input

First line contains the number k of test cases to consider (1 ≤ k ≤ 1000). Each of the following k lines contains one number n < 10 9.

Output

Write on the i-th line all idempotents of i-th test case in increasing order. Only nonnegative solutions bounded by n should be printed.

Sample Input

inputoutput
3
6
15
910186311
0 1 3 4
0 1 6 10
0 1 303395437 606790875

AC代码:

 1 #include <iostream>
 2 using namespace std;
 3 
 4 
 5 int getp(int n){   //得到大于1的最小的因数 
 6     int p=2;
 7     while(n%p!=0){
 8         p++;
 9     }
10     return p;
11 }
12 
13 void e_gcd(int a,int b,int &x,int &y){
14     if(b==0){
15         x=1;
16         y=0;
17         return ;
18     }
19     e_gcd(b,a%b,x,y);
20     int temp=x;
21     x=y;
22     y=temp-a/b*y;
23     return ;
24 } 
25 
26 int main()
27 {
28     int k;
29     cin>>k;
30     while(k--)
31     {
32         int n;
33         cin>>n;
34         int p=getp(n),q=n/p;
35         int x,y;
36         e_gcd(p,q,x,y) ;
37         int x1=x*p;
38         if(x1<0) x1+=n;
39         
40         e_gcd(q,p,x,y) ; 
41         int x2=x*q;
42         if(x2<0) x2+=n;
43         if(x2<x1){
44             int temp=x2;
45             x2=x1;
46             x1=temp;
47         }
48         cout<<0<<" "<<1<<" "<<x1<<" "<<x2<<endl;
49     } 
50     
51 } 
原文地址:https://www.cnblogs.com/liugl7/p/4909584.html