1. 程式人生 > 其它 >求平方根的兩種實現方式:二分法、牛頓迭代法

求平方根的兩種實現方式:二分法、牛頓迭代法

一、二分法

  思路: 假設要求一個數字 A 的平方根,可以想象一個長為a、寬為b的矩形,這個矩形的面積就是數字A 。 當長=寬時,這個矩形就是正方形。在面積不變的情況下,使矩形變成正方形就需要調整長、寬的值,無非是長變短一點、寬變長一點,通過不停的迭代,直到長=寬時就能求出A的平方根,由於一個數的平方根可能是小數,所以只需要求出近似值即可(符合給定的誤差範圍就行)

 以下是用go語言實現的程式碼:

// 全域性變數,精度
var precision = 0.000001

// Sqrt 計算開平方 , val是要計算平方根的值, mode是模式, 0 為二分法,1位牛頓迭代
func Sqrt(val float64, mode int8) (float64, error) {
	if val < 0 {
		return 0, errors.New("負數沒有平方根")
	}

	switch mode {
	case 0:
		return sqrtBisection(val, 1, (val/2)-1)
	case 1:
		return sqrtNewtonRaphson(val)
	default:
		return 0, errors.New("不識別的模式")
	}
}

// 二分法求平方根
func sqrtBisection(val float64, left float64, right float64) (float64, error) {

	count := 0
	for left <= right {
		// 左,右 邊界值相加在除以2得到結果值
		result := (left + right) / 2
		tmp := result * result
		if tmp-val <= precision && tmp-val >= precision*-1 {
			fmt.Printf("二分法迴圈次數:%v\n", count)
			return result, nil
		} else if tmp > val {
			right = result
		} else {
			left = result
		}
		count += 1
	}

	return -1, errors.New("計算錯誤")

}

 

 

 

二、牛頓迭代法求平方根:

   思想: 該方法的核心思想是通過在曲線上的某點做切線,該切線的根就很接近曲線的根,通過多次迭代即可無限逼近曲線真正的根 (前提是該曲線確實存在根,且給定範圍內可導,平方方程可以寫成: x^2 +c = 0 , 這個方程明顯是可導的,所以可以用牛頓迭代法求平方根)。

   參考: https://www.zhihu.com/question/20690553

go語言實現:

func sqrtNewtonRaphson(val float64) (float64, error) {
	// 平方的表示式: x^2 + val = 0 , val就是要開平方的數
	// 平方的導數: k=2x
	// 第一個曲線上的點 (val/2, f(x) ) ,
	// 求出該點的 切線方程,通用切線方程: y=kx+c => 2x^2 +c => c= y-2x^2
	// 求出該切線方程的根(與x軸的交點)
	// 判斷該根的平方與 val的誤差是否在精度內容, 如果是則返回,否則繼續以該根為x座標,算出y座標,得到下一次迭代的點座標,然後繼續上面的步驟

	// ------------------------------------------------

	// 求得曲線上y座標的方程
	getYval := func(x float64) float64 {
		return x*x - val
	}

	// 求切線方程的常數項c的函式
	getTangentC := func(x float64, y float64) float64 {
		return y - 2*(x*x)
	}

	// 求切線的根的函式
	getTangentRoot := func(k float64, c float64) float64 {
		return -c / k
	}

	// 第一個點的座標
	x := val / 2
	y := getYval(x)

	// 求得切線c的值
	c := getTangentC(x, y)

	// 求得切線的根
	root := getTangentRoot(2*x, c)

	// 根的平方值
	tmp := root * root

	// 迭代的次數
	count := 1

	// 迭代執行
	for {
		if tmp-val <= precision && tmp-val >= precision*-1 {
			break
		}
		x = root
		y = getYval(x)
		c = getTangentC(x, y)
		root = getTangentRoot(2*x, c)
		tmp = root * root
		count += 1
	}
	fmt.Printf("牛頓迭代法迴圈的次數:%v\n", count)
	return root, nil
}

 

 

 

如何通俗易懂地講解牛頓迭代法求開方(數值分析)? - 馬同學的回答 - 知乎 https://www.zhihu.com/question/20690553/answer/146104283如何通俗易懂地講解牛頓迭代法求開方(數值分析)? - 馬同學的回答 - 知乎 https://www.zhihu.com/question/20690553/answer/146104283如何通俗易懂地講解牛頓迭代法求開方(數值分析)? - 馬同學的回答 - 知乎 https://www.zhihu.com/question/20690553/answer/146104283