Stein算法求最大公約數 ( ANSI C )

首先引進一個符號: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 );

現在我們已經有了遞歸式,還需要再找出一個退化情況。注意到 g_c_d( x,x ) = x ,我們就用這個。

int Stein( unsigned int x, unsigned int y )
/* return the greatest common divisor of x and y */
{
        
int factor = 0;
        
int temp;

        
if ( x < y )
        
{
                temp 
= x;
                x 
= y;
                y 
= temp;
        }

        
if ( 0 == y )
        
{
                
return 0;
        }

        
while ( x != y )
        
{
                
if ( x & 0x1 )
                
{/* when x is odd */
                        
if ( y & 0x1 )
                        
{/* when x and y are both odd */
                                y 
= ( x - y ) >> 1;
                                x 
-= y;
                        }

                        
else
                        
{/* when x is odd and y is even */
                                y 
>>= 1;
                        }

                }

                
else
                
{/* when x is even */
                        
if ( y & 0x1 )
                        
{/* when x is even and y is odd */
                                x 
>>= 1;
                                
if ( x < y )
                                
{
                                        temp 
= x;
                                        x 
= y;
                                        y 
= temp;
                                }

                        }

                        
else
                        
{/* when x and y are both even */
                                x 
>>= 1;
                                y 
>>= 1;
                                
++factor;
                        }

                }

        }

        
return ( x << factor );
}

 

Stein算法的優勢是不言而喻的,上面的代碼只有位運算和減法,不僅速度加快,而且結局了歐幾里德算法求兩個大數最大公約數的不便。不僅如此,Stein算法還可以很容易的寫成彙編語言實現。

 
發佈了51 篇原創文章 · 獲贊 18 · 訪問量 24萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章