1.1 二分法
利用二分進行開平方的思想很簡單:假定中值為最終解。假定下限為0,上限為x,然後求中值;然後比較中值的平方和x的大小,並根據大小修改下限或者上限;重新計算中值,開始新的迴圈,直到前後兩次中值的距離小於給定的精度為止。需要注意的一點是,如果x小於1,我們需要將上限置為1。**如下:
float sqrtbybisection(float n)
while(fabsf(mid-last) > eps); //eps為精度,一般為0.000001
return mid;
}
這裡有一點需要特別注意:在精度判別時不能利用上下限而要利用前後兩次mid值,否則可能會陷入死迴圈!這是因為由於精度問題,在迴圈過程中可能會產生mid值和up或low中的乙個相同。這種情況下,後面的計算都不會再改變mid值,因而在達不到精度內時就陷入死迴圈。
1.2 牛頓迭代法
將中值替換為切線方程的零根作為最終解,原理可以利用下**釋:
while(fabsf(val-last) > eps); //eps為精度,一般為0.000001
return
val;
}注意到**中初始值的選擇,解釋如下:
ieee浮點標準用的形式來表示乙個數,將該數存入float型別之後變為:
現在需要對這個浮點數進行開方,我們看看各部分都會大致發生什麼變化。指數e肯定會除以2,127保持不變,m需要進行開方。由於指數部分是浮點數的大頭,所以對指數的修改最容易使初始值接近精確值。幸運的是,對指數的開平方我們只需要除以2即可,也即右移一位。但是由於e+127可能是奇數,右移一位會修改指數,我們將先將指數的最低位清零,這就是& 0xff7fffff的目的。然後將該轉換後的整數右移一位,也即將指數除以2,同時尾數也除以2(其實只是尾數的小數部分除以2)。由於右移也會將127除以2,所以我們還需要補償乙個64,這就是最後還需要加乙個(64<<23)的原因。
這裡大家可能會有疑問,最後為什麼加(64<<23)而不是(63<<23),還有能不能不將指數最後一位清零?答案是都可以,但是速度都沒有我上面寫的快。這說明我上面的估計更接近精確值。下面簡單分析一下原因。首先假設e為偶數,不妨設e=2n,開方之後e則應該變為n,127保持不變,我們看看上述**會變為啥。e+127是奇數,會清零,這等價於e+126,右移一位變為n+63,加上補償的64,指數為n+127,正是所需!再假設e為奇數,不妨設e=2n+1,開方之後e應該變為n+1(不精確),127保持不變,我們看看上述**會變為啥。e+127是偶數等於2n+128,右移一位變為n+64,加上補償的64,指數為n+1+127,也是所需!這確實說明上述的估計比其他方法更精確一些,因而速度也更快一些。
1.3 卡馬克演算法—快速平方根倒數演算法
此法實質上是進行了1~2次的牛頓迭代法求開方倒數,求平方根的倒數,實際就是求方程1/(x^2)-a=0的解。將該方程按牛頓迭代法的公式展開為:x[
n+1]
=1/2*x [ n ] *(3-a*x[n]*x[n])$。這個方法的乙個關鍵地方還在於起始值的選取,演算法選取乙個「magic number」,使演算法的初始解非常接近精確解!
float sqrtbycarmack( float number )
2.1 手工開平方演算法法先以10進製為例,解釋手動開平方演算法:
首先將數字每兩位分成一段。如:745836942。就分成:
7|45|83|69|42,即從個位開始分。共分成五段,第一段是7。
對第一段的數字7開方取整,可得a=2。此時,要在2後面接乙個數字b,並在7後面加上下一段的數45,使產生的兩位數ab的平方不大於745。
我們知道,數ab實際值表示為10a+b,其平方是
100a2+
20ab+
b2。我們可以暫時忽略b2
,而產生乙個「試商」b。即b=
(745
−100a2
)/20a
=(745−
100∗2∗
2)/(
20∗2)
=8.625≈8
(向下取整)。但是,我們發現282
=784
>
745 ,於是這個試商需要減少為7。然而,當a=0時,上述求試商的方法不在適用,但我們可以直接取下一段的兩位數開方。如√45≈6。求出試商後,用
745−ab
2 得到新的「第一段」的數。
具體過程:
取出第一段的數mn,開方得到a,然後接上第二段的數pq,用mn
pq−100a2
得到「餘數」x,x/
20a得到試商b,然後調整b(當20∗
a∗b+
b∗b>
x 時b需要減少),調整後,將x−
20a∗b
−b∗b
作為新的餘數x』,ab就是第二輪開平方的結果。由於前面已經將
100a
2 減掉,所以後面每次都不用再減去
100a
2 。重複步驟,直到開方完畢,或達到要求的精度為止。最後得到的a就是平方根。
如求745836942
的平方根: 7|
45|83|
69|42 ①a
=sqr
t(7)
≈2,b
=(745−
400)/40
≈8,28
2=784>
745==
>b=
7==>272
=729
<
745,
745−
729=
16 ②a
=27,b
=1683
/540≈3
,27∗20
∗3+3
∗3=1629
<
1683
,1683
−1629=54
③a=273,b=
5469
/5460≈1
,273∗20
∗1+1
∗1=5461
<
5469
==>b=
1,5469
−5461=8
當運用到二進位制時,數ab實際值表示為a<<1+b,其平方是a2
<<2+
ab<<2+
b2。我們可以暫時忽略b2
,而產生乙個「試商」b,依此類推下去。以下**為求乙個32位數的平方根,演算法將原始值兩兩分組進行計算,注意:根應至多為16位且每次計算的b值只能是0或1:
unsigned
int sqrt_16_1(unsigned
int m)
} return n;
}
將原始值分類大小由2變成4,那麼每次要計算2位二進位制數值,可能情況有00,01,10,11,00情況時由於不更新結果,直接移動兩位即可,所以可不進行處理,內部迴圈因此為1~3!
也可把分類大小變成8,16…只要是2的倍數均可,演算法流程類似!
具體**如下:
unsigned
int sqrt_16_2(unsigned
int m)
m <<= 4;
for (j=3;j>0;j--)}}
return n;
}//32->16
計蒜客 X的平方根
設計函式int sqrt int x 計算 的平方根。輸入乙個整數 輸出它的平方根。直到碰到檔案結束符 eof 為止。對於每組輸入,輸出一行乙個整數,表示輸入整數的平方根。樣例輸入 123 4567 89樣例輸出 111 2222 23python 一 用try except try while t...
Python 計蒜客 X的平方根
設計函式int sqrt int x 計算 x 的平方根。輸入格式 輸入乙個 整數 xx,輸出它的平方根。直到碰到檔案結束符 eof 為止。輸出格式 對於每組輸入,輸出一行乙個整數,表示輸入整數的平方根。樣例輸入 1 2 3 4 5 6 7 8 9 樣例輸出 1 1 1 2 2 2 2 2 一 用t...
LintCode x的平方根
x的平方根 實現 int sqrt int n 函式,計算並返回 n 的平方根。樣例 sqrt 3 1 sqrt 4 2 sqrt 5 2 sqrt 10 3 挑戰 o log x solution 如果使用蠻力法來求解的話,肯定是會超時的。因此,我們需要使用牛頓迭代法來求解這問題 牛頓迭代法 對於...