看其他篇章到目录选择。
函数方程求解,其实是函数的零点问题,也就是说函数的曲线与X轴的交点。对于线性方程,我们可以轻易的求解,对于线性方程组,利用前面讲过的的矩阵分解方法也可以求解。那么对于函数表达的很多非线性方程的求解。我们要依赖数值算法。Commons Math包中专门有一个analysis.solver包来解决这个问题。
Analysis.solver包中有一个基本接口类——UnivariateRealSolver,其中定义了一系列接口方法,最重要的方法就是double solve(UnivariateRealFunction f, double min, double max)了。很明显,这个方法的参数是函数的表达式以及求解的区间范围,返回值是函数在区间内的一个零值点。
Solver包中的这些类的组织结构形式与积分中的类似,结构类图如下:
具体的求解算法有很多,solver包中也实现了很多算法,包括对分法、牛顿法等。我们这里以这两个算法的实现为例,看看该如何使用这个包中的接口方法。待求解的函数仍以正弦函数为例。
1/** *//**
2 *
3 */
4package algorithm.math;
5
6import org.apache.commons.math.ConvergenceException;
7import org.apache.commons.math.FunctionEvaluationException;
8import org.apache.commons.math.analysis.UnivariateRealFunction;
9import org.apache.commons.math.analysis.solvers.BisectionSolver;
10import org.apache.commons.math.analysis.solvers.UnivariateRealSolver;
11import org.apache.commons.math.analysis.solvers.UnivariateRealSolverFactory;
12import org.apache.commons.math.analysis.solvers.UnivariateRealSolverFactoryImpl;
13
14/** *//**
15 * @author Jia Yu
16 * @date 2010-11-24
17 */
18public class SolverTest {
19
20 /** *//**
21 * @param args
22 */
23 public static void main(String[] args) {
24 // TODO Auto-generated method stub
25 solver();
26 factorySolver();
27 }
28
29 private static void factorySolver() {
30 // TODO Auto-generated method stub
31 UnivariateRealFunction f = new SinFunction();
32 UnivariateRealSolverFactory factory=new UnivariateRealSolverFactoryImpl();
33 UnivariateRealSolver solver = factory.newNewtonSolver();
34 try {
35 System.out.println("NewtonSolver : sin(x)=0 when x from -1 to 1, x = "+ solver.solve(f, -1, 1));
36 } catch (ConvergenceException e) {
37 // TODO Auto-generated catch block
38 e.printStackTrace();
39 } catch (FunctionEvaluationException e) {
40 // TODO Auto-generated catch block
41 e.printStackTrace();
42 }
43 }
44
45 private static void solver() {
46 // TODO Auto-generated method stub
47 UnivariateRealFunction f = new SinFunction();
48 UnivariateRealSolver solver = new BisectionSolver();
49 try {
50 System.out.println("BisectionSolver : sin(x)=0 when x from 3 to 4, x = "+ solver.solve(f, 3, 4));
51 } catch (ConvergenceException e) {
52 // TODO Auto-generated catch block
53 e.printStackTrace();
54 } catch (FunctionEvaluationException e) {
55 // TODO Auto-generated catch block
56 e.printStackTrace();
57 }
58 }
59
60}
61
输出结果:
BisectionSolver : sin(x)=0 when x from 3 to 4, x = 3.141592502593994
NewtonSolver : sin(x)=0 when x from -1 to 1, x = 0.0
可以看到,程序都输出了正确的结果。但是,其实还是可以尝试一些复杂的参数来测试算法的,这里就不再多说,不妨看看牛顿法在(-1,4)区间的效果。
不管是哪种方法,其实都是要设定误差限的,默认的是10E-6。
一个小的建议是使用工厂模式的构建方法,因为毕竟这个包内提供了工厂的实现,那为什么不用这种更灵活的方式呢?
写了方程求解的例子,就不要再问什么求平方根的牛顿法或者立方根什么的问题了,不就是x^n-R=0的方程嘛,设定区间[0,+∞]就可以了。原理明白,其他的问题都很好解决,不是吗?
相关资料:
对分法:http://mathworld.wolfram.com/Bisection.html
牛顿法:http://mathworld.wolfram.com/NewtonsMethod.html
Commons math包:http://commons.apache.org/math/index.html