自学内容网 自学内容网

基于落差法实现自动测流的java方法

   某天下午在上班摸鱼的小邓突然接到新任务,如下参考论文进行java的编码实现

关注到文章中求解系数部分有2种假设如下:1.落差系数使用试错法得出 2.落差系数使用回归曲线进行拟合 

分别实现如下:

猜测1:落差系数实验1000次得出,水函数方程系数由最小二乘法求解得出:

 public static void main(String[] args) {
        //水位数据(Z)和对应的流量数据(Q)  Q = f(Z) * ΔZ^β
        // 水位数据
        double[] waterLevels = {24.95, 23.80, 23.87, 24.90, 24.13};
        // 流量数据
        double[] flowRates = {1200, 489, 506, 1160, 676};
        //落差数据
        double[] deltaZ = {1.28, 0.86, 0.86, 1.38, 0.98};
        // 落差指数 0.2-0.8
        double bestBeta = findOptimalBeta(waterLevels, deltaZ, flowRates, 0.2, 0.8, 1000);
        System.out.println("最佳落差指数 β: " + bestBeta);

        double[] coefficients = fitQuadraticEquation(waterLevels, flowRates);
        double c = coefficients[0];
        double b = coefficients[1];
        double a = coefficients[2];
        System.out.println("拟合的模型系数为:a = " + a + ", b = " + b + ", c = " + c);
        double[] predictedFlowRates = calculateFlow(waterLevels, deltaZ, a, b, c, bestBeta);
        // 输出预测的流量值
        System.out.println("预测的流量值:");
        for (int i = 0; i < predictedFlowRates.length; i++) {
            System.out.println("水位 " + waterLevels[i] + " 米, 落差 " + deltaZ[i] + " 米的预测流量为: " + predictedFlowRates[i] + " 立方米/秒");
        }
        // 计算统计量
        calculateStatistics(predictedFlowRates, flowRates);
    }

    public static double findOptimalBeta(double[] waterLevels, double[] deltaZ, double[] flowRates,
                                         double minBeta, double maxBeta, int numIterations) {
        double bestBeta = minBeta;
        double minError = Double.MAX_VALUE;

        for (int i = 0; i < numIterations; i++) {
            double beta = minBeta + (maxBeta - minBeta) * i / (numIterations - 1); // 均匀采样 β
            double[] coefficients = fitQuadraticEquation(waterLevels, flowRates);
            double a = coefficients[2], b = coefficients[1], c = coefficients[0];
            double[] predictedFlowRates = calculateFlow(waterLevels, deltaZ, a, b, c, beta);

            // 计算总误差
            double totalError = 0.0;
            for (int j = 0; j < flowRates.length; j++) {
                totalError += Math.abs(predictedFlowRates[j] - flowRates[j]);
            }

            // 更新最佳 β
            if (totalError < minError) {
                minError = totalError;
                bestBeta = beta;
            }
        }

        return bestBeta;
    }

    /**
     * 使用最小二乘法拟合二次方程 f(z) = aZ^2 + bZ + c 的系数
     */
    public static double[] fitQuadraticEquation(double[] waterLevels, double[] flowRates) {
        WeightedObservedPoints points = new WeightedObservedPoints();
        for (int i = 0; i < waterLevels.length; i++) {
            points.add(waterLevels[i], flowRates[i]);
        }
        // PolynomialCurveFitter 用于拟合单个自变量且需要较少的样本数
        PolynomialCurveFitter fitter = PolynomialCurveFitter.create(2);
        //系数从低次项到高次
        double[] coefficients = fitter.fit(points.toList());
        return coefficients;
    }

    /**
     * 根据水位和落差计算流量 Q = f(Z) * ΔZ^β
     */
    public static double[] calculateFlow(double[] waterLevels, double[] deltaZ, double a, double b, double c, double beta) {
        int n = waterLevels.length;
        double[] flowRates = new double[n];

        for (int i = 0; i < n; i++) {
            double Z = waterLevels[i];
            double fZ = a * Z * Z + b * Z + c; // 使用拟合模型 f(Z)
            flowRates[i] = fZ * Math.pow(deltaZ[i], beta); // 计算流量 Q
        }

        return flowRates;
    }

    /**
     * 计算系统误差、随机不确定度和其他统计量
     */
    public static void calculateStatistics(double[] predictedFlow, double[] actualFlow) {
        double systemError = Cal_SystematicError(actualFlow, predictedFlow);
        double randomUncertainty = Cal_RandomUncertainty(actualFlow, predictedFlow);

        System.out.println("系统误差: " + systemError + "%");
        System.out.println("随机不确定度: " + randomUncertainty + "%");
    }

    /**
     * 计算系统误差
     */
    public static Double Cal_SystematicError(double[] Data_ACT, double[] Data_SIM) {
        double se = 0.0;
        double sum = 0.0;
        int counter = 0;
        for (int i = 0; i < Data_ACT.length; i++) {
            sum += Math.abs(Data_ACT[i] - Data_SIM[i]) / Data_SIM[i];
            counter++;
        }
        se = sum / counter;
        return se * 100; // 返回百分比
    }

    /**
     * 计算随机不确定度
     */
    public static Double Cal_RandomUncertainty(double[] Data_ACT, double[] Data_SIM) {
        double sumSquared = 0.0;
        int counter = Data_ACT.length;

        for (int i = 0; i < counter; i++) {
            sumSquared += Math.pow((Data_ACT[i] - Data_SIM[i]) / Data_SIM[i], 2);
        }
        return 2 * Math.sqrt(sumSquared / (counter - 2)) * 100; // 返回百分比
    }

