当前位置: 首页 > article >正文

基于布谷鸟算法实现率定系数的starter

  布谷鸟算法(Cuckoo Search, CS)是一种基于群体智能的优化算法,灵感来源于布谷鸟的繁殖行为以及宿主鸟发现外来蛋的概率。该算法由 Xin-She Yang 和 Suash Deb 在2009年提出。它结合了莱维飞行(Lévy flight)这一随机漫步模式,这种模式在自然界中被观察到是某些动物寻找食物时采用的方式,具有高效探索大范围空间的能力。之前把matlab的率定系数的转成java实现后,希望做成一个更加通用的模块,便有了此文。

1. 概述

CJH HUP Optimization Starter 是一个基于 Spring Boot 的优化算法工具包,支持从 Excel或 JSON 中读取数据,并通过多目标优化算法计算最优解。该工具包适用于水文分析、流量预测等场景。

2. 功能特性

  • 多数据源支持:支持 Excel 和 JSON 文件格式。

  • 多目标优化:基于 布谷鸟种群算法实现多目标优化。

  • 灵活配置:通过配置文件调整算法参数。

  • 高性能:优化后的算法执行速度快,适合大规模数据处理。

  • 易扩展:支持自定义数据源和优化算法。


3.代码编写

3.1 设置项目结构

3.2定义默认的配置文件

cjh:
  hup:
    data-source-type: excel #目前支持excel和json 默认为excel
    num-pop: 500 #种群规模
    num-obj: 2 #目标函数个数:随机不确定度、系统误差
    num-opt: 5 #优化变量个数
    low-limit: -10 #系数取值范围下限
    up-limit: 20 #系数取值范围上限
    h1: 173.9 #第一层声路高程
    h2: 175.4 #第二层声路高程
    h3: 177.1 #第三层声路高程
    h4: 178.6 #第四层声路高程
    w: 0.6 #河底流速系数
    max-gen: 1000 #最大迭代次数
    q_obs_col: 5 #流量数据所在列
    hs_col: 6 #hs数据所在列
    v-cols: [8, 9, 10, 11] #流速数据列
    lg-hs: #水位面积关系
      164.95: 0
      164.96: 0.01
      164.97: 0.02
      164.98: 0.03

3.3编写自动配置类

@Configuration
@EnableConfigurationProperties(HupProperties.class)
public class HupAutoConfiguration {

    public HupAutoConfiguration() {
        System.out.println("HupAutoConfiguration has been instantiated.");
    }

    @Bean
    @ConditionalOnMissingBean
    public HupOptimizer hupOptimizer(HupProperties properties) {
        System.out.println("HupOptimizer has been instantiated.");
        return new HupOptimizer(properties);
    }

    @Bean
    @ConditionalOnMissingBean
    public HupDataSource hupDataSource(HupProperties properties) {
        if ("json".equalsIgnoreCase(properties.getDataSourceType())) {
            System.out.println("YourAutoConfigurationClass has been instantiated and use type json");
            return new JsonDataSource();
        } else {
            System.out.println("YourAutoConfigurationClass has been instantiated and use type excel");
            return new ExcelDataSource();
        }
    }

    @Bean
    @ConditionalOnMissingBean
    public HupService hupService(HupDataSource dataSource, HupOptimizer optimizer) {
        System.out.println("HupService has been instantiated." );
        return new HupService(dataSource, optimizer);
    }
}

3.4 定义数据源

定义父集数据源接口

public interface HupDataSource {
    Map<String, Object> loadData(String filePath) throws RuntimeException;

    default void validateData(Map<String, Object> data) {
        if (!data.containsKey("Q_obs") || !data.containsKey("v")) {
            throw new RuntimeException("Invalid data format");
        }
    }
}

json数据源

@Component
@ConditionalOnProperty(name = "cjh.hup.data-source-type", havingValue = "json")
public class JsonDataSource implements HupDataSource {

    @Override
    public Map<String, Object> loadData(String jsonData) {
        try {
            FlowData flowData = JSONUtil.toBean(jsonData, FlowData.class, true);
            Map<String, Object> map = new HashMap<>();
            // 添加Q_obs
            map.put("Q_obs", flowData.getQ_obs());
            map.put("Q_obs_len", flowData.getQ_obs().length);
            // 添加v,这里直接放入了double[][]类型的值,因为Java中的Object可以持有任何类型的数据
            map.put("v", flowData.getV());
            map.put("v_len", flowData.getV().length);
            // 添加Hs
            map.put("Hs", flowData.getHs());
            map.put("Hs_len", flowData.getHs().length);
            validateData(map);
            return map;
        } catch (Exception e) {
            throw new RuntimeException("JSON data loading failed", e);
        }
    }
}
@Component
@ConditionalOnProperty(name = "cjh.hup.data-source-type", havingValue = "excel")
public class ExcelDataSource implements HupDataSource {
    @Resource
    HupProperties properties;

    @Override
    public Map<String, Object> loadData(String filePath) {
        try (ExcelReader reader = ExcelUtil.getReader(new File(filePath))) {
            List<List<Object>> data = reader.read();
            Map<String, Object> result = new HashMap<>();
            double[] doubles = extractColumn(data, properties.getQ_obs_col());
            result.put("Q_obs", doubles);
            result.put("Q_obs_len", doubles.length);
            double[][] doubles1 = extractColumns(data, properties.getvCols());
            result.put("v", doubles1);
            result.put("v_len", doubles1.length);
            double[] doubles2 = extractColumn(data, properties.getHs_col());
            result.put("Hs", doubles2);
            result.put("Hs_len", doubles2);

            validateData(result);
            return result;
        } catch (Exception e) {
            throw new RuntimeException("Excel data loading failed", e);
        }
    }

