算法简介
算法实现
这里我使用函数指针方式来保证接口使用的便利性,虽然可变长参数还没做处理,这一点有空再完善吧。。。
#include <iostream> typedef double(*equaton)(double x, ...); double square(double x) { return x * x; } double func1(double x, ...) { return cos(x) - square(x)*x; } double func2(double x, ...) { return -sin(x) - 3 * square(x); } double func3(double x, ...) { return -cos(x) - 6 * x; } bool isbump(double a, double b) { for (double i = a; i <= b; i += 1e-2) if (func3(i) < 0.) return false; return true; } double newton_method(equaton func, equaton der_func, double a, double b, bool isc, double λ) // isc: 凹 is true; 凸 is false { if (func(a)*func(b) >= 0.) return 0.0; double x, pre_x; if (func(a) > 0 && isc) x = a; else x = b; do { pre_x = x; x -= func(x) / der_func(x); } while (fabs(func(x)) > λ && fabs(pre_x - x) > λ); return x; } int main() { std::cout << "方程的根为:" << newton_method(func1, func2, 0., 1., isbump(0., 1.), 1e-6) << std::endl; return 0; }
output:
方程的根为:0.865474
如图:
关于牛顿法开方(或任意次方)算法,强烈推荐看看这个回答:https://www.guokr.com/question/461510/
简单提一下需要注意得地方:求 $ sqrt{2} $ 时,考虑有 $ x^2 - a = 0 $,即 $ x = pm sqrt{a} $,于是 $ x $ 就是迭代公式中的 $ x_i $,$ a = 2 $ 就是我们要求的。回答中给出了求任意次方时的公式,直接套用即可。
开方时的代码实现:
#include <iostream> int main() { double x, pre_x; x = 1; pre_x = 9999; while (fabs(pre_x - x) > 0) { pre_x = x; x = (x + (2 / x)) / 2; } std::cout << x << std::endl; return 0; }
牛顿法求 $ x^n $ 算法实现(代码有误):
#include <iostream> double newton_method_power(double a, double b) { if (a == 0.) return 0.; else if (a == 1. || b == 0.) return 1.; else if (b == 1.) return a; else if (fabs(b) > 1.) return 0.; // 当指数|b| > 1 且为整数时,使用快速幂算法;为浮点数时,可以使用微分近似法或泰勒公式求解 else { b = 1./b; double x, pre_x; x = 1.; pre_x = 9999.; while (fabs(pre_x - x) > 0.) { pre_x = x; x = ((b - 1.)*x / b) + (a / (b*newton_method_power(x, b - 1.))); } return x; } } int main() { std::cout << newton_method_power(8., 1.) << std::endl; return 0; }
微分近似公式:
$$ f(x + Delta x) approx f(x) + f'(x) cdot Delta x $$
例如:
1.求 $ 2^{1.4} $
由 $ f(x) = a^x $,$ f(x + Delta x) = a^{x + Delta x} approx a^{x} + a^{x} ln(a) cdot Delta x $
有 $ 2^{1 + 0.4} approx 2 + 2ln(2) cdot 0.4 approx 2.554518... $
而原表达式 $ 2^{1.4} = 2.63902... $
2.求 $ 2^{9} $
利用公式有 $ 2^{10 - 1} = (2^{10})^{(1 - frac{1}{10})} approx 2^{10} + 2^{10} ln(2^{10}) cdot 0.1 approx 709.783... $
$ 2^9 = 512 $
虽然算得很勉强 2333。。。