牛頓迭代法 簡單入門
阿新 • • 發佈:2018-12-30
引言
牛頓迭代法是在計算機程式設計領域廣泛用來求解方程的演算法。
此演算法類似於二分列舉(僅僅感覺上很類似= =)
我們每次列舉一個值,代入方程看是否為根,不是的話則將的值變為:
(其中 為 的導數)
其證明過程借用了高數中的基本知識泰勒級數,此處不再贅述。
看起來很簡單對嗎?事實上就是這麼簡單。
應用一
下面比如我們要求出下面方程的一個根:
程式碼如下:
const double eps = 1e-6;
double F(double x){return 5*x*x*x + 4*x*x + 2;}
double F1(double x){return 15*x*x + 8*x;} //F(x)的導函式
int newton(double x){ //x是初始列舉值
while(fabs(F(x)) > eps) x -= F(x)/F1(x); //牛頓迭代
return x;
}
如果想求出一個範圍內的所有的根,則可以在該範圍內列舉初始值x,來逼近根的值。
應用二
對於ACM 競賽,牛頓迭代最重要的應用還是在於其能用於求根號。
你可能會有疑惑,求根號不是有 函式嗎,為什麼要自己寫。
這是因為可能存在下面的兩種情形:
對於該問題我們可以使用牛頓迭代+高精度除法。
我們以開平方根舉例
對於一個已知的數 a,開根號本質上是求一個 使得
也就是求函式
的根
因為
故每次迭代,x的變化值為:
所以再套一個高精度除法或者Java的BigInteger就好啦
//51Nod 1166
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Main{
public static void main(String[] args) throws Exception {
BufferedReader reader = new BufferedReader(new InputStreamReader(
System.in), 1 << 16);
String nStr = reader.readLine();
BigDecimal n = new BigDecimal(nStr);
BigDecimal ans = new BigDecimal(nStr.substring(0, nStr.length() / 2));
BigDecimal tmp = BigDecimal.ONE;
BigDecimal two = new BigDecimal("2");
int length = 2;
while(true){
tmp = ans.add(n.divide(ans, length, RoundingMode.HALF_DOWN)).divide(two, length, RoundingMode.HALF_DOWN);
if(tmp.subtract(ans).abs().compareTo(BigDecimal.ONE) == -1)
break;
ans = tmp;
}
String str = ans.toString();
System.out.println(str.substring(0, str.length() - length - 1));
}
}
(二)底層優化
雖然 已經很方便,但確實是存在比其更強大的手寫演算法,這個黑科技來自遊戲雷神之錘3的原始碼:(細節見末尾推薦文章)
int sqrt(float x) {
if(x == 0) return 0;
float result = x;
float xhalf = 0.5f*result;
int i = *(int*)&result;
i = 0x5f375a86- (i>>1); // what the fuck?
result = *(float*)&i;
result = result*(1.5f-xhalf*result*result); // Newton step, repeating increases accuracy
result = result*(1.5f-xhalf*result*result);
return 1.0f/result;
}