结果如下:

猜测2实现:同时 拟合f(z) = az^2+bz+c 和落差系数B 且限制范围0.2-0.8之间 代码如下

public static void main(String[] args) {
        double[] waterLevels = {24.95, 23.80, 23.87, 24.90, 24.13};
        double[] flowRates = {1200, 489, 506, 1160, 676};
        double[] deltaZ = {1.28, 0.86, 0.86, 1.38, 0.98};
        //默认猜测的系数值
        double[] initialGuess = {60.0, -2000.0, 25000.0, 0.35};

        double[] fittedParameters = fitParameters(waterLevels, flowRates, deltaZ, initialGuess);
        double a = fittedParameters[0];
        double b = fittedParameters[1];
        double c = fittedParameters[2];
        double beta = fittedParameters[3];
        System.out.printf("拟合的参数:a = %.3f, b = %.3f, c = %.3f, β = %.3f%n", a, b, c, beta);
        double[] predictedFlowRates = calculateFlow(waterLevels, deltaZ, a, b, c, beta);

        // 输出预测的流量值
        System.out.println("预测的流量值:");
        for (int i = 0; i < predictedFlowRates.length; i++) {
            System.out.println("水位 " + waterLevels[i] + " 米, 落差 " + deltaZ[i] + " 米的预测流量为: " + predictedFlowRates[i] + " 立方米/秒");
        }
        // 计算统计量
        calculateStatistics(predictedFlowRates, flowRates);
    }

    /**
     * 根据水位和落差计算流量 Q = f(Z) * ΔZ^β
     */
    public static double[] calculateFlow(double[] waterLevels, double[] deltaZ, double a, double b, double c, double beta) {
        int n = waterLevels.length;
        double[] flowRates = new double[n];

        for (int i = 0; i < n; i++) {
            double Z = waterLevels[i];
            double fZ = a * Z * Z + b * Z + c; // 使用拟合模型 f(Z)
            flowRates[i] = fZ * Math.pow(deltaZ[i], beta); // 计算流量 Q
        }
        return flowRates;
    }

    /**
     * 计算系统误差、随机不确定度和其他统计量
     */
    public static void calculateStatistics(double[] predictedFlow, double[] actualFlow) {
        double systemError = Cal_SystematicError(actualFlow, predictedFlow);
        double randomUncertainty = Cal_RandomUncertainty(actualFlow, predictedFlow);
        System.out.println("系统误差: " + systemError + "%");
        System.out.println("随机不确定度: " + randomUncertainty + "%");
    }

    /**
     * 计算系统误差
     */
    public static Double Cal_SystematicError(double[] Data_ACT, double[] Data_SIM) {
        double se;
        double sum = 0.0;
        int counter = 0;
        for (int i = 0; i < Data_ACT.length; i++) {
            sum += Math.abs(Data_ACT[i] - Data_SIM[i]) / Data_SIM[i];
            counter++;
        }
        se = sum / counter;
        return se * 100; // 返回百分比
    }

    /**
     * 计算随机不确定度
     */
    public static Double Cal_RandomUncertainty(double[] Data_ACT, double[] Data_SIM) {
        double sumSquared = 0.0;
        int counter = Data_ACT.length;

        for (int i = 0; i < counter; i++) {
            sumSquared += Math.pow((Data_ACT[i] - Data_SIM[i]) / Data_SIM[i], 2);
        }
        return 2 * Math.sqrt(sumSquared / (counter - 2)) * 100; // 返回百分比
    }


    public static double[] fitParameters(double[] waterLevels, double[] flowRates, double[] deltaZ, double[] initialGuess) {
        MultivariateOptimizer optimizer = new BOBYQAOptimizer(7);
        ObjectiveFunction objectiveFunction = new ObjectiveFunction(params -> calculateError(waterLevels, flowRates, deltaZ, params));
        // 设置参数 d 的取值范围为 0.2 到 0.8
        SimpleBounds bounds = new SimpleBounds(
                new double[]{Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, 0.2},
                new double[]{Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 0.8});
        double[] result = optimizer.optimize(
                new MaxEval(100000), //最大评估次数 没有求解的时候可以尝试增大
                objectiveFunction,
                GoalType.MINIMIZE,
                bounds,
                new InitialGuess(initialGuess)
        ).getPoint();
        return result;
    }

    private static double calculateError(double[] waterLevels, double[] flowRates, double[] deltaZ, double[] params) {
        double a = params[0], b = params[1], c = params[2], beta = params[3];
        double errorSum = 0.0;
        for (int i = 0; i < waterLevels.length; i++) {
            double Z = waterLevels[i];
            double predictedFlow = (a * Z * Z + b * Z + c) * Math.pow(deltaZ[i], beta);
            errorSum += Math.pow(predictedFlow - flowRates[i], 2);
        }
        return errorSum;
    }

测试结果如下:

从结果上来看猜测2好像更精确些,再次记录一下,方便后续查询!


原文地址:https://blog.csdn.net/u012440725/article/details/142880545

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!