算法學習(十四)最大公約數問題

題目描述:
求兩個正整數的最大公約數,如果兩個正整數都很大,有什麼簡單的算法?
例如:給定1100100210001,120200021,求其最大公約數。
分析:
求最大公約數問題最經典的就是“輾轉相除法”,其實就是一個數學問題了,來自歐幾里得的著作《幾何原本》,原來也就是知道這麼個方法,但是具體的原理還真沒考慮過,今天就證明一下:
假設f(x,y)是x,y的最大公約數,這裏有k = x/y,b = x%y,有x= k*y + b。如果一個數能夠同時整除x,y,那麼必能同時整除y和b,所以x,y的最大公約數和b,y的最大公約數相同。則f(x,y) = f(y,x%y)(x>=y>0),將原問題轉化爲求兩個更小的數的問題。
所以解法一,就是輾轉相除法。

int gcd(int x,int y)
{
    if(y != 0)
    {
        return gcd(y,x%y);
    }
    else
        return x;
}

我們用到了取模運算,對於大整數而言,取模運算時非常昂貴的開銷,將成爲整個算法的瓶頸。
我們能不能不用除法呢?
我們仿照歐幾里得的證明方法,如果一個數能同時整除x和y,必能整除x-y,y,而反過來也是可以的,所以f(x,y) = f(x-y,y)。我們就不需要進行除法運算,轉換成簡單的減法運算。
解法二:

int  gcd(int x,int y)
{
    if(x < y)
        return gcd(y,x-y);
    if(y == 0)
        return x;
    else
        return gcd(x,x-y);
}

雖然避免了大整數除法,但是使用減法,在大整數面前,迭代次數也是夠夠的。
能不能將解法一和解法二結合呢?
分析公約數特點:
如果x= k* x1;y = k*y1,那麼f(x,y) = k*f(x1,y1)。
如果x = p* x1,假設p是素數,且y%p != 0,那麼f(x,y) = f(p*x1,y) = f(x1,y)。
那麼素數p如何選擇呢,2是一個特殊的素數,因爲它在二進制中如魚得水,x/2 = x>>1 。
取p = 2;
若x,y都是偶數,f(x,y) = f(x/2,y/2) = 2*f(x>> 1,y>>1)
若x爲偶,y爲奇數,f(x,y) =f(x/2,y) = f(x>>1,y)
若x爲奇數,y爲偶,f(x,y) = f(x,y/2) = f(x,y>>1)
若x,y均爲奇數,f(x,y) = f(y,x-y)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章