CICC科普欄目|〖數學算法〗開平方的七種算法

2021-01-18 中國指揮與控制學會


sqrt()函數,是絕大部分語言支持的常用函數,它實現的是開方運算;開方運算最早是在我國魏晉時數學家劉徽所著的《九章算術》被提及。今天寫了幾個函數加上國外大神的幾個神級程序帶大家領略sqrt的神奇之處。

1、古人算法(暴力法)

原理:從0開始0.00001,000002...一個一個試,直到找到x的平方根,代碼如下:

public class APIsqrt {

    static double baoliSqrt(double x) {

        final double _JINGDU = 1e-6;
        double i;
        for (i = 0; Math.abs(x - i * i) > _JINGDU; i += _JINGDU)
            ;
        return i;
    }

    public static void main(String[] args) {
        double x = 3;
        double root = baoliSqrt(x);
        System.out.println(root);
    }

測試結果:

1、7320509999476947

2、牛頓迭代法

計算機科班出身的童鞋可能首先會想到的是《數值分析》中的牛頓迭代法求平方根。原理是:隨意選一個數比如說8,要求根號3,我們可以這麼算:

(8 + 3/8) = 4.1875

(4.1875 + 3/4.1875) = 2.4519

(2.4519 + 3/2.4519) = 1.837

(1.837 + 3/1.837) = 1.735

做了4步基本算出了近似值了,這種迭代的方式就是傳說中的牛頓迭代法了,代碼如下:

public class APIsqrt {

    static double newtonSqrt(double x) {

        if (x < 0) {
            System.out.println("負數沒事開什麼方");
            return -1;
        }
        if (x == 0)
            return 0;

        double _avg = x;
        double last_avg = Double.MAX_VALUE;
        final double _JINGDU = 1e-6;

        while (Math.abs(_avg - last_avg) > _JINGDU) {
            last_avg = _avg;
            _avg = (_avg + x / _avg) / 2;
        }
        return _avg;
    }

    public static void main(String[] args) {
        double x = 3;
        double root = newtonSqrt(x);
        System.out.println(root);
    }
}

測試結果:

17320508075688772

3、暴力-牛頓綜合法

原理:還是以根號3為例,先用暴力法講根號3逼近到1.7,然後再利用上述的牛頓迭代法。雖然沒有用牛頓迭代好,但是也為我們提供一種思路。代碼如下:

public class APIsqrt {

    static double baoliAndNewTonSqrt(double x) {

        if (x < 0) {
            System.out.println("負數沒事開什麼方");
            return -1;
        }
        if (x == 0)
            return 0;

        double i = 0;
        double _avg;
        double last_avg = Double.MAX_VALUE;
        for (i = 0; i*i < x; i += 0.1);
        _avg = i;

        final double _JINGDU = 1e-6;

        while (Math.abs(_avg - last_avg) > _JINGDU) {
            last_avg = _avg;
            _avg = (_avg + x / _avg) / 2;
        }
        return _avg;
    }

    public static void main(String[] args) {
        double x = 3;
        double root = baoliAndNewTonSqrt(x);
        System.out.println(root);
    }
}

測試結果:

1、7320508075689423

4、二分開方法

原理:還是以3舉例:

(0+3)/2 = 1.5, 1.5^2 = 2.25, 2.25 < 3;

(1.5+3)/2 = 2.25, 2.25^2 = 5.0625, 5.0625 > 3;

(1.5+2.25)/2 = 1.875, 1.875^2 = 3.515625; 3.515625>3;

直到前後兩次平均值只差小於自定義精度為止,代碼如下:

public class APIsqrt {

    static double erfenSqrt(double x) {

        if (x < 0) {
            System.out.println("負數沒事開什麼方");
            return -1;
        }
        if (x == 0)
            return 0;

        final double _JINGDU = 1e-6;
        double _low = 0;
        double _high = x;
        double _mid = Double.MAX_VALUE;
        double last_mid = Double.MIN_VALUE;

        while (Math.abs(_mid - last_mid) > _JINGDU) {

            last_mid = _mid;
            _mid = (_low + _high) / 2;
            if (_mid * _mid > x)
                _high = _mid;
            if (_mid * _mid < x)
                _low = _mid;

        }
        return _mid;

    }