    // 实现列提取逻辑
    private static double[] extractColumn(List<List<Object>> data, int col) {
        double[] columnData = new double[data.size() - 1]; // 减去第一行标题
        for (int i = 1; i < data.size(); i++) { // 从第2行开始读取数据
            List<Object> row = data.get(i);
            if (row.get(col) instanceof Number) {
                columnData[i - 1] = ((Number) row.get(col)).doubleValue();
            } else {
                columnData[i - 1] = Double.NaN; // 如果非数值型数据则设为NaN
            }
        }
        return columnData;
    }

    // 从Excel中读取指定列(indices)数据
    // 实现多列提取逻辑
    public static double[][] extractColumns(List<List<Object>> data, int[] indices) {
        double[][] columnData = new double[indices.length][data.size() - 1]; // 减去第一行标题
        for (int i = 1; i < data.size(); i++) { // 从第2行开始读取数据
            List<Object> row = data.get(i);
            for (int j = 0; j < indices.length; j++) {
                int colIndex = indices[j];
                if (row.get(colIndex) instanceof Number) {
                    columnData[j][i - 1] = ((Number) row.get(colIndex)).doubleValue();
                } else {
                    columnData[j][i - 1] = Double.NaN; // 非数值型数据设置为NaN
                }
            }
        }
        return columnData;
    }
}

 3.5定义优化算法

public class HupOptimizer {
    private final HupProperties properties;
    public HupOptimizer(HupProperties properties) {
        this.properties = properties;
    }

    public OptimizationResult optimize(Map<String, Object> inputData) {
        Long t1 = System.currentTimeMillis();
        // 实现优化算法主体逻辑
        InitializeData initializeData = initializePopulation(inputData);
        double[][] nest = initializeData.getNest();
        double[] b = initializeData.getB();
        double[][] Q_shicha = initializeData.getQ_shicha();
        List<Object> Q_fenceng = initializeData.getQ_fenceng();
        int NUM_POP = properties.getNumPop();
        int NUM_OPT = properties.getNumOpt();
        int NUM_OBJ = properties.getNumObj();
        int q_len = (int) inputData.get("Q_obs_len");
        // 创建一个新的数组,大小为NUM_POP行目标+变量个+2数列
        double[][] expandedArray = new double[NUM_POP][NUM_OBJ + NUM_OPT +2 ];
        // 将原始数组的数据复制到新数组中
        for (int t = 0; t < NUM_POP; t++) {
            for (int j = 0; j < NUM_OBJ + NUM_OPT; j++) {
                expandedArray[t][j] = nest[t][j];
            }
        }
        //初始化新添加的列,这里设置为0,你可以根据需要设置为其他值
        for (int i = 0; i < NUM_POP; i++) {
            expandedArray[i][7] = 0;
            expandedArray[i][8] = 0;
        }
        expandedArray = nonDominationSort(expandedArray, NUM_OBJ, NUM_OPT);
        // 优化求解
        System.out.println("计算中...");
        double[][] optimizedNest = optimize(expandedArray, inputData);

        double[] temp = new double[NUM_OPT];
        for (int jj = 0; jj < NUM_POP; jj++) {
            for (int m = 0; m < NUM_OPT; m++) {
                temp[m] = optimizedNest[jj][m];
            }
            FobjData fobj = fobj(temp, inputData);
            double[] f = fobj.getF();
            double bx = fobj.getB();
            b[jj] = bx;
            double[] Q_shichatemp = fobj.getQ_shicha();
            double[][] Q_fencengTemp = fobj.getQ_fenceng();
            for (int t = 0; t < NUM_OBJ; t++) {
                optimizedNest[jj][NUM_OPT + t] = f[0];
            }
            Q_shicha[jj] = Q_shichatemp;
            Q_fenceng.add(jj, Q_fencengTemp);
        }

        // 提取优化变量部分
        double[][] nest_Pareto = new double[nest.length][NUM_OPT];
        for (int i = 0; i < nest.length; i++) {
            System.arraycopy(nest[i], 0, nest_Pareto[i], 0, NUM_OPT);
        }
        // 提取目标函数值部分
        double[][] f_Pareto = new double[nest.length][NUM_OBJ];
        for (int i = 0; i < nest.length; i++) {
            System.arraycopy(nest[i], NUM_OPT, f_Pareto[i], 0, NUM_OBJ);
        }
        // 构建 Pareto 矩阵
        double[][] Pareto = new double[nest.length][NUM_OPT + 1 + NUM_OBJ + q_len];
        for (int i = 0; i < nest.length; i++) {
            System.arraycopy(nest_Pareto[i], 0, Pareto[i], 0, NUM_OPT);
            Pareto[i][NUM_OPT] = b[i];
            System.arraycopy(Q_shicha[i], 0, Pareto[i], NUM_OPT + 1, q_len);
            System.arraycopy(f_Pareto[i], 0, Pareto[i], NUM_OPT + q_len + 1, NUM_OBJ);
        }
        // 寻找随机不确定度最小行及对应的结果
        double minF1 = Double.MAX_VALUE;
        int Y = -1;
        for (int i = 0; i < f_Pareto.length; i++) {
            if (f_Pareto[i][0] < minF1) {
                minF1 = f_Pareto[i][0];
                Y = i;
            }
        }

        double[] k_opt = Arrays.copyOf(nest_Pareto[Y], nest_Pareto[Y].length);
        double b_opt = b[Y];
        double f1_opt = f_Pareto[Y][0];
        double f2_opt = f_Pareto[Y][1];
        // 打印结果
        System.out.printf("k= %.2f %.2f %.2f %.2f %.2f, b= %.2f, 随机不确定度= %.4f, 系统误差= %.4f\n",
                k_opt[0], k_opt[1], k_opt[2], k_opt[3], k_opt[4], b_opt, f1_opt, f2_opt);

        OptimizationResult result = new OptimizationResult();
        result.setB(b_opt);
        result.setK(k_opt);
        result.setUncertainty(f1_opt);
        result.setSystemError(f2_opt);
        Long t2 = System.currentTimeMillis();
        System.out.println("耗时:" + (t2 - t1) / 1000 + "s");
        return result;
    }


