# 牛頓法 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}) $$