---
# System prepended metadata

title: 牛頓法

---

# 牛頓法
contributed by `TonyLinX`

## 定義
牛頓法是用來求根的方法，主要的目的是求近似值，所以通常的用法是給你一個函數 $f(x)$，要你求這個函數的根，它會透過不斷地迭代逐漸收斂到一個近似值，這個近似值會非常靠近你的根。
![image](https://hackmd.io/_uploads/Hk-VB4-mxe.png)

:::info
初始值 ($x_0$) 有幾種常見的選法：
*    $x_0$ = a
*    $x_0$ = a/2
:::

## 公式推導
假設給你一個函數 

$$
f(x) = y = x^2 - a
$$

那這個 a 可以是任何數字，例如：2, 3, 5，所以這個函數就是我們常見的要你求 $\sqrt{2}$, $\sqrt{3}$, $\sqrt{5}$ 的近似值。但一開始我們不知道它的根是多少，只能透過牛頓迭代公式推導，牛頓迭代公式的邏輯：
-    先給定一個 $x_n$ (a)，若 $n=0$，就使用常見的初始值設定
-    在 $(x_n, f(x_n))$ 做切線，與 x 軸交於 $x_{n+1}$ (b) 點
-    在 $(x_{n+1}, f(x_{n+1}))$ 做切線，與 x 軸交於 $x_{n+2}$ (d) 點
-    一直重複上述操作，就會得到 $\sqrt{N}$
   
公式推導： 假設 $x_0$ = a , $f(x) = x^{2} - a$

求 $(x_0, f(x_0))$ 的切線
$$
m = f'(x_n) = \frac{f(x_n) - 0}{x_n - x_{n+1}}
=> x_n - x_{n+1} = \frac{f(x_n)}{f'(x_n)}
=> x_{n+1} = x_n - \frac{f(x_n)}{f'(x_n)}
$$

通用的牛頓法迭代公式：
$$
x_{n+1} = x_n - \frac{f(x_n)}{f'(x_n)}
$$

帶入 $f(x) = y = x^2 - a$ 以及 $f'(x) = 2x$ :
$$
x_{n+1} = x_n - \frac{f(x_n)}{f'(x_n)}
=> x_{n+1} = x_n - \frac{x^2 - a}{2x}
=> x_{n+1} = \frac{1}{2}(x_n + \frac{a}{x_n})
$$

開平方根的牛頓法迭代公式：
$$
x_{n+1} = \frac{1}{2}(x_n + \frac{a}{x_n})
$$


```c
#include <stdio.h>
#include <stdint.h>
#include <math.h>

float sqrt_w_fpu_newton(int a) {
    float x = a;  
    float epsilon = 1e-6f;
    
    while (fabsf(x * x - a) > epsilon)
        x = 0.5f * (x + a / x);

    return x;
}

int sqrt_no_fpu_newton(int a) {
    int x = a;  
    while (x * x > a  || (x + 1) * (x + 1) <= a)
        x = (x + a / x) / 2;
    
    return x;
}

float sqrt_fixed_q8_8(int a_q8_8) {
    
    int x = a_q8_8; //初始值
    
    for (int i = 0; i < 5; i++) {
        x = (x + (((int32_t)a_q8_8 <<8) / x)) >> 1;
    }
    
    return x /256.0f;  
}

int main() {
    int a[] = {1, 2, 3, 4, 5, 9};
    int len = (int)(sizeof(a) / sizeof(a[0]));
    
    for (int i = 0; i < len; i++) {
        float fpu_result = sqrt_w_fpu_newton(a[i]);
        int no_fpu_result = sqrt_no_fpu_newton(a[i]);  
        float fixed_result = sqrt_fixed_q8_8(a[i]<<8);
        
        float error = fabsf(fpu_result - (float)no_fpu_result)/fpu_result * 100;
        float error_fixed = fabsf(fpu_result - fixed_result)/fpu_result * 100;
        
        printf("sqrt(%d) ≈ %.6f (FPU), %d (no FPU), error = %12.8f %%, fixed = %.8f, error_fixed = %12.8f %%\n",
               a[i], fpu_result, no_fpu_result, error, fixed_result, error_fixed);
    }
    
    return 0;
}
```

結果：
```
sqrt(1) ≈ 1.000000 (FPU), 1 (no FPU), error =   0.00000000 %, fixed = 1.00000000, error_fixed =   0.00000000 %
sqrt(2) ≈ 1.414214 (FPU), 1 (no FPU), error =  29.28931999 %, fixed = 1.41406250, error_fixed =   0.01068001 %
sqrt(3) ≈ 1.732051 (FPU), 1 (no FPU), error =  42.26497650 %, fixed = 1.73046875, error_fixed =   0.09134522 %
sqrt(4) ≈ 2.000000 (FPU), 2 (no FPU), error =   0.00000000 %, fixed = 2.00000000, error_fixed =   0.00000000 %
sqrt(5) ≈ 2.236068 (FPU), 2 (no FPU), error =  10.55728149 %, fixed = 2.23437500, error_fixed =   0.07571372 %
sqrt(9) ≈ 3.000000 (FPU), 3 (no FPU), error =   0.00000000 %, fixed = 3.00000000, error_fixed =   0.00000000 %
```

## 開 k 次方根牛頓法公式推導

通用的牛頓法迭代公式：
$$
x_{n+1} = x_n - \frac{f(x_n)}{f'(x_n)}
$$

套進我們的 $f(x) = x^k -a$ 得：

1. 函數:
$$
f(x) = x^k - a
$$
2. 導數:
$$
f'(x) = kx^{k-1}
$$

3. 帶入牛頓公式：
$$
x_{n+1} = x_n - \frac{x^{k}_n - a}{kx^{k-1}}
=> x_{n+1} = \frac{1}{k}((k-1)x_n + \frac{a}{x^{k-1}_n})
$$


開 k 次方根的牛頓法迭代公式：
$$
x_{n+1} = \frac{1}{k}((k-1)x_n + \frac{a}{x^{k-1}_n})
$$

