淺談大數除法 Part 1: 長除法

整數的加減乘除四種運算,無論是在國小算術或是在程式設計中,除法都比其他三種運算來得麻煩。舉例來說,假定我們想動手算 1650794238÷26451,長除法的第一步是
我們必須猜出 內要填的數字,而這正是除法計算痛苦的來源。因為我們沒有表可以查,只能猜一個數字 α,放入 中,看看 16507926451α 是否介於 026450 之間,而如果不幸猜錯,還得重猜並重新計算一次乘法和減法。

我們把這個猜商問題寫得再清楚一點:給定兩個 b 進位的整數 {ξ=xn1x1x0=xn1bn1++x1b+x0,η=yn1y1y0=yn1bn1++y1b+y0, 其中
  • 0xib1 對於所有的 0in2,而 xn10 (可以超過 b1,如上面的例子 b=10,n=5a4=16)。
  • 0yib1 對於所有的 0in1,且 yn10
  • ξ/ηb1
目標找出 a:=ξ/η。有鑑於網路上多數的大數長除法 code,猜商步驟亂寫 (無誤),我決定在這篇導正視聽 (?),介紹一個保證在 O(1) 次內猜到 a 的策略。

策略一:枚舉 a=0,1,,b1

最糟情況猜商次數 Θ(b),不解釋。

策略二:在 0,1,,b1 之間做二分搜找出 a

這是策略一的簡單改良,猜商次數 Θ(logb),一樣不解釋。

策略三:利用 xn1yn1,縮小策略二的二分搜範圍

想法是這樣的:因為
{xn1bn1ξ(xn1+1)bn11,yn1bn1η(yn1+1)bn11,
我們知道
xn1yn1+1axn1yn1
但不幸的是,如果 yn1=1,二分搜的範圍大小在最糟情況還是 Θ(b),也就是猜商次數依然是 Θ(logb)

策略四:看最高一位不夠,那你有看最高兩位嗎?

如果 n=1,則 a=x0/y0 本來就能直接計算 (如同小學算術中,n 位數除以 1 位數的商的每位數,都能從九九乘法表直接得到)。以下假定 n2:如果我們考慮 X:=xn1b+xn2,Y:=yn1b+yn2,利用策略三的想法,一樣有
XY+1aXY
因為
XYXY+1=XY(Y+1)bY1
我們發現 XY+1XY 之間,最多只能包含 2 個整數,因此無論 b 是多少,在任何情況下都能在 2 次內猜中 a

有了上面的猜商策略四,設在 b 進位表示下 ξη 的長度分別為 nk,則用長除法計算 ξ/η 的時間複雜度就是 O(kn),與 b 無關。讀者可以在 ZJ c429 測試這個猜商策略。這個猜商策略給了我們一個啟示:如果我們小時候背的不是 9×9 乘法表,而是 99×9 乘法表,也許在計算除法時,就不會這麼痛苦了 XD

可能會有人問:大數除法有 O(nlogn) 時間複雜度的演算法,那為什麼我還在這邊惹人嫌討論怎麼把 naïve 演算法寫好。我故意把時間複雜度寫成 O(kn) 而不是 O(n2),其實是在暗示當 kn (但 k 還是大到 η 沒辦法用 long long 之類的變數型態裝下),naïve 演算法的 O(kn) 會比常數很大的快速除法 O(nlogn) 快很多。順便一提,我測試了一下,發現 python 與 java 的內建大數並沒有對這種情況做特殊處理:用 python 與 java 的內建大數計算某個長度 30 萬的整數除以 2 的商,執行時間分別為 1.61.9 秒,超慢 wwwwww

留言

這個網誌中的熱門文章

2020 全國賽驗題心得

水母上 Divide-and-Conquer

109 學年度全國資訊學科能力競賽各題詳解