    // 优化求解
    private double[][] optimize(double[][] nest, Map<String, Object> simuPara) {
        int gen = 0;
        int MAX_GEN = properties.getMaxGen();
        int NUM_POP = properties.getNumPop();
        int NUM_OPT = properties.getNumOpt();
        int NUM_OBJ = properties.getNumObj();
        while (gen < MAX_GEN) {
            gen++;
            double pa = 0.5 - gen * (0.5 - 0.05) / MAX_GEN;
            double[][] newNest = emptyNests(nest, pa, simuPara);
            double[][] Tempnest = verticalConcatenate(nest, newNest);
            Tempnest = nonDominationSort(Tempnest, NUM_OBJ, NUM_OPT);
            nest = replace(Tempnest, NUM_OBJ, NUM_OPT, NUM_POP);
        }
        return nest;
    }

    //预生成所有随机数
    private final ThreadLocal<Random> random = ThreadLocal.withInitial(Random::new);

    private InitializeData initializePopulation(Map<String, Object> data) {
        InitializeData resultData = new InitializeData();
        int NUM_POP = properties.getNumPop();
        int NUM_OPT = properties.getNumOpt();
        int NUM_OBJ = properties.getNumObj();
        double LOW_LIMIT = properties.getLowLimit();
        double UP_LIMIT = properties.getUpLimit();
        double[][] nest = new double[NUM_POP][NUM_OPT + NUM_OBJ];
        double[][] Q_shicha = new double[NUM_POP][(int) data.get("Q_obs_len")];
        List<Object> Q_fenceng = new ArrayList<>();
        double[] b = new double[NUM_POP];
        // 并行初始化种群
        IntStream.range(0, NUM_POP).parallel().forEach(i -> {
            double[] KK = new double[NUM_OPT];
            Random rand = random.get();
            for (int j = 0; j < NUM_OPT; j++) {
                KK[j] = LOW_LIMIT + rand.nextDouble() * (UP_LIMIT - LOW_LIMIT);
            }
            System.arraycopy(KK, 0, nest[i], 0, KK.length);
            FobjData fobjData = fobj(KK, data);
            System.arraycopy(fobjData.getF(), 0, nest[i], NUM_OPT, NUM_OBJ);
        });
        resultData.setNest(nest);
        resultData.setQ_shicha(Q_shicha);
        resultData.setQ_fenceng(Q_fenceng);
        resultData.setB(b);
        return resultData;
    }

