課程筆記

C6 Open Method (開放法)

C6.1 Simple Fixed-Point Iterative (固定迭代法)

  • 步驟:

    1. 將方程式定義為 之形式。
    2. 建立一初值
    3. 透過 求新的根,重複進一步確定 中, 間的誤差。
    4. 評估 的相對百分誤差是否符合停止點目標決定是否疊代執行。
    5. 若為發散情況可行的策略:
      • 換方程式整理方法(只要符合 的形式就可以)
      • 換方法
      • 換初值
  • 收斂:

    • 當 |g’(x)| < 1 會收斂;而 |g’(x)| > 1 時會發散。
    • 當切線(derivative)為正,則誤差疊代情呈單調收斂;若為負 (negative),則誤差疊代呈震盪收斂/發散。

6.2 Newton-Raphson Method (牛頓法)

  • 步驟:

    1. 確定 f(x)
    2. 確定 f’(x)
    3. 帶入 得到迴圈使用之方程組。
    4. 通常 10 次以內會有解,如果太久建議中斷調整。
  • 牛頓法可能遭遇問題:

    1. 重根
    2. 正好接觸斜率接近 0 時可能容易發散導致收斂慢。(當 f’(x)接近 0 時, 可能會變很大) 沒有通用解法,初值的挑選很重要。

6.3 Secant Method (割線法)

原理:

  1. 已知牛頓法,並且由於:
  2. 可得:
  3. 其他步驟與牛頓法相似,但不需要計算導數。

Homework

題目

如上周: 使用固定迭代法、牛頓法與割線法求解此方程式的根。

解題流程

於前次作業 (2024/10/09) 使用 Bisection Method 與 False-Position Method 二種數值方法求解方程式 。先將其移項為方程式 。當 成立時,存在式 的根。

Simple Fixed-Point Iterative Method

為使用 Simple Fixed-Point Iterative Method 求解方程式 ,需先將方程式轉換為 之形式。可得式 ,本次作業將分別嘗試使用二式實行數值方法求解。

Newton-Raphson Method

為使用 Newton-Raphson Method 求解方程式 ,首先如同前次作業將其移項為方程式 。當 成立時,存在式 的根。接著建立使用 Newton-Raphson Method 計算的方程式如式 ,其中需要式 的一階導函數如式

Secant Method

為使用 Secant Method 求解方程式 ,首先如同前次作業將其移項為方程式 。當 成立時,存在式 的根。接著建立使用 Secant Method 計算的方程式如式

Code

針對本作業撰寫 Golang 代碼如下所示。其中,定義 calcApproxError() 函數計算當前與前次疊代結果的相對誤差,用於判斷 是否成立;calcSimpleFixedPointIterationMethod()calcNewtonRaphsonMethod()calcSecantMethod() 分別使用 Simple Fixed-Point Iterative Method、Newton-Raphson Method 與 Secant Method 三種數值方法尋找根。

  • 方法 calcSimpleFixedPointIterationMethod() 輸入項包含欲求根之方程式 f 輸入方程式 中的 、數值方法初值 x_l、停止點誤差 epsilon_s。並於函數中參考 Simple Fixed-Point Iterative Method 計算 x_root_new = f(x_root_old) (相當於 ),反覆疊代直至 並於疊代過程中列印過程資訊。其中,為避免式 存在無法收斂的情況,定義 times_max = 50 限制疊代次數。

  • 方法 calcNewtonRaphsonMethod() 輸入項包含欲求根之方程式 f、欲求根方程式的一階導函數 f_prime、數值方法初值 x_l、停止點誤差 epsilon_s。並於函數中參考 Newton-Raphson Method 計算 x_root_new = x_root_old - f(x_root_old) / f_prime(x_root_old) (相當於 ),反覆疊代直至 並於疊代過程中列印過程資訊。

  • 方法 calcSecantMethod() 輸入項包含欲求根之方程式 f、數值方法初值 x_l 與 x_2 作為初始切線定位點、停止點誤差 epsilon_s。並於函數中參考 Secant Method 計算 x_next := x_root_new - (f(x_root_new)*(x_root_old - x_root_new))/(f(x_root_old)-f(x_root_new)) (相當於 ),反覆疊代直至 並於疊代過程中列印過程資訊。

而 Homework() 函數定義了於本作業中欲求得的方程式 f (相當於式 )、f_prime (相當於式 )、f_SFPI_1 (相當於式 )、f_SFPI_2 (相當於式 ),並分別呼叫 calcSimpleFixedPointIterationMethod()calcNewtonRaphsonMethod()calcSecantMethod() 方法。其中,初值於此定義 x_l = 0.5 (Secant Method 另外定義 x_2 = 0.25),停止點誤差 epsilon_s = 0.005 (相當於 )。最後,透過 main() 函數呼叫 Homework() 函數執行。

package main
 
import (
	"fmt"
	"math"
)
 
