diff --git a/ruoyi-modules/ruoyi-demo/pom.xml b/ruoyi-modules/ruoyi-demo/pom.xml index a7632ff..e1a44ff 100644 --- a/ruoyi-modules/ruoyi-demo/pom.xml +++ b/ruoyi-modules/ruoyi-demo/pom.xml @@ -22,6 +22,12 @@ true + + org.apache.commons + commons-math3 + 3.4.1 + + org.dromara diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/test/DEAExample.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/test/DEAExample.java new file mode 100644 index 0000000..0446a86 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/test/DEAExample.java @@ -0,0 +1,49 @@ +package org.dromara.demo.controller.test; + +import java.util.List; + +/** + * DEA模型使用示例 + */ +public class DEAExample { + public static void main(String[] args) { + // 示例:3个DMU,2个投入指标,1个产出指标 + // 投入矩阵: 每行代表一个DMU的投入 + // 例如: 投入可以是员工数量和资金投入 + double[][] inputs = { + {2, 3}, // DMU 0的投入 + {3, 4}, // DMU 1的投入 + {1, 5}, // DMU 2的投入 + {2, 3}, // DMU 3的投入 + {3, 4}, // DMU 4的投入 + {1, 5} // DMU 5的投入 + }; + + // 产出矩阵: 每行代表一个DMU的产出 + // 例如: 产出可以是利润 + double[][] outputs = { + {5}, // DMU 0的产出 + {6}, // DMU 1的产出 + {4}, // DMU 2的产出 + {5}, // DMU 0的产出 + {6}, // DMU 1的产出 + {4} // DMU 2的产出 + + }; + + // 计算所有DMU的效率值 + List results = DEAUtils.calculateAllDMUs(inputs, outputs); + + // 输出结果 + for (DEAUtils.DEAResult result : results) { + System.out.println(result); + } + + // 单独计算某个DMU的效率值 + int targetDMU = 1; + DEAUtils.DEAResult specificResult = DEAUtils.calculateDEA(inputs, outputs, targetDMU); + System.out.println("\n单独评估DMU " + targetDMU + " 的详细结果:"); + System.out.println("效率值: " + specificResult.getEfficiency()); + System.out.println("是否DEA有效: " + specificResult.isEfficient()); + } +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/test/DEAUtils.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/test/DEAUtils.java new file mode 100644 index 0000000..c171850 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/test/DEAUtils.java @@ -0,0 +1,267 @@ +package org.dromara.demo.controller.test; + +import org.apache.commons.math3.linear.Array2DRowRealMatrix; +import org.apache.commons.math3.linear.RealMatrix; +import org.apache.commons.math3.optim.*; +import org.apache.commons.math3.optim.linear.*; +import org.apache.commons.math3.optim.nonlinear.scalar.GoalType; + +import java.util.ArrayList; +import java.util.List; + +/** + * DEA(数据包络分析)工具类,实现面向投入的CCR模型 + */ +public class DEAUtils { + + /** + * 计算指定DMU的效率值 + * + * @param inputs 投入矩阵,每行代表一个DMU的所有投入,行数为DMU数量,列数为投入指标数量 + * @param outputs 产出矩阵,每行代表一个DMU的所有产出,行数为DMU数量,列数为产出指标数量 + * @param dmuIndex 要评估的DMU索引(从0开始) + * @return DEA计算结果,包含效率值和松弛变量等信息 + * @throws IllegalArgumentException 输入数据不合法时抛出 + */ + public static DEAResult calculateDEA(double[][] inputs, double[][] outputs, int dmuIndex) { + // 验证输入数据合法性 + validateInput(inputs, outputs, dmuIndex); + + int n = inputs.length; // DMU数量 + int m = inputs[0].length; // 投入指标数量 + int s = outputs[0].length; // 产出指标数量 + + // 变量数量: n个lambda + 1个theta + m个投入松弛变量 + s个产出松弛变量 + int varCount = n + 1 + m + s; + + // 目标函数: 最小化theta(第n个变量) + double[] objectiveCoefficients = new double[varCount]; + objectiveCoefficients[n] = 1.0; // theta的系数为1,其他变量系数为0 + + // 创建线性规划问题 + LinearObjectiveFunction objective = new LinearObjectiveFunction(objectiveCoefficients, 0); + + // 约束条件列表 + List constraints = new ArrayList<>(); + + // 1. 投入约束: sum(lambda_j * x_ij) + s_i^- = theta * x_ik (i=1..m) + for (int i = 0; i < m; i++) { + double[] coefficients = new double[varCount]; + // 设置lambda_j的系数 + for (int j = 0; j < n; j++) { + coefficients[j] = inputs[j][i]; + } + // 设置theta的系数 + coefficients[n] = -inputs[dmuIndex][i]; + // 设置投入松弛变量的系数 + coefficients[n + 1 + i] = 1.0; + + // sum(lambda_j * x_ij) - theta * x_ik + s_i^- = 0 + constraints.add(new LinearConstraint(coefficients, Relationship.EQ, 0.0)); + } + + // 2. 产出约束: sum(lambda_j * y_rj) - s_r^+ = y_rk (r=1..s) + for (int r = 0; r < s; r++) { + double[] coefficients = new double[varCount]; + // 设置lambda_j的系数 + for (int j = 0; j < n; j++) { + coefficients[j] = outputs[j][r]; + } + // 设置产出松弛变量的系数 + coefficients[n + 1 + m + r] = -1.0; + + // sum(lambda_j * y_rj) - s_r^+ = y_rk + constraints.add(new LinearConstraint(coefficients, Relationship.EQ, outputs[dmuIndex][r])); + } + + // 3. 变量非负约束 + // lambda_j >= 0 (j=1..n) + // theta无符号限制,但在CCR模型中通常theta <= 1 + // s_i^- >= 0 (i=1..m) + // s_r^+ >= 0 (r=1..s) + for (int j = 0; j < n; j++) { + double[] coefficients = new double[varCount]; + coefficients[j] = 1.0; + constraints.add(new LinearConstraint(coefficients, Relationship.GEQ, 0.0)); + } + + // 投入松弛变量非负 + for (int i = 0; i < m; i++) { + double[] coefficients = new double[varCount]; + coefficients[n + 1 + i] = 1.0; + constraints.add(new LinearConstraint(coefficients, Relationship.GEQ, 0.0)); + } + + // 产出松弛变量非负 + for (int r = 0; r < s; r++) { + double[] coefficients = new double[varCount]; + coefficients[n + 1 + m + r] = 1.0; + constraints.add(new LinearConstraint(coefficients, Relationship.GEQ, 0.0)); + } + + // 4. theta <= 1约束 + double[] thetaCoeff = new double[varCount]; + thetaCoeff[n] = 1.0; + constraints.add(new LinearConstraint(thetaCoeff, Relationship.LEQ, 1.0)); + + // 求解器配置 + SimplexSolver solver = new SimplexSolver(); + LinearOptimizer optimizer = solver; + + // 求解线性规划问题 + PointValuePair solution = optimizer.optimize( + new LinearConstraintSet(constraints), + objective, + GoalType.MINIMIZE, + new NonNegativeConstraint(false), // 已单独设置非负约束 + new MaxIter(1000) + ); + + // 解析结果 + double[] solutionPoint = solution.getPoint(); + double theta = solutionPoint[n]; // 效率值 + + // 投入松弛变量 + double[] inputSlacks = new double[m]; + System.arraycopy(solutionPoint, n + 1, inputSlacks, 0, m); + + // 产出松弛变量 + double[] outputSlacks = new double[s]; + System.arraycopy(solutionPoint, n + 1 + m, outputSlacks, 0, s); + + // lambda值 + double[] lambdas = new double[n]; + System.arraycopy(solutionPoint, 0, lambdas, 0, n); + + return new DEAResult(theta, inputSlacks, outputSlacks, lambdas, dmuIndex); + } + + /** + * 计算所有DMU的效率值 + * + * @param inputs 投入矩阵 + * @param outputs 产出矩阵 + * @return 所有DMU的DEA计算结果列表 + */ + public static List calculateAllDMUs(double[][] inputs, double[][] outputs) { + List results = new ArrayList<>(); + for (int i = 0; i < inputs.length; i++) { + results.add(calculateDEA(inputs, outputs, i)); + } + return results; + } + + /** + * 验证输入数据的合法性 + */ + private static void validateInput(double[][] inputs, double[][] outputs, int dmuIndex) { + if (inputs == null || outputs == null) { + throw new IllegalArgumentException("投入和产出矩阵不能为null"); + } + + if (inputs.length != outputs.length) { + throw new IllegalArgumentException("投入和产出矩阵的行数(DMU数量)必须一致"); + } + + if (inputs.length == 0) { + throw new IllegalArgumentException("至少需要一个DMU"); + } + + int m = inputs[0].length; + for (double[] input : inputs) { + if (input.length != m) { + throw new IllegalArgumentException("所有DMU的投入指标数量必须一致"); + } + } + + int s = outputs[0].length; + for (double[] output : outputs) { + if (output.length != s) { + throw new IllegalArgumentException("所有DMU的产出指标数量必须一致"); + } + } + + if (dmuIndex < 0 || dmuIndex >= inputs.length) { + throw new IllegalArgumentException("DMU索引超出范围"); + } + } + + /** + * DEA计算结果封装类 + */ + public static class DEAResult { + private final double efficiency; // 效率值(theta) + private final double[] inputSlacks; // 投入松弛变量 + private final double[] outputSlacks; // 产出松弛变量 + private final double[] lambdas; // 各DMU的权重 + private final int dmuIndex; // 对应的DMU索引 + + public DEAResult(double efficiency, double[] inputSlacks, double[] outputSlacks, + double[] lambdas, int dmuIndex) { + this.efficiency = efficiency; + this.inputSlacks = inputSlacks; + this.outputSlacks = outputSlacks; + this.lambdas = lambdas; + this.dmuIndex = dmuIndex; + } + + /** + * 判断该DMU是否为DEA有效 + * 当效率值=1且所有松弛变量为0时,DMU为DEA有效 + */ + public boolean isEfficient() { + if (Math.abs(efficiency - 1.0) > 1e-9) { + return false; + } + for (double slack : inputSlacks) { + if (Math.abs(slack) > 1e-9) { + return false; + } + } + for (double slack : outputSlacks) { + if (Math.abs(slack) > 1e-9) { + return false; + } + } + return true; + } + + // getter方法 + public double getEfficiency() { + return efficiency; + } + + public double[] getInputSlacks() { + return inputSlacks.clone(); + } + + public double[] getOutputSlacks() { + return outputSlacks.clone(); + } + + public double[] getLambdas() { + return lambdas.clone(); + } + + public int getDmuIndex() { + return dmuIndex; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("DMU ").append(dmuIndex).append(" 评估结果:\n"); + sb.append(" 效率值: ").append(String.format("%.4f", efficiency)).append("\n"); + sb.append(" 是否DEA有效: ").append(isEfficient()).append("\n"); + sb.append(" 投入松弛变量: "); + for (double slack : inputSlacks) { + sb.append(String.format("%.4f ", slack)); + } + sb.append("\n 产出松弛变量: "); + for (double slack : outputSlacks) { + sb.append(String.format("%.4f ", slack)); + } + return sb.toString(); + } + } +}