    private double[][] emptyNests(double[][] oldNest, double pa, Map<String, Object> simuPara) {
        int m = properties.getNumObj(); // 目标函数个数
        int nd = properties.getNumOpt(); // 优化变量的个数
        // 深拷贝 oldNest 到 newNest
        double[][] newNest = new double[oldNest.length][oldNest[0].length];
        for (int i = 0; i < oldNest.length; i++) {
            System.arraycopy(oldNest[i], 0, newNest[i], 0, oldNest[i].length);
        }
        double[][] nest = new double[oldNest.length][nd];

        // 复制 oldNest 到 newNest 并提取优化变量
        for (int i = 0; i < oldNest.length; i++) {
            System.arraycopy(oldNest[i], 0, nest[i], 0, nd);
        }
        int n = nest.length;
        Random rand = new Random();
        // 生成布尔矩阵 K
        boolean[][] K = new boolean[n][nd];
        for (int i = 0; i < K.length; i++) {
            for (int j = 0; j < K[i].length; j++) {
                K[i][j] = rand.nextDouble() > pa;
            }
        }
        // 找到每行中 true 值的数量等于 nd 的行索引
        List<Integer> x = new ArrayList<>();
        for (int i = 0; i < K.length; i++) {
            int count = 0;
            for (int j = 0; j < K[i].length; j++) {
                if (K[i][j]) {
                    count++;
                }
            }
            if (count == nd) {
                x.add(i);
            }
        }
        // 修改布尔矩阵 K 中特定行的最后一列
        for (int row : x) {
            K[row][K[row].length - 1] = false;
        }
        // 生成布尔矩阵 L
        boolean[][] L = new boolean[K.length][K[0].length];
        for (int i = 0; i < K.length; i++) {
            for (int j = 0; j < K[i].length; j++) {
                L[i][j] = !K[i][j];
            }
        }
        // 计算每行中 0 的数量
        int[] count = new int[K.length];
        for (int i = 0; i < K.length; i++) {
            int rowCount = 0;
            for (int j = 0; j < K[i].length; j++) {
                if (L[i][j]) {
                    rowCount++;
                }
            }
            count[i] = rowCount;
        }
        // 生成随机排列
        List<Integer> indices = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            indices.add(i);
        }
        Collections.shuffle(indices);
        int[] rand1 = indices.stream().mapToInt(Integer::intValue).toArray();
        Collections.shuffle(indices);
        int[] rand2 = indices.stream().mapToInt(Integer::intValue).toArray();
        // 生成随机缩放因子
        double randNum = rand.nextDouble();
        // 计算 delta
        double[][] delta = new double[n][nd];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < nd; j++) {
                delta[i][j] = randNum * (nest[rand1[i]][j] - nest[rand2[i]][j]);
            }
        }
        // 计算 stepsize1
        double[][] stepsize1 = new double[n][nd];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < nd; j++) {
                stepsize1[i][j] = K[i][j] ? delta[i][j] : 0.0;
            }
        }
        // 计算 delta2
        double[] delta2 = new double[n];
        for (int i = 0; i < n; i++) {
            double sum = 0.0;
            for (int j = 0; j < nd; j++) {
                sum += stepsize1[i][j];
            }
            delta2[i] = sum / count[i];
        }
        // 计算 stepsize2
        double[][] stepsize2 = new double[nest.length][nest[0].length];
        for (int i = 0; i < nest.length; i++) {
            for (int j = 0; j < nest[0].length; j++) {
                stepsize2[i][j] = delta2[i] * (L[i][j] ? 1.0 : 0.0);
            }
        }

        // 计算最终步长 stepsize
        double[][] stepsize = new double[nest.length][nest[0].length];
        for (int i = 0; i < nest.length; i++) {
            for (int j = 0; j < nest[0].length; j++) {
                stepsize[i][j] = stepsize1[i][j] - stepsize2[i][j];
            }
        }
        // 更新巢穴位置+stepsize new_nest
        double[][] new_nest = new double[nest.length][nest[0].length];
        for (int i = 0; i < nest.length; i++) {
            for (int j = 0; j < nest[0].length; j++) {
                new_nest[i][j] = nest[i][j] + stepsize[i][j];
            }
        }
        //fobj计算2个函数值
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < nd; j++) {
                newNest[i][j] = nest[i][j] + stepsize1[i][j];
            }
            // 评估 new nest 并存储结果
            double[] f = fobj(newNest[i], simuPara).getF();
            System.arraycopy(f, 0, newNest[i], nd, m);
        }
        return newNest;
    }


    private FobjData fobj(double[] KK, Map<String, Object> data) {
        FobjData fobjData = new FobjData();
        // 初始化目标函数值
        double[] f = new double[2];
        // 参数传递
        double[][] lgHS = DataUtil.mapToArray(properties.getLgHs());
        double[] Q_obs = (double[]) data.get("Q_obs");
        double[][] v = (double[][]) data.get("v");
        double[] Hs = (double[]) data.get("Hs");
        double H1 = properties.getH1();
        double H2 = properties.getH2();
        double H3 = properties.getH3();
        double H4 = properties.getH4();
        double w = properties.getW();
        int n = (int) data.get("Q_obs_len");
        double lowlimit = properties.getLowLimit();
        double uplimit = properties.getUpLimit();
        double[] k = KK; // matlab中kk是一个一行NUM_OPT列的数组 经过转秩变成了一个 NUM_OPT列1行的数组

        // 推算流量
        double[] v0 = new double[v[0].length];
        for (int i = 0; i < v[0].length; i++) {
            v0[i] = w * v[0][i]; // 计算v0
        }
        double[] rows0 = exColsWithNum(lgHS, 0);
        double[] rows1 = exColsWithNum(lgHS, 1);
        double[] S0 = chazhi(rows0, rows1, new double[]{H1});
        double[] S1 = subtract(chazhi(rows0, rows1, new double[]{H2}), S0);
        double[] S2 = subtract(chazhi(rows0, rows1, new double[]{H3}), chazhi(rows0, rows1, new double[]{H2}));
        double[] S3 = subtract(chazhi(rows0, rows1, new double[]{H4}), chazhi(rows0, rows1, new double[]{H3}));
        double[] S4 = subtract(chazhi(rows0, rows1, Hs), chazhi(rows0, rows1, new double[]{H4}));

        double[] Q0 = new double[v0.length];
        double[] Q1 = new double[v[0].length];
        double[] Q2 = new double[v[1].length];
        double[] Q3 = new double[v[2].length];
        double[] Q4 = new double[v[3].length];

        for (int i = 0; i < v0.length; i++) {
            Q0[i] = (v0[i] + v[0][i]) * 0.5 * S0[0];
        }
        for (int i = 0; i < Q1.length; i++) {
            Q1[i] = (v[0][i] + v[1][i]) * 0.5 * S1[0];
        }

        for (int i = 0; i < Q2.length; i++) {
            Q2[i] = (v[1][i] + v[2][i]) * 0.5 * S2[0];
        }
        for (int i = 0; i < Q3.length; i++) {
            Q3[i] = (v[2][i] + v[3][i]) * 0.5 * S3[0];
        }
        for (int i = 0; i < Q4.length; i++) {
            Q4[i] = v[3][i] * S4[i];
        }
        // 合并流量
        double[][] Q_fenceng = {Q0, Q1, Q2, Q3, Q4};
        double[] Q_shicha = new double[Q_fenceng[0].length]; // 按列求和,初始化为零的数组
        double[][] tempx = new double[Q_fenceng.length][Q_fenceng[0].length]; // 按列求和,初始化为零的数组
        // 构建点乘矩阵 合并为单层循环
        int rows = Q_fenceng.length;
        int cols = Q_fenceng[0].length;
        for (int i = 0; i < rows * cols; i++) {
            int j = i / cols;
            int kIndex = i % cols;
            tempx[j][kIndex] = k[j] * Q_fenceng[j][kIndex];
        }
        //按列求和
        for (int i = 0; i < Q_shicha.length; i++) {
            Q_shicha[i] = Arrays.stream(exColsWithNum(tempx, i)).sum();
        }
        // 计算误差均值,修正推算流量
        //计算 Q_obs 和 Q_shicha 之间的差的平均值
        double sumDifference = 0.0;
        for (int i = 0; i < Q_obs.length; i++) {
            sumDifference += Q_obs[i] - Q_shicha[i];
        }
        double b = sumDifference / Q_obs.length;
        //更新 Q_shicha
        for (int i = 0; i < Q_shicha.length; i++) {
            Q_shicha[i] += b;
        }
        // 增加惩罚函数
        double penalty = 0.0;
        int s1 = 0;
        int s2 = 0;
        for (double ki : k) {
            if (ki < lowlimit) {
                s1 += 1;
            }
            if (ki > uplimit) {
                s2 += 1;
            }
        }
        if (s1 > 0 || s2 > 0) {
            penalty = 100;
        }

        // 计算两个目标函数
        f[0] = 2 * Math.sqrt(sumSquaredError(Q_obs, Q_shicha) / (n - 2)) + penalty; //随机不确定度
        f[1] = Math.abs(sumRelativeError(Q_obs, Q_shicha) / n) + penalty; // 系统误差
        fobjData.setF(f);
        fobjData.setB(b);
        fobjData.setQ_fenceng(Q_fenceng);
        fobjData.setQ_shicha(Q_shicha);
        return fobjData;
    }


    // 辅助方法:计算平方误差和
    private static double sumSquaredError(double[] Q_obs, double[] Q_shicha) {
        double sum = 0.0;
        for (int i = 0; i < Q_obs.length; i++) {
            sum += Math.pow((Q_obs[i] - Q_shicha[i]) / Q_shicha[i], 2);
        }
        return sum;
    }

    // 辅助方法:计算相对误差之和
    private static double sumRelativeError(double[] Q_obs, double[] Q_shicha) {
        double sum = 0.0;
        for (int i = 0; i < Q_obs.length; i++) {
            sum += (Q_obs[i] - Q_shicha[i]) / Q_shicha[i];
        }
        return sum;
    }

    public static double[] exColsWithNum(double[][] matrix, int columnIndex) {

        // 创建一个一维数组来存储列向量
        double[] columnVector = new double[matrix.length];

        // 遍历二维数组的行,提取指定列的元素
        for (int i = 0; i < matrix.length; i++) {
            columnVector[i] = matrix[i][columnIndex];
        }
        return columnVector;
    }


    /**
     * 按对应位置相减两个 double[] 数组,并返回结果数组。
     * 如果其中一个数组只有一个元素,则将该元素与另一个数组的每个元素进行相减。
     *
     * @param a 第一个 double[] 数组
     * @param b 第二个 double[] 数组
     * @return 按对应位置相减后的结果数组
     */
    public static double[] subtract(double[] a, double[] b) {
        int lengthA = a.length;
        int lengthB = b.length;
        double[] result;

        if (lengthA == 1 && lengthB > 1) {
            // a 只有一个元素,b 有多个元素
            result = new double[lengthB];
            for (int i = 0; i < lengthB; i++) {
                result[i] = a[0] - b[i];
            }
        } else if (lengthB == 1 && lengthA > 1) {
            // b 只有一个元素,a 有多个元素
            result = new double[lengthA];
            for (int i = 0; i < lengthA; i++) {
                result[i] = a[i] - b[0];
            }
        } else {
            // 两个数组都有多个元素,或者都只有一个元素
            int minLength = Math.min(lengthA, lengthB);
            result = new double[minLength];
            for (int i = 0; i < minLength; i++) {
                result[i] = a[i] - b[i];
            }
        }
        return result;
    }

    // chazhi 方法,根据 x 和 y 进行线性插值 二分插值优化
    private static double[] chazhi(double[] x, double[] y, double[] x0) {
        double[] z = new double[x0.length];
        for (int jj = 0; jj < x0.length; jj++) {
            double xc = x0[jj];
            if (xc <= x[0]) {
                z[jj] = y[0];
                continue;
            }
            if (xc >= x[x.length - 1]) {
                z[jj] = y[y.length - 1];
                continue;
            }
            // 使用二分查找替代线性搜索
            int idx = Arrays.binarySearch(x, xc);
            if (idx >= 0) {
                z[jj] = y[idx];
            } else {
                int insertionPoint = -idx - 1;
                int ii = insertionPoint - 1;
                double weight = (xc - x[ii]) / (x[ii + 1] - x[ii]);
                z[jj] = y[ii] * (1 - weight) + y[ii + 1] * weight;
            }
        }
        return z;
    }

    // 主函数:非支配排序和拥挤距离计算
    public static double[][] nonDominationSort(double[][] x, int M, int V) {
        int N = x.length;
        // 存储不同层级的非支配解集
        ArrayList<ArrayList<Integer>> F = new ArrayList<>();
        F.add(new  ArrayList<>());
        // 创建个体数组
        Individual[] individuals = new Individual[N];

        // 初始化个体信息
        for (int i = 0; i < N; i++) {
            individuals[i] = new Individual();
            for (int j = 0; j < N; j++) {
                // 比较个体 i 和个体 j 的支配关系
                int domLess = 0, domEqual = 0, domMore = 0;
                for (int k = 0; k < M; k++) {
                    if (x[i][V + k] < x[j][V + k]) {
                        domLess++;
                    } else if (x[i][V + k] == x[j][V + k]) {
                        domEqual++;
                    } else {
                        domMore++;
                    }
                }
                if (domLess == 0 && domEqual!= M) {
                    individuals[i].n++;
                }
                if (domMore == 0 && domEqual!= M) {
                    individuals[i].p.add(j);
                }
            }
            if (individuals[i].n == 0) {
                x[i][M + V] = 0;
                F.get(0).add(i);
            }
        }

        // 分层处理非支配解集
        int front = 0;
        while (!F.get(front).isEmpty())  {
            ArrayList<Integer> Q = new ArrayList<>();
            for (int i : F.get(front))  {
                for (int j : individuals[i].p) {
                    if (--individuals[j].n == 0) {
                        x[j][M + V] = front + 1;
                        Q.add(j);
                    }
                }
            }
            front++;
            F.add(Q);
        }

        // 根据层级排序
        double[][] sortedBasedOnFront = Arrays.copyOf(x,  N);
        Arrays.sort(sortedBasedOnFront,  Comparator.comparingDouble(o  -> o[M + V]));

        // 结果矩阵,包含层级和拥挤距离
        ArrayList<double[]> z = new ArrayList<>();
        int currentIndex = 0;

        // 遍历所有层级(除了最后一个空层级)
        for (int f = 0; f < F.size()  - 1; f++) {
            int frontSize = F.get(f).size();
            double[][] y = new double[frontSize][M + V + 1 + M];

            // 将当前层级的个体复制到 y 矩阵
            for (int i = 0; i < frontSize; i++) {
                System.arraycopy(sortedBasedOnFront[currentIndex  + i], 0, y[i], 0, sortedBasedOnFront[0].length);
            }
            currentIndex += frontSize;

            // 计算每个目标的拥挤距离
            for (int i = 0; i < M; i++) {
                final int columnToSort = V + i;
                // 根据第 i 个目标排序
                Integer[] indexOfObjectives = new Integer[frontSize];
                for (int j = 0; j < frontSize; j++) {
                    indexOfObjectives[j] = j;
                }
                Arrays.sort(indexOfObjectives,  Comparator.comparingDouble(a  -> y[a][columnToSort]));

                double fMax = y[indexOfObjectives[frontSize - 1]][columnToSort];
                double fMin = y[indexOfObjectives[0]][columnToSort];

                // 设置边界个体的拥挤距离为无穷大
                y[indexOfObjectives[frontSize - 1]][M + V + 1 + i] = Double.POSITIVE_INFINITY;
                y[indexOfObjectives[0]][M + V + 1 + i] = Double.POSITIVE_INFINITY;

                // 计算中间个体的拥挤距离
                for (int j = 1; j < frontSize - 1; j++) {
                    double nextObj = y[indexOfObjectives[j + 1]][columnToSort];
                    double prevObj = y[indexOfObjectives[j - 1]][columnToSort];
                    if (fMax - fMin == 0) {
                        y[indexOfObjectives[j]][M + V + 1 + i] = Double.POSITIVE_INFINITY;
                    } else {
                        y[indexOfObjectives[j]][M + V + 1 + i] = (nextObj - prevObj) / (fMax - fMin);
                    }
                }
            }

            // 计算总拥挤距离
            double[] distance = new double[frontSize];
            for (int i = 0; i < M; i++) {
                for (int j = 0; j < frontSize; j++) {
                    distance[j] += y[j][M + V + 1 + i];
                }
            }

            // 将总拥挤距离存入 y 矩阵
            for (int j = 0; j < frontSize; j++) {
                y[j][M + V + 1] = distance[j];
            }

            // 截取 y 矩阵的前 M + V + 2 列
            double[][] yTruncated = new double[frontSize][M + V + 2];
            for (int i = 0; i < frontSize; i++) {
                System.arraycopy(y[i],  0, yTruncated[i], 0, M + V + 2);
            }

            // 将 yTruncated 复制到 z 矩阵
            appendToZ(z, yTruncated, currentIndex - frontSize, currentIndex);
        }

        return convertTo2DArray(z);
    }

    // 将 ArrayList 转换为二维数组
    public static double[][] convertTo2DArray(ArrayList<double[]> list) {
        double[][] array = new double[list.size()][];
        for (int i = 0; i < list.size();  i++) {
            array[i] = list.get(i);
        }
        return array;
    }

    // 将 y 矩阵的内容复制到 z 矩阵的指定位置
    public static void appendToZ(ArrayList<double[]> z, double[][] y, int previousIndex, int currentIndex) {
        while (z.size()  < currentIndex) {
            z.add(new  double[y[0].length]);
        }
        for (int i = 0; i < y.length;  i++) {
            System.arraycopy(y[i],  0, z.get(previousIndex  + i), 0, y[i].length);
        }
    }

    // 个体类,存储个体的被支配次数和支配的个体索引列表
    private static class Individual {
        int n = 0;
        ArrayList<Integer> p = new ArrayList<>();
    }

    private static double[][] replace(double[][] nest, int numObj, int numOpt, int pop) {
        int N = nest.length;
        double[][] sortedChromosome = Arrays.copyOf(nest, N); // 复制原始数据
        Arrays.sort(sortedChromosome, Comparator.comparingDouble(o -> o[numObj + numOpt])); // 根据层级排序

        // 找到当前种群中等级最大的个体
        double maxRank = sortedChromosome[N - 1][numObj + numOpt];

        // 根据排名和拥挤距离选择个体,直到整个种群规模达到规定值
        double[][] f = new double[pop][numObj + numOpt + 2];
        int previousIndex = 0;
        for (int i = 1; i <= maxRank; i++) {
            int currentIndex = 0;
            final int tempI = i;
            double[] column = exColsWithNum(sortedChromosome, numObj + numOpt);
            // 找到等于 tempI 的最大索引
            currentIndex = IntStream.range(0, column.length)
                    .filter(j -> column[j] == tempI)
                    .max()
                    .orElse(-1) - 1;
            if (currentIndex > pop) { // 如果当前索引大于保留种群规模限制
                int remaining = pop - previousIndex;
                double[][] tempPop = Arrays.copyOfRange(sortedChromosome, previousIndex, currentIndex);
                Integer[] tempSortIndex = new Integer[tempPop.length];
                for (int j = 0; j < tempPop.length; j++) {
                    tempSortIndex[j] = j;
                }
                Arrays.sort(tempSortIndex, (i1, i2) -> Double.compare(tempPop[i2][numObj + numOpt + 1], tempPop[i1][numObj + numOpt + 1])); // 按拥挤距离降序排列

                for (int j = 0; j < remaining; j++) {
                    f[previousIndex + j] = tempPop[tempSortIndex[j]];
                }
                return f; // 将保留的种群作为选择结果输出
            } else if (currentIndex < pop) { // 如果当前位置小于最大规模限制
                for (int j = previousIndex; j < currentIndex; j++) {
                    f[j] = sortedChromosome[j];
                }
            } else {
                for (int j = previousIndex; j < currentIndex; j++) {
                    f[j] = sortedChromosome[j];
                }
                return f; // 如果当前位置等于最大规模,直接返回
            }
            previousIndex = currentIndex;
        }
        return f; // 返回最终的种群
    }

    // 垂直拼接矩阵
    public static double[][] verticalConcatenate(double[][] matrix1, double[][] matrix2) {
        //直接复制二维数组
        double[][] result = Arrays.copyOf(matrix1, matrix1.length + matrix2.length);
        System.arraycopy(matrix2, 0, result, matrix1.length, matrix2.length);
        return result;
    }
}

 3.6定义服务类

public class HupService {
    private final HupDataSource dataSource;
    private final HupOptimizer optimizer;

    public HupService(HupDataSource dataSource, HupOptimizer optimizer) {
        this.dataSource = dataSource;
        this.optimizer = optimizer;
    }

    public OptimizationResult performOptimization(String filePath) {
        Map<String, Object> data = dataSource.loadData(filePath);
        return optimizer.optimize(data);
    }
}

4. 快速开始

4.1 添加依赖

pom.xml 中添加以下依赖:

<dependency>
    <groupId>com.cjh</groupId>
    <artifactId>cjh-hup-starter</artifactId>
    <version>1.0.0</version>
</dependency>

4.2 配置文件

application.yml 中配置算法参数默认参数如下:

cjh:
  hup:
    data-source-type: excel #目前支持excel和json 默认为excel
    num-pop: 500 #种群规模
    num-obj: 2 #目标函数个数:随机不确定度、系统误差
    num-opt: 5 #优化变量个数
    low-limit: -10 #系数取值范围下限
    up-limit: 20 #系数取值范围上限
    h1: 173.9 #第一层声路高程
    h2: 175.4 #第二层声路高程
    h3: 177.1 #第三层声路高程
    h4: 178.6 #第四层声路高程
    w: 0.6 #河底流速系数
    max-gen: 1000 #最大迭代次数
    q_obs_col: 5 #流量数据所在列
    hs_col: 6 #hs数据所在列
    v-cols: [8, 9, 10, 11] #流速数据列
    lg-hs: #水位面积关系
      164.95: 0
      164.96: 0.01
      164.97: 0.02
      164.98: 0.03

