您的位置:首页 > 其它

经典算法(5)- 用二进制方法实现扩展的最大公约数(Extended GCD)

2011-05-19 11:52 435 查看
二进制方法中,只需要移位(<<和>>)和加减操作(+和-),不像欧几里德算法中需要乘法和除法运算。虽然算法效率更高,但是程序的可读性和可维护性差一些。

如果设d=gcd(u,v) = u.x + v.y, 本算法涉及到六种操作:

1)已知ext_gcd(u,v)如何求ext_gcd(u,2v)=u'.x' + v'.y',其中u为奇数,v可奇可偶,d=gcd(u,v)为奇数;

2)已知ext_gcd(u,v)如何求ext_gcd(2u,v)=u'.x' + v'.y',其中v为奇数,u可奇可偶,d=gcd(u,v)为奇数;

3)已知ext_gcd(u-v,v)如何求ext_gcd(u,v)=u'.x' + v'.y';

4)已知ext_gcd(u,u-v)如何求ext_gcd(u,v)=u'.x' + v'.y';

5)已知u/2^c = v = d(c为大于0的整数),如何求ext_gcd(u,v)=u.x' + v.y';

6)已知u= v/2^c = d(c为大于0的整数),如何求ext_gcd(u,v)=u.x' + v.y'.

其中第1)种操作比较麻烦,第2)种操作只需要在第1)种操作的基础上将u和v交换一下。下面介绍一下第1)种操作的原理:

在第1)操作中,因为d=gcd(u,2v)=gcd(u-v,v)=(u-v)x+v(x+y),设u1=u-v,x1=x,v1=v,y1=x+y,即问题转化为已知d=u1.x1+u1.y1,求ext_gcd(u,2v)=u.x' + (2v)y'=d;

如果y为偶数,显然可以采用这样一组值:x'=x,y'=y/2;但是如果y为奇数,则需要将d表示为d=u1(x1+v1.k) + v1(y1-u1.k),其中k为正奇数1,3,5....,找到这样的k,满足x' = x +v.k和y'=(y-u.k)/2都不为0。

第3)种操作比较简单,因为d=gcd(u-v,v) = (u-v)x + vt = ux + v(y-x),即x'=x,y'=y-x;

第4)种操作类似,因为d=gcd(u,u-v) = ux + (u-v)t = u(x+y) - vy,即x'=x+y,y'=-y;

第5)种操作,可以得到d=v=u + (1-2^c).v,即x'=1,y'=1-2^c;

第6)种操作只需要在第5)种操作的基础上将u和v交换一下。

在下面的实现中,只有上面第2)和6)种操作需要改变v和y的符号。

