一個求解平方根的算法題


1. 問題描述

問題是這樣子的,給定一個數a,求解這個數的平方根,要求精度f<0.0001.

好久沒有操刀實現算法代碼了,今天就來寫一個小的,后續算法依舊是研究的重點。比較軟件中,算法是核心是靈魂啊!

 

2. 算法分析

說起來,這個算法題其實不是太麻煩,主要采取的就是不斷試探,逼近真是目標值的思路,最容易想到的就是,不斷的折半逼近,有點類似二分的思想。同時,一個重要的思想:

1. 設目標值平方根為Se

2. 待求解的輸入數據為St

3. |Se*Se - St| < f*f

4. 求出一個Se*Se - St > 0的情況下的Se值,因為平方根分正負兩個值,求出一個即可,這里求正值

 

3. 實現過程

這里,直接上JAVA的實現代碼,注意,這里只實現了平方根大於1的問題場景,至於平方根在0~1之間這種場景,未處理!

package pingFangGeng;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

/**
 * @author shihuc
 * @date  2018年6月19日 上午8:31:51
 * 
 * 本算法近似計算一個大於1的數字的平方根
 */
public class Solution {

    /**
     * @author shihuc
     * @param args
     */
    public static void main(String[] args) {
        Scanner scan = null;
        try {
            scan = new Scanner(new File("./src/pingFangGeng/input.txt"));
            int count = scan.nextInt();
            for(int i = 0; i < count; i++){
                int source = scan.nextInt();
                double fraction = scan.nextDouble();
                double res = binDivFind(0, source, source, fraction*fraction);
                System.out.println("No." + i + ": source=" + source + ", fraction=" + fraction + ", result=" + res);
            }
        } catch (FileNotFoundException e) {            
            e.printStackTrace();
        } finally {
            if (scan != null){
                scan.close();
            }
        }
    }
    
    /**
     * 算法思路:
     * |Se*Se - St| < fraction*fraction
     * 其中Se為目標值,St為給定待求解的數據,fraction為給定的精度
     *  
     * @author shihuc
     * @param src
     * @param fraction
     * @return
     */
    private static double binDivFind(double st, double ed, double tar, double fraction) {
        double Se = (double) ((st + ed) / 2);
        double Se2 =  Se * Se;
        double Set = Se2 - tar;
        double res = Set * Set;
        double rval = Se;
        if(res >= fraction){
            if(Set > 0){
                rval = binDivFind(st, Se, tar, fraction);
            }else{
                rval = binDivFind(Se, ed, tar, fraction);
            }
        }
        return rval;
    }

}

 

代碼中的測試用例,請看下面的文件:

6
9876543 0.0001
33 0.001
999 0.0001
777 0.00001
8888 0.001
1000000 0.0001

 

測試運行的結果,如下:

No.0: source=9876543, fraction=1.0E-4, result=3142.696771881704
No.1: source=33, fraction=0.001, result=5.744636535644531
No.2: source=999, fraction=1.0E-4, result=31.606961332261562
No.3: source=777, fraction=1.0E-5, result=27.874719826038927
No.4: source=8888, fraction=0.001, result=94.27618705481291
No.5: source=1000000, fraction=1.0E-4, result=999.9999999763531

 

分析與總結:

1. 整個算法實現,主要是通過遞歸的思路,不斷折半試探,逐步逼近目標值。

2. 待求解數據,在算法實現過程中,若用float的數據類型,則會出現較大的誤差,采用double的話,精度可信。

3. 0~1之間平方根的場景,只需將當前的算法做一個反向思路實現,因為0~1之間的數,平方后的數比元素數還要小。

 

2018-06-21:

PS: 針對上述分析與總結中的第3點,這里補充上算法的修正結果,即將待求解值在0-1之間的數字的平方根求解算法也支持的問題,在這里做一下兼容處理,即上述的算法做如下的調整,即可實現【0,N】其中N大於1的任何數的平方根的求解。主要是將上述java代碼中的main函數做了一點點調整,區分source值是大於1還是在0-1之間的。

為何要這么分段呢?

1. 小於1的數a求平方后比原始數a小,例如0.8的平方是0.64

2. 逼近邏輯中,平方根x一定總是在原始待求解數a的右邊,即x > a, 且又滿足 x < 1

下面看更新后的main區域的代碼:

public static void main(String[] args) {        
        Scanner scan = null;
        try {
            scan = new Scanner(new File("./src/pingFangGeng/inputFree.txt"));
            int count = scan.nextInt();
            for(int i = 0; i < count; i++){
                double source = scan.nextDouble(); double fraction = scan.nextDouble();
                double res = 0; if(source <= 1) { res = binDivFind(source, 1, source, fraction*fraction); }else{ res = binDivFind(0, source, source, fraction*fraction); }
                System.out.println("No." + i + ": source=" + source + ", fraction=" + fraction + ", result=" + res);
            }
        } catch (FileNotFoundException e) {            
            e.printStackTrace();
        } finally {
            if (scan != null){
                scan.close();
            }
        }
    }

大家感興趣的,可以試試,效果還是不錯的。 當然了,本算法中,用的是二分法,性能相對牛頓法差點,下次專門寫一個用牛頓法實現求平方根的算法。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM