关于百度百科的解释:传送门
牛顿迭代法是在计算机编程领域广泛用来求解方程的算法。
我们每次枚举一个值X0,代入方程看是否为根,不是的话则将X0的值变为:
X0=X0−F(X0)/F′(X0)
(其中F′(x)为F(x) 的导数)
比如要求出下面方程的一个根:
F(x)=5x^3+4x^2+2
const double eps=1e-6;
double F(double x)
{
return 5*x*x*x+4*x*x+2;
}
//F(x)的导函数
double F1(double x)
{
return 15*x*x+8*x;
}
int newton(double x)
{
while(fabs(F(x))>eps){
x-=F(x)/F1(x);
}
return x;
}
如果想求出一个范围内的所有的跟,则可以在该范围内枚举初始值x,来逼近根的值。
对于ACM 竞赛,牛顿迭代最重要的应用还是在于其能用于求根号。
高精度开根号:
对于该问题我们可以使用牛顿迭代+高精度除法,对于一个已知的数a,开根号本质上是求一个X0使得 X0^2=a,也就是求函数F(x)=x^2−a 的根 ,因为F′(x)=2x,故每次迭代,x的变化值为: x=x−(x^2−a)/(2*x)=(x+a/x)/2
所以再套一个高精度除法或者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));
}
}
二)底层优化
虽然sqrt()sqrt() 已经很方便,但确实是存在比其更强大的手写算法,这个黑科技来自游戏雷神之锤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;
}