1. 程式人生 > >如何用匯編語言編寫一個求最大公約數(GCD)的過程——輾轉相除法

如何用匯編語言編寫一個求最大公約數(GCD)的過程——輾轉相除法

選題:《組合語言  基於X86處理器》【Kip Irvine著】——  Chapter7 程式設計練習第6題

         兩個數的最大公約數(GCD)是指能整除這兩個數的最大整數。下述虛擬碼描述的是迴圈整數除法的GCD演算法:

int GCD(int x,int y)
{
     x = abs(x)
     y = abs(y)
     do{
           int n = x % y
           x = y
           y = n
      }while(y>0)
      return x
}

   用匯編語言實現該程式,並編寫測試程式,通過向該函式傳遞不同的數值對其進行多次呼叫。

---------------------------------------------------------------------------------------------------------------------------

我們可以從題目得到兩個很重要的資訊:

一、最大公約數的概念能整除這兩個數的最大整數

二、演算法思想:

1、兩個數假定x,y;

2、兩者相除取餘;

3、若,餘數不為0,則將除數作為下一次的被除數,商作為下一次的除數;

4、直到找到餘數為零時的商,返回此時商的值。

而我們將上述的演算法思想取了一個好聽的名字叫做“輾轉相除法”,此外,大家可以自行驗證一下該方法是否能準確的求出兩個數值的最大公約數。

接下來我們就來講講,如何使用匯編語言來編寫這樣的一個演算法:

1、首先我們建立asm專案,配置好彙編環境與外部庫路徑;然後我們初始化DWORD型別的A,B兩個數,另外準備一個變數result用於儲存最後找到的最大公約數;此外,另編寫三句提示語便於清晰的顯示:

Include Irvine32.inc

.data
    prompt1 BYTE '第一個數字是:',0
    prompt2 BYTE '第二個數字是:',0
    prompt3 BYTE '兩個數字的最大公約數是:',0

    A DWORD 14750 
    B DWORD 37550 
    result DWORD  ?

2、然後由於我們想多次輸出過程,所以我們繼續編寫一個輔助顯示的——Display函式;

;---------------------------
Display PROC
;用於被呼叫,顯示結果
;---------------------------
    call  WriteInt		;呼叫writeInt,輸出數字
    call  crlf
    ret
Display ENDP

3、接著開始構思程式主體,思路大概是:先輸出第一個數字,再輸出第二個數字,然後呼叫演算法求出結果,最後輸出結果;

.code
main PROC
;顯示第一個數字:
    mov	  EDX,OFFSET prompt1	        ;用offset獲取顯示語的偏移地址,並存入EDX中
    call  WriteString			;呼叫WriteString,將顯示語以字串形式顯示出來
    mov   EAX,A
    call  Display			;呼叫Display函式
;顯示第二個數字:
    mov	  EDX,OFFSET prompt2
    call  WriteString
    mov   EBX,B
    call  Display
;呼叫演算法:
    mov   EDX,OFFSET prompt3
    call  WriteString
    call  ZhanzhuanMethod		;呼叫輾轉相除函式,求兩個數字的最大公約數
;顯示結果:
    mov   EAX, result			;將結果result存放入EAX中
    call  Display			;再次呼叫Display函式,輸出結果

4、然後就是最關鍵的GCD演算法實現了,在這裡我們對先梳理一下演算法思路:

  • 將A、B的值分別送入暫存器EAX與EBX中;
  • 為了保證被除數大於除數,故使用cmp指令比較A、B大小;
  • 被除數大於除數,即使用無符號數跳轉指令JNB(leftOp>=rightOp),其結果為否,則交換;結果為真,則跳轉到L1;
  • 編寫L1自身迴圈用於找到餘數為0時的商,其中若找到餘數為0的商則跳,將結果放入result並ret。
具體如下:
;---------------------------
ZhanzhuanMethod PROC		;輾轉相除函式
;用於求最大公約數的方法
;---------------------------
    mov  EAX, A
    mov  EBX, B
    cmp  EAX, EBX		;比較EAX與EBX 
    JNB  L1			;EAX大於或等於EBX,則跳轉到L_1
    mov  B, EAX			;若上一步為否,則:交換位置,避免除數大於被除數
    mov  A, EBX

L1:
    mov  EAX, A		        ;已交換/預設不變(滿足條件時),保證了A > B
    mov  EBX, B
    mov  EDX, 0			;初始化EDX為0
    div  EBX			;A%B,兩個數字相除,餘數自動存入EDX中
    cmp  EDX, 0			;比較EDX與0
    JE   L_End			;JE指令為等於跳轉。若餘數為0,則找到了最終的最大公約數,跳轉到L_End
    mov  EAX, B			;若上一步餘數不為0,則將 除數B 放入 EAX 中
    mov  A, EAX			;再把原除數B,通過EAX傳遞給A,作為用作下一次操作的 被除數
    mov  B, EDX			;再把EDX內的餘數,賦給B,作為下一次操作的 除數 (從而再次保證了A > B)
    jmp	 L1

L_End:
    mov  result,EBX
    ret
ZhanzhuanMethod  ENDP

5、最後別忘了關鍵的“尾巴”:
main   ENDP
END    main

那麼到這裡,本題就算是成功的解決了,經過最終的除錯,我們可以看到除錯結果如下:

在環境搭建與連結庫路徑匯入無誤的情況下,程式到底能不能run呢?感興趣的小夥伴們趕快試一下吧!

----------------------------------------------------------------------------------------------------------------------------

附:(完整程式碼)

Include Irvine32.inc

.data
    prompt1 BYTE '第一個數字是:',0
    prompt2 BYTE '第二個數字是:',0
    prompt3 BYTE '兩個數字的最大公約數是:',0

    A DWORD 14750 
    B DWORD 37550 
    result DWORD  ?

.code
main PROC
;顯示第一個數字:
    mov	  EDX,OFFSET prompt1	        ;用offset獲取顯示語的偏移地址,並存入EDX中
    call  WriteString			;呼叫WriteString,將顯示語以字串形式顯示出來
    mov   EAX,A
    call  Display			;呼叫Display函式
;顯示第二個數字:
    mov	  EDX,OFFSET prompt2
    call  WriteString
    mov   EBX,B
    call  Display
;呼叫演算法:
    mov   EDX,OFFSET prompt3
    call  WriteString
    call  ZhanzhuanMethod		;呼叫輾轉相除函式,求兩個數字的最大公約數
;顯示結果:
    mov   EAX, result			;將結果result存放入EAX中
    call  Display			;再次呼叫Display函式,輸出結果
   
;---------------------------
ZhanzhuanMethod PROC			;輾轉相除函式
;用於求最大公約數的方法
;---------------------------
    mov  EAX, A
    mov  EBX, B
    cmp  EAX, EBX			;比較EAX與EBX 
    JNB  L1				;EAX大於或等於EBX,則跳轉到L_1
    mov  B, EAX				;若上一步為否,則:交換位置,避免除數大於被除數
    mov  A, EBX

L1:
    mov  EAX, A				;已交換/預設不變(滿足條件時),保證了A > B
    mov  EBX, B
    mov  EDX, 0				;初始化EDX為0
    div  EBX				;A%B,兩個數字相除,餘數自動存入EDX中
    cmp  EDX, 0				;比較EDX與0
    JE   L_End				;JE指令為等於跳轉。若餘數為0,則找到了最終的最大公約數,跳轉到L_End
    mov  EAX, B				;若上一步餘數不為0,則將 除數B 放入 EAX 中
    mov  A, EAX				;再把原除數B,通過EAX傳遞給A,作為用作下一次操作的 被除數
    mov  B, EDX				;再把EDX內的餘數,賦給B,作為下一次操作的 除數 (從而再次保證了A > B)
    jmp	 L1

L_End:
    mov result,EBX
    ret
ZhanzhuanMethod  ENDP

;---------------------------
Display PROC
;用於被呼叫,顯示結果
;---------------------------
    call  WriteInt			;呼叫writeInt,輸出數字
    call  crlf
    ret
Display ENDP

main ENDP
END  main