func calcApproxError(Current, Previous float64) float64 {
	if Current == 0 {
		return math.Inf(1) // 防止除以零
	}
	return math.Abs((Current - Previous) / Current)
}
 
func calcSimpleFixedPointIterationMethod(f func(float64) float64, x_1, epsilon_s float64) float64 {
	// f is the function to find the root of
	// x_1 is the initial guess
 
	times := 1
	times_max := 50
	x_root_old := x_1
	x_root_new := f(x_root_old)
 
	for calcApproxError(x_root_new, x_root_old) > epsilon_s && times <= times_max {
		error_appr := calcApproxError(x_root_new, x_root_old)
		fmt.Printf("Iteration %2d: x_root = %f, error_appr = %f\n", times, x_root_new, error_appr)
		x_root_old = x_root_new
		x_root_new = f(x_root_old)
		times++
	}
	if times >= times_max {
		fmt.Println("Method failed to converge.")
		return math.NaN()
	} else {
		fmt.Printf("Iteration %2d: x_root = %f, error_appr = %f\n", times, x_root_new, calcApproxError(x_root_new, x_root_old))
	}
	return x_root_new
}
 
func calcNewtonRaphsonMethod(f, f_prime func(float64) float64, x_1, epsilon_s float64) float64 {
	// f is the function to find the root of
	// f_prime is the derivative of f
	// x_1 is the initial guess
 
	times := 1
	x_root_old := x_1
	x_root_new := x_root_old - f(x_root_old)/f_prime(x_root_old)
 
	for calcApproxError(x_root_new, x_root_old) > epsilon_s {
		error_appr := calcApproxError(x_root_new, x_root_old)
		fmt.Printf("Iteration %2d: x_root = %f, error_appr = %f\n", times, x_root_new, error_appr)
		x_root_old = x_root_new
		x_root_new = x_root_old - f(x_root_old)/f_prime(x_root_old)
		times++
	}
	fmt.Printf("Iteration %2d: x_root = %f, error_appr = %f\n", times, x_root_new, calcApproxError(x_root_new, x_root_old))
	return x_root_new
}
 
func calcSecantMethod(f func(float64) float64, x_1, x_2, epsilon_s float64) float64 {
	// f is the function to find the root of
	// x_1 and x_2 are the initial guesses
 
	times := 1
	x_root_old := x_1
	x_root_new := x_2
	x_next := x_root_new - (f(x_root_new)*(x_root_old - x_root_new))/(f(x_root_old)-f(x_root_new))
 
 
	for calcApproxError(x_root_new, x_root_old) > epsilon_s {
		error_appr := calcApproxError(x_root_new, x_root_old)
		fmt.Printf("Iteration %2d: x_root = %f, error_appr = %f\n", times, x_root_new, error_appr)
		x_root_old = x_root_new
		x_root_new = x_next
		x_next = x_root_new - (f(x_root_new)*(x_root_old - x_root_new))/(f(x_root_old)-f(x_root_new))
		times++
	}
	fmt.Printf("Iteration %2d: x_root = %f, error_appr = %f\n", times, x_root_new, calcApproxError(x_next, x_root_new))
	return x_root_new
}
 
func Homework() {
	f := func(x float64) float64 {
		// e^x = 2 - sin(2x)
		// f(x) = 2 - sin(2x) - e^x
		return 2 - math.Sin(2*x) - math.Exp(x)
	}
	f_prime := func(x float64) float64 {
		// e^x = 2 - sin(2x)
		// f(x) = 2 + sin(2x) - e^x
		// f'(x) = -2cos(2x) - e^x
		return -2*math.Cos(2*x) - math.Exp(x)
	}
	f_SFPI_1 := func(x float64) float64 {
		// e^x = 2 - sin(2x)
		// x = ln(2 - sin(2x))
		return math.Log(2 - math.Sin(2*x))
	}
	
	f_SFPI_2 := func(x float64) float64 {
		// e^x = 2 - sin(2x)
		// x = 0.5 * arcsin(2 - e^x)
		return 0.5 * math.Asin(2 - math.Exp(x))
	}
 
	fmt.Println("Simple Fixed Point Iteration Method:")
	fmt.Println("x = ln(2 - sin(2x))")
	fmt.Printf("Root is: %.10f\n", calcSimpleFixedPointIterationMethod(f_SFPI_1, 0.5, 0.005))
	fmt.Println("x = 0.5 * arcsin(2 - e^x)")
	fmt.Printf("Root is: %.10f\n", calcSimpleFixedPointIterationMethod(f_SFPI_2, 0.5, 0.005))
	fmt.Println()
	fmt.Println("Newton-Raphson Method:")
	fmt.Printf("Root is: %.10f\n", calcNewtonRaphsonMethod(f, f_prime, 0.5, 0.005))
	fmt.Println()
	fmt.Println("Secant Method:")
	fmt.Printf("Root is: %.10f\n", calcSecantMethod(f, 0.5, 0.25, 0.005))
}
 
func main() {
	Homework()
	fmt.Scanln()// Wait for the user
}