4.3 数据格式

Excel 文件
  • 文件名:1000-2000.xlsx

  • 数据格式:

    • 第5列:实测流量 Q_obs

    • 第8-11列:四层流速 v

    • 第6列:水位 Hs

JSON 数据
  • 数据格式:

{
    "Q_obs":[1430.0, 1010.0, 1450.0, 1200.0, 1470.0, 1990.0, 1860.0, 1550.0, 1600.0, 1520.0, 1780.0, 1650.0, 1440.0, 1530.0, 1340.0, 1580.0],
    "v": [[0.4825, 0.398, 0.628, 0.422, 0.664, 0.6522, 0.606, 0.5292, 0.534, 0.5111, 0.586, 0.56, 0.485, 0.5106, 0.46, 0.5373],
    [0.5, 0.374, 0.54, 0.46, 0.538, 0.6722, 0.664, 0.55, 0.55, 0.5363, 0.6, 0.5833, 0.5033, 0.52, 0.478, 0.5545],[0.515, 0.374, 0.55, 0.48, 0.562, 0.72, 0.67, 0.5633, 0.57, 0.5474, 0.614, 0.6017, 0.53, 0.5589, 0.482, 0.56],
    [0.525, 0.388, 0.54, 0.5, 0.574, 0.7611, 0.69, 0.56, 0.58, 0.5853, 0.524, 0.6, 0.54, 0.5667, 0.508, 0.5691]],
    "Hs":[179.59, 179.18, 179.18, 178.92, 179.58, 179.72, 179.59, 179.42, 179.65, 179.4, 179.68, 179.49, 179.43, 179.56, 179.18, 179.48]
}
水位面积关系线使用map配置
  • 数据配置为:

cjh:
  hup:
    lg-hs: #水位面积关系
      164.95: 0
      164.96: 0.01
      164.97: 0.02
      164.98: 0.03
      164.99: 0.04
      165: 0.07
      165.01: 0.09

5. 核心 API

5.1 HupService 服务类

方法说明
  • performOptimization(String dataPath)

    • 功能:执行优化算法。

    • 参数:

      • dataPath:数据文件路径或者是数据(支持 Excel、JSON)。

    • 返回值:OptimizationResult,包含优化结果。

示例代码
@RestController
@RequestMapping("/optimization")
public class OptimizationController {
​
    @Autowired
    private HupService hupService;
​
    @PostMapping
    public ResponseEntity<OptimizationResult> optimize(@RequestParam String dataPath) {
        try {
            OptimizationResult result = hupService.performOptimization(dataPath);
            return ResponseEntity.ok(result);
        } catch (DataProcessingException e) {
            return ResponseEntity.badRequest().build();
        }
    }
}

5.2 OptimizationResult 结果类

字段说明
  • k:优化变量系数(double[])。

  • b:偏差值(double)。

  • uncertainty:随机不确定度(double)。

  • systemError:系统误差(double)。

示例输出
{
    "k": [1.23, 2.34, 3.45, 4.56, 5.67],
    "b": 0.12,
    "uncertainty": 0.05,
    "systemError": 0.02
}