    public static void main(String[] args) {
        double x = 3;
        double root = erfenSqrt(x);
        System.out.println(root);
    }
}

測試結果:

1、732051134109497

5、計算 (int)(sqrt(x))算法

PS:此算法非博主所寫

原理:空間換時間,細節請大家自行探究,代碼如下:

public class APIsqrt2 {
    final static int[] table = { 0, 16, 22, 27, 32, 35, 39, 42, 45, 48, 50, 53,
            55, 57, 59, 61, 64, 65, 67, 69, 71, 73, 75, 76, 78, 80, 81, 83, 84,
            86, 87, 89, 90, 91, 93, 94, 96, 97, 98, 99, 101, 102, 103, 104,
            106, 107, 108, 109, 110, 112, 113, 114, 115, 116, 117, 118, 119,
            120, 121, 122, 123, 124, 125, 126, 128, 128, 129, 130, 131, 132,
            133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 144,
            145, 146, 147, 148, 149, 150, 150, 151, 152, 153, 154, 155, 155,
            156, 157, 158, 159, 160, 160, 161, 162, 163, 163, 164, 165, 166,
            167, 167, 168, 169, 170, 170, 171, 172, 173, 173, 174, 175, 176,
            176, 177, 178, 178, 179, 180, 181, 181, 182, 183, 183, 184, 185,
            185, 186, 187, 187, 188, 189, 189, 190, 191, 192, 192, 193, 193,
            194, 195, 195, 196, 197, 197, 198, 199, 199, 200, 201, 201, 202,
            203, 203, 204, 204, 205, 206, 206, 207, 208, 208, 209, 209, 210,
            211, 211, 212, 212, 213, 214, 214, 215, 215, 216, 217, 217, 218,
            218, 219, 219, 220, 221, 221, 222, 222, 223, 224, 224, 225, 225,
            226, 226, 227, 227, 228, 229, 229, 230, 230, 231, 231, 232, 232,
            233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239, 240,
            240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246,
            247, 247, 248, 248, 249, 249, 250, 250, 251, 251, 252, 252, 253,
            253, 254, 254, 255 };

    /**
     * A faster replacement for (int)(java.lang.Math.sqrt(x)). Completely
     * accurate for x < 2147483648 (i.e. 2^31)...
     */
    static int sqrt(int x) {
        int xn;

        if (x >= 0x10000) {
            if (x >= 0x1000000) {
                if (x >= 0x10000000) {
                    if (x >= 0x40000000) {
                        xn = table[x >> 24] << 8;
                    } else {
                        xn = table[x >> 22] << 7;
                    }
                } else {
                    if (x >= 0x4000000) {
                        xn = table[x >> 20] << 6;
                    } else {
                        xn = table[x >> 18] << 5;
                    }
                }

                xn = (xn + 1 + (x / xn)) >> 1;
                xn = (xn + 1 + (x / xn)) >> 1;
                return ((xn * xn) > x) ? --xn : xn;
            } else {
                if (x >= 0x100000) {
                    if (x >= 0x400000) {
                        xn = table[x >> 16] << 4;
                    } else {
                        xn = table[x >> 14] << 3;
                    }
                } else {
                    if (x >= 0x40000) {
                        xn = table[x >> 12] << 2;
                    } else {
                        xn = table[x >> 10] << 1;
                    }
                }

                xn = (xn + 1 + (x / xn)) >> 1;

                return ((xn * xn) > x) ? --xn : xn;
            }
        } else {
            if (x >= 0x100) {
                if (x >= 0x1000) {
                    if (x >= 0x4000) {
                        xn = (table[x >> 8]) + 1;
                    } else {
                        xn = (table[x >> 6] >> 1) + 1;
                    }
                } else {
                    if (x >= 0x400) {
                        xn = (table[x >> 4] >> 2) + 1;
                    } else {
                        xn = (table[x >> 2] >> 3) + 1;
                    }
                }

                return ((xn * xn) > x) ? --xn : xn;
            } else {
                if (x >= 0) {
                    return table[x] >> 4;
                }
            }
        }

        return -1;
    }
    public static void main(String[] args){
        System.out.println(sqrt(65));

    }
}

測試結果:8

6、最快的sqrt算法

PS:此算法非博主所寫

這個算法很有名,大家可能也見過,作者是開發遊戲的,圖形算法中經常用到sqrt,作者才寫了一個神級算法,和他那神秘的0x5f3759df,代碼如下

#include <math.h>
float InvSqrt(float x){
 float xhalf = 0.5f*x;
 int i = *(int*)&x; // get bits for floating VALUE
 i = 0x5f375a86- (i>>1); // gives initial guess y0
 x = *(float*)&i; // convert bits BACK to float
 x = x*(1.5f-xhalf*x*x); // Newton step, repeating increases accuracy
 return x;
}

int main(){
  printf("%lf",1/InvSqrt(3));

   return 0;
}

測試結果:

感興趣的朋友可以參考http://wenku.baidu.com/view/a0174fa20029bd64783e2cc0.html  是作者解釋這個算法的14頁論文《Fast Inverse Square Root》

7、一個與算法6相似的算法

PS:此算法非博主所寫

代碼如下:

#include <math.h>
float SquareRootFloat(float number) {
    long i;
    float x, y;
    const float f = 1.5F;

    x = number * 0.5F;
    y  = number;
    i  = * ( long * ) &y;
    i  = 0x5f3759df - ( i >> 1 );
    y  = * ( float * ) &i;
    y  = y * ( f - ( x * y * y ) );
    y  = y * ( f - ( x * y * y ) );
    return number * y;
}

int main(){
  printf("%f",SquareRootFloat(3));

   return 0;
}

測試結果:

以上文章觀點僅代表文章作者,僅供參考,以拋磚引玉!


如何加入學會

註冊學會會員:

個人會員:

關注學會微信:中國指揮與控制學會(c2_china),回復「個人會員」獲取入會申請表,按要求填寫申請表即可,如有問題,可在公眾號內進行留言。通過學會審核後方可在線進行支付寶繳納會費。

單位會員:

關注學會微信:中國指揮與控制學會(c2_china),回復「單位會員」獲取入會申請表,按要求填寫申請表即可,如有問題,可在公眾號內進行留言。通過學會審核後方可繳納會費。


長按下方學會二維碼,關注學會微信



相關焦點