import java.util.Stack;
/**
*
* @author ljs 2011-5-19
*
* solve extended gcd using Binary Method
*
*/
public class ExtGCD_Binary {
/**
* caculate x,y to satisfy Bezout Identity: u.x + v.y = gcd(u,v)
* @param u
* @param v
* @return {d,x,y} for: d = u.x + v.y;
*/
public static int[] extGCDBinary(int u,int v){
u=(u<0)?-u:u;
v=(v<0)?-v:v;

if(u==0)
return new int[]{v,0,1};
if(v==0)
return new int[]{u,1,0};

int k=0;
while((u & 0x01)==0 && (v & 0x01) == 0){
u>>=1; //divide by 2
v>>=1;
k++;
}
//with respect to d=gcd(u,v)=u.s+v.t
/* the operation type is defined as:
1: c, u / 2^c (push two numbers to stack: first push c, then push the op number 1)
2: c, v / 2^c  (push two numbers to stack: first push c, then push the op number 2)
3: gcd(u-v, v) (only push the op number 3 to stack)
4: gcd(u, u-v) (only push the op number 4 to stack)
*/
Stack<Integer> ops= new Stack<Integer>();
//at this time, there is at least one number is odd between m and n
int t=-v; //set it negative for later comparison of (t>0)
if((v & 0x01)==1){
//if v is odd
t = u;
}

//process t as a possible even number
boolean firstNoOp = true;
while(t != 0){
if(firstNoOp) {
firstNoOp = false;
}else{
if(t>0)
ops.push(3);
else
ops.push(4);
}

int c=0;
while((t & 0x01)==0){
//do until t is not even
t>>=1;
c++;
}
if(t>0) {//u > v (the max is replaced by |t|)
u=t;
if(c>0) {
ops.push(c);
ops.push(1);
}
}else{ //u<v (the max is replaced by |t|)
v=-t;
if(c>0){
ops.push(c);
ops.push(2);
}
}
//now u and v are all odd, then u-v is even
t = u-v;
}
int d = u;

//the following steps are to caculate x,y in d=u.x + v.y

int x=1,y=0;
//special processing for the last operation
if(!ops.isEmpty()){
//e.g. for input u=3,v=3, then ops is empty, d=3
int op = ops.pop();
int c = ops.pop();
//op number 3 and 4 can not be the last step since: u-v = v, then v is even; or u=u-v, then u=0;
//both are not possible
int tmp = 1<<c;
if(op == 1){
x = 1;
y = 1- tmp;

u = v;
for(int i=0;i<c;i++)
u <<= 1;
}else if(op == 2){
x = 1- tmp;
y = -1;

v = -u;
for(int i=0;i<c;i++)
v <<= 1;
}
}
while(!ops.isEmpty()){
int op = ops.pop();
switch(op){
case 1:{
int c = ops.pop();
for(int i=0;i<c;i++){
if((x & 0x01)==0){
//if x is even
x >>= 1;
}else{
y += u;
int tmp = x - v;
while(y ==0 || tmp ==0){
y += (u<<1); //*2
tmp -= (v<<1); //*2
};
x = tmp >>1;
}
u <<= 1;
}
break;
}
case 2:{
int c = ops.pop();
for(int i=0;i<c;i++){
if((y & 0x01)==0){
//if y is even
y >>= 1;
}else{
x += v;
int tmp = y-u;
while(x ==0 || tmp ==0){
x += (v<<1);  //*2
tmp -= (u<<1);  //*2
};
y = tmp >>1;
}
v <<= 1;
}
y = -y;
v = -v;
break;
}
case 3:
y -= x;
u += v;
break;
case 4:
x += y;
v = u - v;

y = -y;
break;
}
}
//e.g. gcd(1,4)
y = (v<0)?-y:y;

for(int i=0;i<k;i++)
d <<=1;
return new int[]{d,x,y};
}

public static void print(int m,int n,int[] extGCDResult){
m = (m<0)?-m:m;
n = (n<0)?-n:n;
System.out.format("extended gcd of %d and %d is: %d = %d.{%d} + %d.{%d}%5s%n",m,n,extGCDResult[0],m,extGCDResult[1],n,extGCDResult[2],(m*extGCDResult[1] + n * extGCDResult[2] == extGCDResult[0])?"OK":"WRONG!!!");
}

public static void main(String[] args) {
int m = -18;
int n= 12;
print(m,n,extGCDBinary(m,n));

//co-prime
m = 15;
n= 28;
print(m,n,extGCDBinary(m,n));

m = 6;
n= 3;
print(m,n,extGCDBinary(m,n));

m = 6;
n= 3;
print(m,n,extGCDBinary(m,n));

m = 6;
n= 0;
print(m,n,extGCDBinary(m,n));

m = 0;
n= 6;
print(m,n,extGCDBinary(m,n));

m = 0;
n= 0;
print(m,n,extGCDBinary(m,n));

m = 1;
n= 1;
print(m,n,extGCDBinary(m,n));

m = 3;
n= 3;
print(m,n,extGCDBinary(m,n));

m = 2;
n= 2;
print(m,n,extGCDBinary(m,n));

m = 1;
n= 4;
print(m,n,extGCDBinary(m,n));

m = 4;
n= 1;
print(m,n,extGCDBinary(m,n));

m = 10;
n= 14;
print(m,n,extGCDBinary(m,n));

m = 14;
n= 10;
print(m,n,extGCDBinary(m,n));

m = 10;
n= 4;
print(m,n,extGCDBinary(m,n));

m = 273;
n= 24;
print(m,n,extGCDBinary(m,n));

m = 120;
n= 23;
print(m,n,extGCDBinary(m,n));

}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: