java 求解 一元N次方程

这里每次都解出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;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值