6. 扩展功能

6.1 自定义数据源

  1. 实现 DataSource 接口:

@Component
public class CustomDataSource implements DataSource {
    @Override
    public Map<String, Object> loadData(String filePath) {
        // 自定义数据加载逻辑
    }
}
  1. 在配置文件中指定数据源类型:

cjh:
  hup:
    data-source-type: custom

6.2 自定义优化算法

  1. 继承 HupOptimizer 类:

@Component
public class CustomOptimizer extends HupOptimizer {
    @Override
    public OptimizationResult optimize(Map<String, Object> inputData) {
        // 自定义优化逻辑
    }
}
  1. 在配置文件中指定优化器:

cjh:
  hup:
    optimizer: customOptimizer

7. 性能优化建议

  • 数据预处理:确保输入数据格式正确,避免无效数据。

  • 并行计算:启用多线程处理大规模数据。

  • JVM 调优:调整堆内存和垃圾回收器参数。


8. 常见问题

8.1 数据文件加载失败

  • 原因:文件路径错误或格式不正确。

  • 解决方案:检查文件路径和格式是否符合要求。

8.2 优化结果不理想

  • 原因:算法参数配置不当。

  • 解决方案:调整 num-popmax-gen 等参数。

9.代码测试与下载

9.1代码测试

 本地测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class CjhHupSpringBootStarterApplicationTests {

    @Autowired
    private HupService hupService;

    @Test
    public void testExcelOptimization() {
        OptimizationResult optimizationResult = hupService.performOptimization("D:\\waterConservancy\\CJH_HUP_JAVA -dev\\src\\test\\resources\\RLine\\excel\\1000-2000.xls");
        System.out.println(optimizationResult.toString());
    }

    @Test
    public void testJsonOptimization() {
        OptimizationResult optimizationResult = hupService.performOptimization("{\"Q_obs\":[1430.0,1010.0,1450.0,1200.0,1470.0,1990.0,1860.0,1550.0,1600.0,1520.0,1780.0,1650.0,1440.0,1530.0,1340.0,1580.0],\"v\":[[0.4825,0.398,0.628,0.422,0.664,0.6522,0.606,0.5292,0.534,0.5111,0.586,0.56,0.485,0.5106,0.46,0.5373],[0.5,0.374,0.54,0.46,0.538,0.6722,0.664,0.55,0.55,0.5363,0.6,0.5833,0.5033,0.52,0.478,0.5545],[0.515,0.374,0.55,0.48,0.562,0.72,0.67,0.5633,0.57,0.5474,0.614,0.6017,0.53,0.5589,0.482,0.56],[0.525,0.388,0.54,0.5,0.574,0.7611,0.69,0.56,0.58,0.5853,0.524,0.6,0.54,0.5667,0.508,0.5691]],\"Hs\":[179.59,179.18,179.18,178.92,179.58,179.72,179.59,179.42,179.65,179.4,179.68,179.49,179.43,179.56,179.18,179.48]}");
        System.out.println(optimizationResult.toString());
    }
}

引用测试时

在项目中引入starter如下:

编写测试control如下:

@Controller
public class BasicController {

    @Resource
    private  HupService hupService;



    @RequestMapping("/hello")
    @ResponseBody
    public String hello() {
        OptimizationResult result = hupService.performOptimization("D:\\waterConservancy\\CJH_HUP_JAVA -dev\\src\\test\\resources\\RLine\\excel\\1000-2000.xls");
        System.out.println(result.toString());

        return result.toString();
    }
}

结果如下:

9.2代码下载

https://download.csdn.net/download/u012440725/90355887


http://www.kler.cn/a/540914.html

相关文章:

  • 免费在腾讯云Cloud Studio部署DeepSeek-R1大模型
  • Linux系统-centos防火墙firewalld详解
  • [LVGL] 在VC_MFC中移植LVGL
  • 基于SpringBoot的线上历史馆藏管理系统
  • 1.2 变革里程碑:Transformer 的崛起
  • 【通俗易懂说模型】反向传播(附多元回归与Softmax函数)
  • SPI通信及设备驱动
  • TCP长连接、HTTP短轮询、HTTP长轮询、HTTP长连接、WebSocket的区别
  • Wpf美化按钮,输入框,下拉框,dataGrid
  • 【AI学习】LLM的发展方向
  • Qt:Qt Creator项目创建
  • CEF132 编译指南 MacOS 篇 - 基础开发工具安装实战 (二)
  • 游戏引擎学习第93天
  • 【Java】多线程和高并发编程(三):锁(下)深入ReentrantReadWriteLock
  • C++ decltype 规则推导
  • 能够复刻人类意识并实现永生的虚拟生态系统
  • (一)Axure制作移动端登录页面
  • pgsql最快的数据导入BeginBinaryImport
  • P3413 SAC#1 - 萌数
  • 中国城商行信贷业务数仓建设白皮书(第五期:智能决策体系构建)
  • 基于javaweb宠物领养平台管理系统设计和实现
  • webpack配置项之---output.assetModuleFilename
  • 解决“wsl 检测到 localhost 代理配置,但未镜像到 WSL。NAT 模式下的 WSL 不支持 localhost 代理”
  • 深度解析:使用ChromeDriver和webdriver_manager实现无头浏览器爬虫
  • OpenEuler学习笔记(二十二):OpenEuler上部署开源ERP系统Odoo
  • E7770A公共接口单元