遗传算法库jmetal最简示例
这个示例展示了如何使用 JMetal 进行简单的多目标优化问题。你可以根据需要修改问题、算法参数和算子来适应不同的优化问题。
遗传算法的简单处理流程
摘自 https://pymoo.org/algorithms/soo/ga.html
https://github.com/Mycenae/PaperWeekly/blob/master/jMetalPy.md
NSGAII 算法特点:
- 种群大小保持一致:在 NSGA-II 中,父代种群和子代种群的大小是相同的。如果初始种群包含𝑁 个个体,那么在每一代中,子代的数量也将是 𝑁。
- 选择机制:在合并的种群中(父代和子代的组合),算法根据非支配等级和拥挤度距离选择出 𝑁 个个体作为下一代的种群。因此,最终的可行解个数不会超过初始种群的个体数。
- 多目标优化:尽管最终的可行解个数最多为 𝑁,但 NSGA-II 的目标是找到多个 Pareto 最优解,最终返回的解可能在目标空间中形成一个 Pareto 前沿。
NSGAIII 算法特点:
- NSGAII 因为使用非支配排序和拥挤度距离来选择个体, 当目标函数数量较多时候, 计算效率会变差.
- NSGAIII 的优势主要体现在目标函数较多的情况下, 它使用均匀分布的参考点来引导个体的选择, 能更加有效覆盖Pareto前沿, 解的多样性会更好, 速度更快, 尤其是在高维目标(即多目标函数)
相关类库
- jmetal-algorithm :包含各种遗传算法和算法执行Main函数代码,
- jmetal-core : 包含各种基础类
- jmetal-problem : 定义了 AbstractDoubleProblem 和 AbstractIntegerProblem 问题域抽象, 也定义了很多常用的问题模型, 比如 ZDT1 性能测试用问题, 比如 Traveling Salesman Problem 等.
- jmetal-lab: 可选包, 包含作者的一些学习和测试代码, 我们可以借鉴一下.
编程提示:
- 问题定义示例可以参考 ZDT1
- 求解器主函数可以参考 NSGAIIRunner
- NSGAII 算法是按照最小值做优化的, 所以如果要求最大值, objective 函数要乘负一.
- 在新版6.6中, 貌似没找到算法迭代 Observer, 这样对于观察每代的情况不是很方便, Problem 的 evaluate() 会对个体评价, 可以很容易访问到的决策变量变量(即基因)和objective(即适应度), 所以可以在这个方法中加上日志来观察演进变化.
关于约束问题
- 需要说明的是, 所有的遗传算法都是随机搜索算法,可能会生成不满足约束的解, 对于多目标函数尤为如此.
- NSGAII/SPEA2 , 尤其是在解决无约束多目标优化问题的性能而被广泛使用, 对于约束, 这些算法底层使用了惩罚函数来柔性评估约束, 这种方法并不能保证生成的解一定满足约束, 只是使得满足约束的解有更大可能被选择.
- 推荐使用BinaryTournamentSelection 选择算子, 它实现"feasibility rule"的一种选择操作。BinaryTournamentSelection在选择操作中,会随机选择两个解进行比较,如果两个解在Pareto支配关系中可以比较,那么就选择支配对方的解;如果两个解在Pareto支配关系中无法比较,那么就选择满足约束的解;如果两个解都满足约束,或者都不满足约束,那么就随机选择一个解。 但是,需要注意的是,即使使用了"feasibility rule",也不能保证所有的解都满足约束,因为遗传算法是一种随机搜索算法,可能会生成不满足约束的解.
- 在代码中可通过设置 solution.constraints()[i] 取值来设定约束条件, 即给出惩罚值, 取值>=0为满足约束, 取值小于0为违反约束. 针对违法约束, 不要设置成固定的惩罚值, 应该按照远离约束条件的程度来设定惩罚值, 这样后代才有可能进化; 满足约束, 可以直接设定一个定值.
- 为了保证结果一定满足约束, 方法有:
. 可以在算法结果solutionSet中过滤掉哪些不符合约束的solution
. 在将约束条件转换成目标函数.
. 对于违反约束的情况, 可在Problem 的 evaluate() 方法中, 主动设置 objective 为一个较差的结果.
代码解释:
- 定义问题:可以直接使用内置的 ZDT1 问题,它是一个经典的双目标优化问题, 我也自定义了两个简单的问题。
- 定义算法:使用 NSGA-II 算法,并设置种群大小和迭代次数。
- 设置交叉和变异算子:这里使用 SBX 交叉和多项式变异。
- 设置选择算子:使用二进制锦标赛选择。
- 运行算法:执行算法并记录执行时间。
- 打印结果:打印每个解的详细信息。
- 输出结果到文件:将结果输出到文件中。
package aaaaaaa;
import java.util.ArrayList;
import java.util.List;
import org.uma.jmetal.algorithm.Algorithm;
import org.uma.jmetal.algorithm.examples.AlgorithmRunner;
import org.uma.jmetal.algorithm.multiobjective.nsgaii.NSGAIIBuilder;
import org.uma.jmetal.operator.crossover.CrossoverOperator;
import org.uma.jmetal.operator.crossover.impl.SBXCrossover;
import org.uma.jmetal.operator.mutation.MutationOperator;
import org.uma.jmetal.operator.mutation.impl.PolynomialMutation;
import org.uma.jmetal.operator.selection.SelectionOperator;
import org.uma.jmetal.operator.selection.impl.BinaryTournamentSelection;
import org.uma.jmetal.problem.Problem;
import org.uma.jmetal.problem.doubleproblem.impl.AbstractDoubleProblem;
import org.uma.jmetal.problem.multiobjective.zdt.ZDT1;
import org.uma.jmetal.solution.doublesolution.DoubleSolution;
import org.uma.jmetal.util.AbstractAlgorithmRunner;
import org.uma.jmetal.util.ConstraintHandling;
import org.uma.jmetal.util.JMetalLogger;
import org.uma.jmetal.util.comparator.RankingAndCrowdingDistanceComparator;
import org.uma.jmetal.util.fileoutput.SolutionListOutput;
import org.uma.jmetal.util.fileoutput.impl.DefaultFileOutputContext;
public class App {
public static void main(String[] args) {
// 1. 定义问题
Problem<DoubleSolution> problem = null;
problem = new ZDT1();
problem = new TwoObjectiveProblem();
problem = new PortfolioProblem();
problem = new SquareDistanceProblem();
// 2. 设置交叉和变异算子 和 设置选择算子
// 定义交叉操作: SBX交叉
double crossoverProbability = 0.9;
double crossoverDistributionIndex = 20.0;
CrossoverOperator<DoubleSolution> crossover = new SBXCrossover(crossoverProbability,
crossoverDistributionIndex);
// 定义变异操作: 多项式变异
double mutationProbability = 1.0 / problem.numberOfVariables();
double mutationDistributionIndex = 20.0;
MutationOperator<DoubleSolution> mutation = new PolynomialMutation(mutationProbability,
mutationDistributionIndex);
// 定义选择操作: 二元竞标赛选择
SelectionOperator<List<DoubleSolution>, DoubleSolution> selection = new BinaryTournamentSelection<>(
new RankingAndCrowdingDistanceComparator<>());
// 3. 迭代次数和种群大小
int populationSize = 100;
// 4. 定义算法(NSGA-II)
// Algorithm<List<DoubleSolution>> algorithm = new NSGAIIBuilder<>(problem, crossover, mutation, populationSize)
// .setSelectionOperator(selection).setMaxEvaluations(25000).build();
Algorithm<List<DoubleSolution>> algorithm = new NSGAIIBuilder<>(problem, crossover, mutation, populationSize)
.setSelectionOperator(selection).setMaxEvaluations(25000).build();
// 5. 运行算法
AlgorithmRunner algorithmRunner = new AlgorithmRunner.Executor(algorithm).execute();
List<DoubleSolution> solutionSet = algorithm.result();
long computingTime = algorithmRunner.getComputingTime();
JMetalLogger.logger.info("Total execution time: " + computingTime + "ms");
// 6. 打印非支配排序结果,每个solution包含决策变量取值和目标函数取值.
for (DoubleSolution solution : solutionSet) {
JMetalLogger.logger.info("Solution: " + solution);
}
JMetalLogger.logger.info("Solution Count: " + solutionSet.size());
// 7. save to tsv files
new SolutionListOutput(solutionSet).setVarFileOutputContext(new DefaultFileOutputContext("VAR.csv", ","))
.setFunFileOutputContext(new DefaultFileOutputContext("FUN.csv", ",")).print();
AbstractAlgorithmRunner.printFinalSolutionSet(solutionSet);
}
}
/*
* 演示单目标函数, 多决策变量, 另外带有约束条件处理过程
*
*/
class SquareDistanceProblem extends AbstractDoubleProblem {
private int generationCount;
public SquareDistanceProblem() {
name("SquareDistance Problem");
numberOfObjectives(1); // 设置目标函数数量
numberOfConstraints(2);// 设置约束条件数量
double maxWidth = 998;
double maxHeight = 680;
Integer numberOfVariables = 4;
List<Double> lowerLimit = new ArrayList<>(numberOfVariables);
List<Double> upperLimit = new ArrayList<>(numberOfVariables);
// x1
lowerLimit.add(0.0);
upperLimit.add(maxWidth);
// y1
lowerLimit.add(0.0);
upperLimit.add(maxHeight);
// x2
lowerLimit.add(0.0);
upperLimit.add(maxWidth);
// y2
lowerLimit.add(0.0);
upperLimit.add(maxHeight);
variableBounds(lowerLimit, upperLimit);
}
/** Evaluate() method */
public DoubleSolution evaluate(DoubleSolution solution) {
generationCount = generationCount + 1;
System.out.println("Generation: " + generationCount);
double x1 = solution.variables().get(0);
double y1 = solution.variables().get(1);
double x2 = solution.variables().get(2);
double y2 = solution.variables().get(3);
double distance = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
double objective = -1 * distance;
solution.objectives()[0] = objective;
evaluateConstraints(solution);
System.out.println("Feasible: " + ConstraintHandling.isFeasible(solution));
// 当约束不满足时候, 调整objective为较差的值, 这样遗传算法为抛弃这一后代
if (ConstraintHandling.isFeasible(solution) == false) {
solution.objectives()[0] = 1000;
}
return solution;
}
/** EvaluateConstraints() method */
public void evaluateConstraints(DoubleSolution solution) {
JMetalLogger.logger.info("evaluateConstraints");
double x1 = solution.variables().get(0);
double y1 = solution.variables().get(1);
double x2 = solution.variables().get(2);
double y2 = solution.variables().get(3);
// https://jmetal.readthedocs.io/en/latest/constraints.html
double contraintX = x1 - x2; // 约束条件为x1-x2>=0, 如果<0被认为违反约束
solution.constraints()[0] = contraintX;// ;contraintX
double contraintY = y1 - y2; // 约束条件为y1-y2>=0, 如果<0被认为违反约束
solution.constraints()[1] = contraintY;// contraintY;
}
}
/*
* 定义两个目标函数的示例 单决策变量, 该优化会有很多个解
*/
class TwoObjectiveProblem extends AbstractDoubleProblem {
private int generationCount;
public TwoObjectiveProblem() {
name("TwoObjectiveProblem");
numberOfObjectives(2);// 设置目标函数数量
numberOfConstraints(2);// 设置约束条件数量
// Set variable bounds
List<Double> lowerLimit = new ArrayList<>();
lowerLimit.add(-5.0);
List<Double> upperLimit = new ArrayList<>();
upperLimit.add(5.0);
variableBounds(lowerLimit, upperLimit);
}
public DoubleSolution evaluate(DoubleSolution solution) {
generationCount = generationCount + 1;
System.out.println("Generation: " + generationCount);
double x = solution.variables().get(0);
solution.objectives()[0] = x * x; // f1 = x^2
solution.objectives()[1] = (x - 2) * (x - 2); // f2 = (x - 2)^2
evaluateConstraints(solution);
System.out.println("Feasible: " + ConstraintHandling.isFeasible(solution));
return solution;
}
/** EvaluateConstraints() method */
public void evaluateConstraints(DoubleSolution solution) {
JMetalLogger.logger.info("evaluateConstraints");
double x = solution.variables().get(0);
// https://jmetal.readthedocs.io/en/latest/constraints.html
// 设置约束条件为 1.5>x>1
double contraintX = -1;
if (x > 1 && x < 1.5) {
contraintX = 1; // OK
} else if (x <= 1) {
contraintX = -1 * (1 - x); // 惩罚
} else {
contraintX = -1 * (x - 1.5); // 惩罚
}
solution.constraints()[0] = contraintX;
}
}
/*
* 假设了三个资产的预期收益率分别为5%,7%,9%, 风险分别为1%,2%,3%。 在实际应用中,这些值通常需要从历史数据中计算得出。
*/
class PortfolioProblem extends AbstractDoubleProblem {
public PortfolioProblem() {
numberOfObjectives(2); // Minimize risk and maximize return
numberOfConstraints(1);
// Set variable bounds
// 3 stock share, 决策变量为它们在投资组合中的占比
List<Double> lowerLimit = new ArrayList<>();
lowerLimit.add(0.0);
lowerLimit.add(0.0);
lowerLimit.add(0.0);
List<Double> upperLimit = new ArrayList<>();
upperLimit.add(1.0);
upperLimit.add(1.0);
upperLimit.add(1.0);
variableBounds(lowerLimit, upperLimit);
}
public DoubleSolution evaluate(DoubleSolution solution) {
double[] x = new double[solution.variables().size()];
double sum = 0.0;
for (int i = 0; i < solution.variables().size(); i++) {
x[i] = solution.variables().get(i);
sum += x[i];
}
// 约束处理: 将投资比例标准化, Add the constraint that the sum of the weights must be 1
// 这里没有使用 solution.constraints() 来进行约束限定, 因为solution.constraints()不是强制约束
// 所以在变量层面做了归一化处理.
if (sum > 0) {
for (int i = 0; i < solution.variables().size(); i++) {
x[i] /= sum; // 标准化为比例
}
}
for (int i = 0; i < solution.variables().size(); i++) {
solution.variables().set(i, x[i]);
}
// Calculate the portfolio return and risk
// Example returns, 收益率分别为5%,7%,9%
double return_ = x[0] * 0.05 + x[1] * 0.07 + x[2] * 0.09;
// Example risks, 风险分别为1%,2%,3%
double risk_ = x[0] * 0.01 + x[1] * 0.02 + x[2] * 0.03;
// Set the objectives
solution.objectives()[0] = -1 * return_; // We want to maximize the return, so we minimize -return
solution.objectives()[1] = risk_; // We want to minimize the risk
if (sum != 1) {
solution.constraints()[0] = -100 * sum; // 惩罚
} else {
solution.constraints()[0] = 1; // OK
}
System.out.println("Feasible: " + ConstraintHandling.isFeasible(solution));
return solution;
}
}
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>sss</groupId>
<artifactId>aaaaaaa</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.uma.jmetal</groupId>
<artifactId>jmetal-core</artifactId>
<version>6.6</version>
</dependency>
<dependency>
<groupId>org.uma.jmetal</groupId>
<artifactId>jmetal-algorithm</artifactId>
<version>6.6</version>
</dependency>
<dependency>
<groupId>org.uma.jmetal</groupId>
<artifactId>jmetal-problem</artifactId>
<version>6.6</version>
</dependency>
<dependency>
<groupId>org.uma.jmetal</groupId>
<artifactId>jmetal-lab</artifactId>
<version>6.6</version>
</dependency>
</dependencies>
</project>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)