  • 〖數學算法〗求圓周率的幾種算法
    下面就介紹幾種求圓周率的方法: 這是粗略的求圓周率一種常用算法在(0,0)和(1,1)範圍內隨機投test_sum個點,如果落到圓內,hit_sum數量加1,最後用hit_sum/test_sum算出落在圓內的概率,由得圓周率 PI=hit_sum / test_sum * 4public class PI {
  • CICC科普欄目|計算機起源的數學思想
    而第二個問題即判定問題,在哥德爾的工作發表之後,人們很難想像存在這樣的判定算法,於是阿蘭圖靈開始思考如果證明這樣的算法是不存在的。圖靈採取了這樣的一條道路,他首先分析了人的計算過程。通過丟掉非本質的細節,將這些計算活動局限在少數幾種極為簡單的基本操作上。然後圖靈說明人可以被一個能夠執行這些基本操作的機器所替代。
  • 武漢特別EmT.DJ團隊-〖HotMix〗-Dj_Xmao
    全網慢搖原創第一店  http://dj193.taobao.com微信 whdj193EmT.DJ團體相關專輯22張:【經典老歌慢搖】之大眼貓愛吃魚特別EmT.Dj團體-DJ華聰〖特別.EmT.音樂團體〗dj linker 金秋中文慢搖〖特別EmT.DJ團隊〗-RNB串燒 DJ小飛〖特別EmT.Dj團體〗-DJ_ReNo國慶激情小串燒〖特別EmT.Dj團體〗DJ小凡情歌慢搖II〖特別EmT.Dj團體〗Electro House派隊-DJ小凡ManGo華氏BO-RA87清歌欣賞專集-〖特別EmT.Dj團體〗-DJ.H,Xu特別EmT.Dj
  • 厲害了,拉馬努金機:用算法發現新數學!
    除了那些實實在在的數學定理之外,他的思想也給許多後世的科學家帶去了啟示。在拉馬努金提出的定理中,經常涉及到連分數的概念,它會將一個數表示成為無限的嵌套分數和。以色列理工學院的數學家Gal Raayoni和他的同事受到拉馬努金的啟發,利用這種思路發明了一種新穎、系統的方法,並將它取名為拉馬努金機。這是一種電腦程式,它可以利用算法推導出基本常數的新的數學公式,並揭示其基本結構。
  • HLBPR:一種混合的基於局部相似度的BPR算法
    在這些模型之中,貝葉斯個性化排序算法是第一個引入了對排序進行優化的著名算法。然而這個算法通常存在著數據稀疏性的問題。在這篇文章中,我們介紹一種混合的基於局部相似度的貝葉斯個性化排序(簡稱HLBPR)算法來解決數據稀疏性的問題。我們的主要思路是通過用戶消費記錄的局部相似性來發掘用戶感興趣的潛在商品,然後使用這些擴充的數據來優化物品排序。最後,我們通過在兩個實際數據集上的實驗來證明我們模型的有效性。
  • 量 共振搜索算法有望成為最快算法
    原文已發表在CPL Express Letters欄目 Received 24 April 2020; online 27 April 2020
  • 〖提示〗北京時間21:30將公布美國1月PPI月率
    投網2月14日訊, 〖提示〗北京時間21:30將公布美國1月PPI月率。 重要提示文章部分內容及圖片來源於網絡,我們尊重作者版權,若有疑問可與我們聯繫。
  • 小樂數學科普:新量子算法終於破解非線性方程——譯自量子雜誌
    抽象在科學和數學中引導著有前途的想法。量子計算機利用量子現象比傳統計算機更有效地執行某些計算。由於具有這些功能,與傳統機器相比,它們可以指數方式快速地解決複雜的線性微分方程式。長期以來,研究人員一直希望他們可以通過巧妙的量子算法來解決非線性問題。新方法將非線性偽裝成作為更易處理的線性近似集,儘管它們的精確方法差異很大。
  • 〖提示〗北京時間21:30將公布:美國和加拿大1月貿易帳
    投網3月7日訊, 〖提示〗北京時間21:30將公布: 美國和 加拿大1月貿易帳。 重要提示文章部分內容及圖片來源於網絡,我們尊重作者版權,若有疑問可與我們聯繫。
  • 谷歌AutoML新進展,進化算法加持,僅用數學運算自動找出ML算法
    機器之心報導參與:魔王、杜偉、小舟僅使用基礎數學運算就能自動搜索機器學習算法?谷歌 Quoc V. Le 等人提出了 AutoML-Zero 方法。AutoML-Zero 旨在通過從空程序或隨機程序開始,僅使用基礎數學運算,來自動發現能夠解決機器學習任務的電腦程式。其目標是同時搜索 ML 算法的所有屬性,包括模型結構和學習策略,同時將人類偏見最小化。近來,機器學習(ML)取得了顯著的成功,這要歸功於深度神經網絡等 ML 算法。
  • 什麼是遺傳算法?怎樣繪製遺傳算法流程圖
    遺傳算法是一種通過模擬自然進化過程搜索最優解的操作方法,遺傳算法有三個基本算子選擇、交叉和變異。對於遺傳算法我們也可以使用流程圖對其整個過程進行總結歸納,那要怎樣繪製遺傳算法流程圖呢?下面是分享的簡單操作方法,希望可以幫助大家。
  • 從數學到物理學:加密算法簡介
    是不是只有那些數學頭腦很好的人,才能理解那些在信息安全中常常用到的技術(密碼學)?如果你要成為密碼學家,那可能是的,畢竟密碼學家的工作就是構造極難破解的加密算法。但是加密方法在當今世界的用途已經非常普遍了,從保護用戶的信用卡信息、保護遠程用戶的網絡連接,到保護智力產權、防止盜版,密碼學無處不在。
  • 歸納總結:管理類聯考(數學)必考知識點
    原標題:歸納總結:管理類聯考(數學)必考知識點 萬學海文 萬學海文老師根據近幾年的真題,總結了管理類聯考(數學)的必考知識點和大家分享, 其中,【】表示重難點,〖〗表示重點預測。
  • 算法之「算法」:所有機器學習算法都可以表示為神經網絡
    隨後出現了一個又一個新算法,從邏輯回歸到支持向量機。但是眾所周知,神經網絡是算法的算法及機器學習的巔峰。我們可以說,神經網絡是對機器學習的普遍概括,而不是僅僅一次嘗試。直覺上,我們可以通過幾個例子理解,更嚴謹地講,這種說法也可以通過數學方法證明。我們先來定義一下什麼是神經網絡:它是一個體系結構,包括輸入層、隱藏層和輸出層,各層的節點之間互相連接。信息通過線性變換(權重和偏置)和非線性變換(激活函數)從輸入層轉換到輸出層,有一些方法可以更新模型的可訓練參數。
  • 〖#明物質與暗物質〗(大智慧∞小故事)
    ������������������ *〖#明物質與暗物質〗(大智慧∞小故事)***要相信,才能感受到┅┅美國科學家說在宇宙之中有百分之八十屬於暗物質。什麼是暗物質?暗物質就是看不見的東西。❇️❇️ *你們現在看到的「花」是明物質,但是「空氣」、「細菌」,我們看得見嗎?
  • 趙老師物理:你會利用數學技巧比較實際電壓與額定電壓嗎?
    #中考物理提分#目的: (1)熟悉小燈泡問題分析 (2)理解電路安全問題 (3)鞏固串聯分壓原則 (4)學習利用數學技巧進行判斷例題:1、小燈泡---R_L= 〖U_L額〗^2P_L1 =〖(6V)〗^26w=6 Ω---R_L甲
  • 數學老師傾心奉獻:二次根式乘除法中自主學習易錯的三類知識點
    只要你要學習數學,收藏本文也許會對你有用。〖網頁不支持數學公式,所有內容請以圖片為準〗【自學導讀】二次根式的乘除,主要涉及的數學知識是二次根式的乘除運算和二次根式的化簡。本節內容的學習目標是要掌握二次根式的乘除運算法則和化簡二次根式的常用方法。
  • 理論計算機有哪些特別的算法,它們的算法複雜性很高嗎?
    針對這種定義方式,有很多數學上的好處,當然也有很多問題,特別是在一些特定的場合。因此,有時需要改變定義以獲得更加準確的結論,比如平滑分析等。怎樣定義一個算法是快速有效的?它的定義是,在漸近意義下最壞情況算法的運行時間總是不超過問題輸入規模的一個多項式函數。選擇一個多項式函數的基本標準是:當問題的規模增加一倍時,其運行時間也只是常數倍增加。
  • 了解算法的前世今生
    一、中國算法的前世中國古代數學是以創造算法特別是各種解方程的算法為主線。從線性方程組到高次多項式方程,乃至不定方程,中國古代數學家創造了一系列先進的算法(中國數學家稱之為「術」),他們用這些算法去求解相應類型的代數方程,從而解決導致這些方程的各種各樣的科學和實際問題。
  • 歷史圖上的PageRank算法設計與實現
    PageRank算法是用于衡量網頁重要程度的算法,而網絡中不斷有網站新建或刪除,這樣的網絡用歷史圖來表示顯得相當貼切,因此我們考慮在歷史圖上利用CSR(Compressed Sparse Row)結構實現PageRank,使得程序能夠給出在幾個目標時間的各網站的評分,進而能夠提供網站評分的變化情況,給出網站影響力趨勢的預測。