基于落差法实现自动测流的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)!