contributed by < jouae
>
Github jouae/sqrt
這個頁面談及到關於開根號的演算法跟分析,其中涵蓋 Linux int_sqrt.c Python sqrt 等整數開根號部分
This page discusses algorithms and analysis related to the numerical method for integer square root, including integer square root operations in Linux int_sqrt.c and Python's sqrt.
關鍵字:根號、平方根、快速平方根、牛頓法、查表平方根、整數平方根
keywords: integer square root, Newton's method, fast integer square root, Heron's method
在例題中使用平方和公式來理解,如何從乘法到單純加減與位移運算來開平方根。先觀察其規律
三元平方和
四元平方和
考慮一般化的平方和
觀察規律可以發現 元平方和可以藉由 到 元平方和總和,同時加上一項
其中 而 且
總結上述兩個觀察的結果:
數值系統在二進位表示下為:
為最高位為 之位元,二進位轉十進位如下:
假設 可以用二進位系統逼近:
其中 且 。故逼近過程只要從 也就是最高位元為 開始計算到最低位元 ,確認 或是 對 ,藉由上述 可以確認有兩種可能:
舉例來說,設 則 ,最高位元 從 開始計算。
故 ,因此 的整數平方根為 。
令 為差值表實際值與逼近值的誤差,其中 運用以下性質可以縮減上述計算過程:
由於 與 則可以將 表示為
令 與 。重新將 表示為
同時藉由 則
如果 則
同時
則 。
總結迭代關係為
與
則可以藉由 得知平方根為
英國 University of Sheffield 數學與統計系任教的 Frazer Jarvis 教授在他的著作中有一篇〈Square roots by subtraction〉介紹了一個計算平方根的方法。據其所言:
When I was at school, my mathematics teacher showed me the following very strange method to work out square roots, using only subtraction, which is apparently an old Japanese method.
Mayer Goldberg 在 〈A Spigot-Algorithm for Square-Roots: Explained and Extended〉稱其為 Japanese algorithm ,並提出一改進算法,在內文 3.2 Reconstructing the Original “Japanese Algorithm” 中提及,所謂的 Japanese algorithm 似乎是針對「算盤」計算而演化來。
The “Japanese algorithm” is essentially the optimized algorithm of the previous section, with one additional, non-mathematical optimization, that is probably intended to simplify computation on an abacus.
要會算盤計算的人驗證。
以下敘述 Frazer Jarvis 的〈Square roots by subtraction〉一文中 Japanese algorithm 作法跟證明過程。
此算法在十進位系統中,針對所求 的平方根逼近先初始化數對 為
再藉由兩個迭代規則更新數對
虛擬碼如下:
顯而易見的這個算法的缺陷之一在於 int a
滿足 R2 時,假如 a = 100*a
超過 INT_MAX
會有 overflow 的情況。
Mayer Goldberg 基於以下目標改進 Japanese algorithm :
- To uncover the layered representation that is at the heart of this algorithm, in order to understand what insights it captured about square-roots.
- To use this layered representation to understand how the algorithm works.
- To generalize the algorithm to other roots.
原文中使用十進位系統,此處使用二進位系統表示 且專注於整數型態根號
此處 為最高位為 1 的位元。同時藉由上述表示法有以下關係:
手法如 digit-by-digit 一樣,令所求平方根為
藉由 Nested Parentheses 算平方和的方法,每次可以提取出一項 且 。
舉例來說 時,可以寫成 與 相加的平方
經由這一輪的拆解後就有另一個平方和 包含在第一個括號裏頭,一樣用同樣的手法可以寫成
再來一次
在這個算法重要的地方是我們想要專注於使用減法來找出平方根,所以藉由改寫一些形式我們可以對 以減法的方式找出
原文中由於是十進位系統,所以當 時需要知道起碼 個可能才能推出 。但在二進位系統中,只要確認 是不是 即可
那 可以寫成
int_sqrt
The algorithm is unusual in that it is almost branch-free, requiring only a single final correction step, and in that the number of required iterations is easily predicted in advance. The algorithm is efficient both at small scales and asymptotically, and represents an attractive compromise between speed and simplicity.
由 Mark Dickinson 整理的查表演算法,並在 Python 3.8 中採用。
在 從 √2 的存在談開平方根的快速運算:牛頓法 一文中提及截斷誤差為
上述並非對整數平方根逼近,故為了處理整數取值逼近,上述牛頓法的迭代過程以下取整數函數改寫成
再來 Mark 照著以下思路至證明存在整數根號
Lemma 2.
任意正整數 滿足
Lemma 4.
講了初始 滿足 時,以迭代式生成的數列 有下界存在且為
Lemma 5.
證明少了 only if 部分
非空正整數子集才能應用良序原則,證明中的細節少聲明這部分。
參考 PR #14: Modify the proof of Therorem 6
在文中 Mark 提及該算法計算出的該數列不一定需要收斂至 ,文中使用 "in the sense" 一詞(a general feeling or understanding)描述該算法最終會是一定數,但缺少相關證明與佐證,以下為原文:
Note that while the sequence is guaranteed to encounter eventually, it's not necessarily true that the sequence converges to , in the sense that it's eventually constant.
綜上所述怎麼樣選取初始值是改進這個迭代法效率的要點。
實作上對初始值的選取如下:
流程圖的繪製要依照國際標準 ISO-5807-1985
想要不使用邏輯判斷分支, Mark 提出要從初始值的選取下手。
一個直觀的初始值選擇為超過 最小的 2 的冪(power of 2)
從 bit 的角度以 的冪討論這個問題,先給定義:
藉由不等式的推論過程 Mark 給出 的最小下界:
假設有一非負整數 和 滿足
根據該下取整函數不等式關係得到
不等式兩側由於都是正數,故兩側各別平方不改變不等式方向
對 取位元長度則不等式可以表示為
由於該不等式關係 小於或等於 ,當 減去 後則只滿足小於關係
不等式兩側同減 後同除以 則
再次從取整函數不等式關係得到
再次從 不等式關係出發,對兩側同時加 後在同時除以
考慮不等式兩側同時做下取整函數,不等式左側 由於 為整數,故 帶入下取整函數後會得到 則該不等式關係為變成
以符號表式可以寫成
其中
具體的來說,該等不等式之間是邏輯等價的
僅需要證明 和 邏輯等價,以及 和 邏輯等價。只要確認該兩命題成立,便可以由 推至其他不等式關係。
由於我們已經證明完該不等式 為 的充分條件,故只要證明該不等式同時也為 的必要條件即可。
由於 為整數, ,故 可以寫成
證明 和 邏輯等價方式類似,在此不贅述。所以該等不等式之間彼此間邏輯等價。
該等邏輯等價關係告訴我們:該初始值選擇條件可以從 的位元長度計算
此定義可以等價表示:如果 或 則 為一鄰近平方根。
假如有一 的鄰近平方根 ,則 時, 的整數根號為 ;則 時, 的整數根號為 。總得來說,一個找出鄰近平方根的演算法,提供了一個計算整數平方根的方式。
此演算法的想法:
Our algorithm is based on the idea of “lifting” a near square root of a quotient of to a near square root of .
此想法藉由帶餘數除法(division with remainder),將被開根號數 寫成一個除數與商數的乘積,加上一個餘數,該除數以某個正整數平方表示且除數小於或等於該被開根號數 。同時,當 為完全平方時,除數會於 ;否則,藉由除數與商數的乘積近似該被開根號數 ,除法結果近似於該商數。對此被除數與除數開根號後相除,商數開根號會近似該除式。再次從帶餘數除法得知,商數開根號乘上除數會近似 開根號。
假設 為一正整數,滿足 和 為 一鄰近平方根。
換言之,對 開平方根會得到 ,也就是說可以將 近似表示為
則可以將 的關係寫成
也就是說,對於求解 可以得到一近似平方根 ,則照 Heron's method ,寫出一改進的近似方法
原文中給出一個限制在於 ,假設 不要太大的情況下,使用該近似方法可以求得 的鄰近平方根。具體的作法是令 為一偶數,則根據假設 為 的鄰近平方根,其中 為 的近似值。則替換掉 後近似方法為
需修正 example 11 typo
floor(n/k^2) –> floor(n/4k^2)
使用 Python script 先生成出 至 的平方,儲存成一 perfect_square32.txt
。
再使用 C 讀取該 perfect_square32.txt
執行十分逼近法開根號。