这里每次都解出n个根,可能存在同根
一元N次函数的图像是一条波浪线,解一定在波峰与波谷之间(这里认为无穷大时波峰,无穷小时波谷) ,因为不是在解数学题,我就不写的那么准确了。
这里是使用牛顿迭代公式,因为牛顿迭代公式一次只能求一个根,因此需要设置不同的初始值来求出三个根
大体思路如下:
1.求ax^3+bx^2+cx+d 的两个顶点(平衡点),根只能在平衡点两侧
x1-------top1------x2-----top2------x3
2. 我们规定 a>0 ,不考虑a<0 的情况,(如果小于0 左右同时乘-1)
3.求出拐点,正常情况下两个平衡点之间一定存在一个拐点,用拐点作为初始值计算最快。
public static void main(String[] args) {
//ax^n+bx^n-1+cx^n-2-------------- =0
double a = 100;
double b = -100;
double c = 4;
double d = -100;
double e = 10;
double f = 10;
double g = 10;
List<Double> result = solveEquations(a, b, c, d, e, f, g);
result.forEach(x -> System.out.printf("根x=%f, 带入方程得:%.15f\r\n", x, fx(x, a, b, c, d, e, f, g)));
}
public static List<Double> solveEquations(double... params) {
int m = params.length;
if (m == 3) {
return solveEquations2(params[0], params[1], params[2]);
} else if (m == 2) {
return Lists.newArrayList(-params[1] / params[0]);
}
int n = m - 1;
double[] params1 = new double[n];
for (int i = 0; i < n; i++) {
params1[i] = params[i] * (n - i);
}
//求平衡点,因为解必定在平衡点之间 (一阶导数为0)
List<Double> doubles = solveEquations(params1);
//求拐点, 两个平衡点之间一定有一个拐点,通过拐点求解最快(二阶导数为0)
double[] params2 = new double[n-1];
for (int i = 0; i < n - 1; i++) {
params2[i] = params[i] * (n - i) * (n- i- 1);
}
List<Double> doubles1 = solveEquations(params2);
List<Double> result = new ArrayList<>();
double x = 0;
if (doubles.isEmpty()) {
for (int i = 0; i < n; i++) {
doubles.add(null);
}
}
Double lastTopX = null;
Double lastV = null;
int topIndex = 1;
Double[] resultArray = new Double[n];
//因为a>0 函数最右侧必定是递增的,所以从右往左依次求解
for (int i = doubles.size() - 1; i >= 0; i--) {
Double topX = doubles.get(i);
if (topX == null) {
topX = 100.0;
}
double topY = fx(topX, params);
//如果最右侧顶点的y值大于0,则第一个解不存在
Double v = null;
if (topY <= 0 && topIndex == 1) {
v = f2x(topX + 5, params);
}
boolean b = (topIndex % 2 == 1 && topY < 0) || (topIndex % 2 == 0 && topY > 0);
if (topIndex > 1) {
if (lastTopX.equals(topX)) {
v = lastV;
}
double x1 = 0;
if (b) {
//找到拐点
Double finalLastTopX = lastTopX;
Double finalTopX = topX;
Optional<Double> first = doubles1.stream().filter(xg -> xg <= finalLastTopX && xg >= finalTopX).findFirst();
//正常情况下应该是存在拐点的
if (first.isPresent()) {
x1 = first.get();
} else {
x1 = (lastTopX + topX) / 2;
}
v = f2x(x1, params);
}
}
resultArray[i + 1] =v;
if (topIndex == doubles.size()) {
if (Objects.equals(lastTopX, topX)) {
v = lastV;
}
double x1 = 0;
if (b) {
x1 = (topX - 5 ) / 2;
v = f2x(x1, params);
}
resultArray[0] =v;
}
lastV = v;
topIndex ++;
lastTopX = topX;
}
Double last = null;
for (int i = n -1; i >= 0; i--) {
Double v = resultArray[i];
if (v == null) {
resultArray[i] = last;
continue;
}
last = v;
}
last = null;
for (int i = 0; i < n; i++) {
Double v = resultArray[i];
if (v == null) {
resultArray[i] = last;
continue;
}
last = v;
}
List<Double> list = Arrays.asList(resultArray);
list.sort(Double::compareTo);
return list;
}
public static double fx(double x, double... params) {
double y = 0;
int n = params.length - 1;
for (int i = 0; i < params.length; i++) {
y += params[i] * Math.pow(x, n - i);
}
return y;
}
public static double f1x(double x, double... params) {
double y = 0;
int n = params.length - 1;
for (int i = 0; i < params.length; i++) {
y += params[i] * (n - i) * Math.pow(x, n - i - 1);
}
return y;
}
public static double f2x(double x, double... params) {
double x1 = 0;
do {
x1 = x;
x = x1 - fx(x1, params) / f1x(x1, params);
} while (!(Math.abs(x1 - x) < 0.0000000000001));
return x1;
}
//这里是一元二次方程,可以直接套公式,也可以像我这样写
public static List<Double> solveEquations2(double a, double b, double c) {
// (2 * a * x + b) = 0 为ax^2+bx+c=0 的顶点 x = -b/2a
List<Double> result = new ArrayList<>();
double topX = -b/(2*a);
double x;
double v = b * b - 4 * a * c;
if (v < 0) {
return result;
}
if (v == 0) {
result.add(topX);
return result;
}
double x1 = topX - 5;
double x2 = topX + 5;;
do {
x = x1;
x1 = x - (a * x * x + b * x + c) / (2 * a * x + b);
} while (!(Math.abs(x1 - x) < 0.0000000000001));
do {
x = x2;
x2 = x - (a * x * x + b * x + c) / (2 * a * x + b);
} while (!(Math.abs(x2 - x) < 0.0000000000001));
result.add(x1);
result.add(x2);
return result;
}