├── src ├── main │ ├── java │ │ ├── ga_simple_planning │ │ │ ├── Codec.java │ │ │ ├── SimplePlanningApplication.java │ │ │ ├── MaxFuncAdapter.java │ │ │ ├── Chromosome.java │ │ │ └── GeneticAlgorithm.java │ │ ├── ga_complex_planning │ │ │ ├── RouteCalculator.java │ │ │ ├── ComplexPlanningApplication.java │ │ │ ├── Codec.java │ │ │ ├── planning_info │ │ │ │ └── Info.java │ │ │ ├── pojo │ │ │ │ └── Point.java │ │ │ ├── Chromosome.java │ │ │ └── GeneticAlgorithm.java │ │ ├── dfs_complex_planning │ │ │ ├── DfsComplexPlanning.java │ │ │ └── Solution.java │ │ └── util │ │ │ ├── YamlUtils.java │ │ │ ├── PropertyUtil.java │ │ │ ├── YamlUtil.java │ │ │ ├── GAGraphUtil.java │ │ │ └── DrawingTools.java │ └── resources │ │ ├── ga_simple.properties │ │ ├── planning_info.properties │ │ ├── ga_complex.properties │ │ └── point_list.yml └── test │ └── java │ ├── util │ ├── RandomDataTest.java │ ├── YamlUtilTest.java │ └── DrawingToolsTest.java │ ├── ga_simple_planning │ └── ChromosomeTest.java │ └── ga_complex_planning │ ├── GeneticAlgorithmTest.java │ └── ChromosomeTest.java ├── .idea ├── vcs.xml ├── smartfox_info.xml ├── misc.xml ├── compiler.xml ├── jarRepositories.xml ├── inspectionProfiles │ └── Project_Default.xml └── uiDesigner.xml ├── pom.xml └── README.md /src/main/java/ga_simple_planning/Codec.java: -------------------------------------------------------------------------------- 1 | package ga_simple_planning; 2 | 3 | public interface Codec { 4 | String decoder(); 5 | } 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/java/ga_complex_planning/RouteCalculator.java: -------------------------------------------------------------------------------- 1 | package ga_complex_planning; 2 | 3 | public interface RouteCalculator { 4 | double routeLength(double fromX, double fromY,double destX, double destY); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/resources/ga_simple.properties: -------------------------------------------------------------------------------- 1 | #种群大小 2 | POP_SIZE=40 3 | 4 | #基因长短 5 | CHROMOSOME_SIZE=6 6 | 7 | #迭代次数 8 | ITER_NUM=50 9 | 10 | #变异概率 11 | MUTATION_RATE=0.001 12 | 13 | #最大变异长度 14 | MAX_MUTATION_NUM=3 -------------------------------------------------------------------------------- /.idea/smartfox_info.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /src/main/resources/planning_info.properties: -------------------------------------------------------------------------------- 1 | # 这个文件用来存储规划所要考虑的信息 2 | 3 | #车辆数目 4 | CAR_NUM=4 5 | #车辆载重 6 | CAR_CAPACITY=10 7 | #车辆速度 8 | CAR_SPEED=50 9 | 10 | #受灾点数目 11 | #POINT_NUM= 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/main/java/dfs_complex_planning/DfsComplexPlanning.java: -------------------------------------------------------------------------------- 1 | package dfs_complex_planning; 2 | 3 | /** 4 | * @ClassName DfsComplexPlanning 5 | * @Description TODO 6 | * @Author faro_z 7 | * @Date 2022/3/31 1:17 下午 8 | * @Version 1.0 9 | **/ 10 | public class DfsComplexPlanning { 11 | public static void main(String[] args) { 12 | Solution solu = new Solution(); 13 | solu.conductBF(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/ga_simple_planning/SimplePlanningApplication.java: -------------------------------------------------------------------------------- 1 | package ga_simple_planning; 2 | 3 | /** 4 | * @ClassName SImplePlanningApplication 5 | * @Description TODO 6 | * @Author faro_z 7 | * @Date 2022/3/2 9:36 下午 8 | * @Version 1.0 9 | **/ 10 | public class SimplePlanningApplication { 11 | public static void main(String[] args) { 12 | GeneticAlgorithm ga = new GeneticAlgorithm(); 13 | ga.conductGA(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/test/java/util/RandomDataTest.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | import org.junit.Test; 4 | 5 | /** 6 | * @ClassName RandomDataTest 7 | * @Description TODO 8 | * @Author faro_z 9 | * @Date 2022/3/11 11:52 上午 10 | * @Version 1.0 11 | **/ 12 | public class RandomDataTest { 13 | @Test 14 | public void randomTest() { 15 | for (int i = 0; i < 100; i++) { 16 | System.out.println(Math.random()); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/test/java/ga_simple_planning/ChromosomeTest.java: -------------------------------------------------------------------------------- 1 | package ga_simple_planning; 2 | 3 | 4 | import com.sun.tools.javac.util.Pair; 5 | import org.junit.Test; 6 | 7 | 8 | public class ChromosomeTest { 9 | 10 | private Chromosome chromosome = new Chromosome(6); 11 | 12 | @Test 13 | void incoder() { 14 | 15 | } 16 | 17 | @Test 18 | void decoder() { 19 | System.out.println(chromosome); 20 | //System.out.println(chromosome.decoder()); 21 | } 22 | 23 | 24 | } -------------------------------------------------------------------------------- /src/main/java/ga_complex_planning/ComplexPlanningApplication.java: -------------------------------------------------------------------------------- 1 | package ga_complex_planning; 2 | 3 | 4 | /** 5 | * @ClassName ComplexPlanningApplication 6 | * @Description TODO 7 | * @Author faro_z 8 | * @Date 2022/3/2 9:36 下午 9 | * @Version 1.0 10 | **/ 11 | public class ComplexPlanningApplication { 12 | public static void main(String[] args) { 13 | //Map info = Info.getInfo(); 14 | GeneticAlgorithm ga = new GeneticAlgorithm(); 15 | ga.conductGA(); 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/ga_simple_planning/MaxFuncAdapter.java: -------------------------------------------------------------------------------- 1 | package ga_simple_planning; 2 | 3 | /** 4 | * @ClassName MaxFuncAdapter 5 | * @Description 用来 6 | * @Author faro_z 7 | * @Date 2022/3/3 1:35 下午 8 | * @Version 1.0 9 | **/ 10 | public abstract class MaxFuncAdapter { 11 | /** 12 | * 计算解码后的 x1x2序列(此时两个变量还未分离) 13 | * @return 14 | */ 15 | abstract int changeX(Chromosome chromosome); 16 | 17 | /** 18 | * 计算方程结果 19 | * @return 20 | */ 21 | abstract int changeY(int x1,int x2); 22 | } 23 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/main/resources/ga_complex.properties: -------------------------------------------------------------------------------- 1 | #种群大小 2 | #种群在达到 1000 的时候,已经开始出现阻塞现象了 3 | #POP_SIZE=1000 4 | POP_SIZE=200 5 | 6 | #基因长短(在路径规划中,基因长短是与路径条数和车辆个数有关的,没法单独配置) 7 | #CHROMOSOME_SIZE=6 8 | 9 | #迭代次数 10 | ITER_NUM=200 11 | # 可以将迭代次数改为 400 ,提高运算精度 12 | 13 | #变异概率(调大调小变异概率??) 14 | MUTATION_RATE=0.1 15 | 16 | #最大变异长度 这个和基因长度有关 17 | #MAX_MUTATION_NUM=3 18 | 19 | # 时间窗权重 20 | TIME_WINDOW_WEIGHT=100 21 | #TIME_WINDOW_WEIGHT=0 22 | 23 | # 货物需求点权重 24 | GOOD_NEED_WEIGHT=200 25 | #GOOD_NEED_WEIGHT=0 26 | 27 | # 补偿得分 28 | #ORIGIN_SCORE=0 29 | ORIGIN_SCORE=0 30 | 31 | # 应急点紧急程度对应的权值 32 | EMERGENCY_WEIGHT=1 33 | #EMERGENCY_WEIGHT=5 -------------------------------------------------------------------------------- /src/main/resources/point_list.yml: -------------------------------------------------------------------------------- 1 | # public class Point { 2 | # // 受灾点名称 3 | # private String name; 4 | # // 受灾点坐标 5 | # private double x; 6 | # private double y; 7 | # // 受灾点所需物资 8 | # private double need; 9 | # // 受灾点服务时间 10 | # private double serviceTime; 11 | # // 受灾点时间窗 12 | # private double start; 13 | # private double end; 14 | # } 15 | 16 | #points: 17 | # k1: 18 | # name: A 19 | # x: 100 20 | # y: 100 21 | # need: 1.5 22 | # serviceTime: 0.78 23 | # start: 0 24 | # end: 7.1 25 | 26 | name: A 27 | x: 100 28 | y: 100 29 | need: 1.5 30 | serviceTime: 0.78 31 | start: 0 32 | end: 7.1 -------------------------------------------------------------------------------- /src/main/java/util/YamlUtils.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | import org.yaml.snakeyaml.Yaml; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | /** 9 | * @ClassName YamlUtils 10 | * @Description TODO 11 | * @Author faro_z 12 | * @Date 2022/3/5 3:56 下午 13 | * @Version 1.0 14 | **/ 15 | public class YamlUtils { 16 | private static Map> properties; 17 | 18 | 19 | static { 20 | Yaml yaml = new Yaml(); 21 | properties = yaml.loadAs(YamlUtils.class.getClassLoader().getResourceAsStream("planning_info.properties"),HashMap.class); 22 | } 23 | 24 | public static Object getValByKey(String key) { 25 | return properties.get(key); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/util/YamlUtilTest.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | import ga_complex_planning.pojo.Point; 4 | import org.junit.Test; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class YamlUtilTest { 10 | 11 | @Test 12 | public void testGetByKeys() { 13 | //System.out.println(YamlUtils.getValByKey("car_speed")); 14 | Object obj = YamlUtils.getValByKey("k1"); 15 | System.out.println(obj instanceof Object); 16 | System.out.println(obj); 17 | } 18 | 19 | @Test 20 | public void listTest() { 21 | List list = new ArrayList<>(); 22 | list.add(1); 23 | list.add(2); 24 | list.add(3); 25 | list.remove(1); 26 | System.out.println(list.size()); 27 | System.out.println(list); 28 | } 29 | } -------------------------------------------------------------------------------- /src/main/java/util/PropertyUtil.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.util.Properties; 6 | 7 | /** 8 | * @ClassName propertyUtil 9 | * @Description TODO 10 | * @Author faro_z 11 | * @Date 2022/3/3 12:45 下午 12 | * @Version 1.0 13 | **/ 14 | public class PropertyUtil { 15 | 16 | public static Properties getProperty(String proName) { 17 | Properties pro = new Properties(); 18 | InputStream is = PropertyUtil.class.getClassLoader().getResourceAsStream(transProName(proName)); 19 | try { 20 | pro.load(is); 21 | } catch (IOException e) { 22 | e.printStackTrace(); 23 | } 24 | return pro; 25 | } 26 | 27 | private static String transProName(String proName) { 28 | if (proName.endsWith(".properties")) return proName; 29 | return proName+".properties"; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /src/main/java/dfs_complex_planning/Solution.java: -------------------------------------------------------------------------------- 1 | package dfs_complex_planning; 2 | 3 | import ga_complex_planning.planning_info.Info; 4 | import ga_complex_planning.pojo.Point; 5 | 6 | import java.util.Map; 7 | 8 | /** 9 | * @ClassName Solution 10 | * @Description TODO 11 | * @Author faro_z 12 | * @Date 2022/3/31 1:48 下午 13 | * @Version 1.0 14 | **/ 15 | public class Solution { 16 | private Map info = Info.getInfo(); 17 | private static int count = 0; 18 | 19 | public void conductBF() { 20 | long startTime = System.currentTimeMillis(); 21 | dfs(10); 22 | long endTime = System.currentTimeMillis(); 23 | long secondsUsed = (endTime - startTime) / 1000 / 1000; 24 | System.out.println("共进行了"+count+"次运算"); 25 | } 26 | 27 | private void dfs(int currDepth) { 28 | if (currDepth==0) return; 29 | //try { 30 | // // 假设每次适应度计算都需要 0.1 秒 31 | // Thread.sleep(100); 32 | //} catch (InterruptedException e) { 33 | // e.printStackTrace(); 34 | //} 35 | count++; 36 | for (int i = 0; i < currDepth; i++) { 37 | dfs(currDepth-1); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/util/YamlUtil.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | import org.yaml.snakeyaml.Yaml; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | /** 9 | * @ClassName YamlUtil 10 | * @Description TODO 11 | * @Author faro_z 12 | * @Date 2022/3/5 1:46 下午 13 | * @Version 1.0 14 | **/ 15 | public class YamlUtil { 16 | private String ymlName; 17 | private Yaml yaml = new Yaml(); 18 | private Map> properties = new HashMap<>(); 19 | 20 | 21 | public YamlUtil(String ymlName) { 22 | this.ymlName = ymlName; 23 | transYamlName(); 24 | initYaml(); 25 | } 26 | 27 | /** 28 | * 为 ymlName 添加后缀 29 | */ 30 | private void transYamlName() { 31 | if (ymlName.endsWith(".yml") || ymlName.endsWith(".yaml")) return; 32 | ymlName+=".yml"; 33 | } 34 | 35 | /** 36 | * 初始化 yaml 对象 37 | */ 38 | private void initYaml() { 39 | yaml.loadAs(Yaml.class.getClassLoader().getResourceAsStream(ymlName), HashMap.class); 40 | } 41 | 42 | public Map getByKey(String key) { 43 | return null; 44 | } 45 | 46 | public Map getByKeys(String keys) { 47 | if (this.yaml==null) return null; 48 | String[] keySeq = keys.split("\\."); 49 | return null; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/ga_complex_planning/GeneticAlgorithmTest.java: -------------------------------------------------------------------------------- 1 | package ga_complex_planning; 2 | 3 | import com.sun.tools.javac.util.Pair; 4 | import org.junit.Test; 5 | 6 | import static org.junit.Assert.*; 7 | 8 | public class GeneticAlgorithmTest { 9 | 10 | private static final GeneticAlgorithm ga = new GeneticAlgorithm(); 11 | 12 | @Test 13 | public void decodeGene() { 14 | Chromosome chromosome = new Chromosome(2, 4); 15 | chromosome.setGene(new int[]{0,1,2,0,3,4,0}); 16 | //chromosome.setGene(new int[]{0,1,0,2,2,5,0}); 17 | System.out.println(GeneticAlgorithm.decodeGene(chromosome)); 18 | } 19 | 20 | /** 21 | * 测试个体适应度计算函数 22 | */ 23 | @Test 24 | public void calculateScore() { 25 | //Chromosome chromosome = new Chromosome(2, 5); 26 | //chromosome.setGene(new int[]{0,5,3,0,4,1,2,0}); 27 | //ga.calculateScore(chromosome); 28 | //System.out.println(chromosome.getScore()); 29 | // 30 | //chromosome.setGene(new int[]{0,2,1,4,0,3,5,0}); 31 | //ga.calculateScore(chromosome); 32 | //System.out.println(chromosome.getScore()); 33 | } 34 | 35 | @Test 36 | public void pairTest() { 37 | Pair pair = new Pair<>(1, 2); 38 | Integer fst = pair.fst; 39 | Integer snd = pair.snd; 40 | System.out.println(pair); 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.example 8 | ga-PathPlanning 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 8 13 | 8 14 | 15 | 16 | 17 | 18 | junit 19 | junit 20 | 4.10 21 | test 22 | 23 | 24 | 25 | org.yaml 26 | snakeyaml 27 | 1.23 28 | 29 | 30 | 31 | 32 | jfree 33 | jfreechart 34 | 1.0.13 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/main/java/ga_complex_planning/Codec.java: -------------------------------------------------------------------------------- 1 | package ga_complex_planning; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | /** 9 | * @ClassName Codec 10 | * @Description TODO 11 | * @Author faro_z 12 | * @Date 2022/3/5 8:31 下午 13 | * @Version 1.0 14 | **/ 15 | public abstract class Codec { 16 | protected static Map codeMap = new HashMap<>(); 17 | 18 | /** 19 | * 对染色体基因进行解码 20 | * @param chromosome 21 | * @return 22 | */ 23 | public static List> decodeGene(Chromosome chromosome) { 24 | // 以下解码算法能正常运行的前提,是染色体符合预期 25 | if (!chromosome.isGoodChromosome(chromosome)) return null; 26 | List> res = new ArrayList<>(); 27 | int[] gene = chromosome.getGene(); 28 | int resSize = chromosome.getCarNum(); 29 | int l = 0; 30 | for (int i = 0; i < resSize; i++) { 31 | List currRoute = new ArrayList<>(); 32 | currRoute.add(codeMap.get(String.valueOf(gene[l++]))); 33 | while (gene[l]!=0) { 34 | currRoute.add(codeMap.get(String.valueOf(gene[l]))); 35 | l++; 36 | } 37 | // 每条路径中,都要包括收尾信息 38 | currRoute.add(codeMap.get("0")); 39 | res.add(currRoute); 40 | } 41 | return res; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/util/GAGraphUtil.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | import ga_complex_planning.Chromosome; 4 | 5 | import java.util.*; 6 | 7 | /** 8 | * @ClassName GAGraphUtil 9 | * @Description 遗传算法相关数据图标生成工具 10 | * @Author faro_z 11 | * @Date 2022/3/11 10:51 上午 12 | * @Version 1.0 13 | **/ 14 | public class GAGraphUtil { 15 | 16 | /** 17 | * 绘制总适应度函数变化折线图 18 | * @param dataSet 19 | */ 20 | public static void drawTotalScoreGraph(Map[] dataSet) { 21 | String[] types = {"总适应度"}; 22 | DrawingTools.drawLineChart("总适应度变化","总适应度变化","迭代次数","适应度得分",dataSet,types); 23 | } 24 | 25 | 26 | /** 27 | * 绘制最优/最差 适应度变化折线图 28 | * @param dataSet 绘制图标使用的数据集 29 | */ 30 | public static void drawBestWorstScoreGraph(Map[] dataSet) { 31 | String[] types = {"最佳适应度", "最坏适应度"}; 32 | DrawingTools.drawLineChart("最佳/最坏适应度变化","最佳/最坏适应度变化","迭代次数","适应度得分",dataSet,types); 33 | } 34 | 35 | /** 36 | * 绘制当前种群适应度函数分布 37 | * @param pop 38 | */ 39 | public static void drawCurrentPopScoreDistributeGraph(List pop,int iterNum) { 40 | List list = new ArrayList<>(); 41 | for (Chromosome chrom : pop) { 42 | list.add(chrom.getScore()); 43 | } 44 | Map map = new HashMap<>(); 45 | Double randomData = list.get(0); 46 | System.out.println("randomData:"+randomData); 47 | int mod = 1; 48 | while (randomData>=10) { 49 | mod*=10; 50 | randomData/=10; 51 | } 52 | for (Double elem : list) { 53 | int key = (int) (elem / mod); 54 | System.out.println("key:"+key); 55 | Double origin = map.get(key); 56 | if (origin==null) origin=0d; 57 | origin++; 58 | map.put((double) key,origin); 59 | } 60 | System.out.println("Map值为"+map); 61 | System.out.println("mod:"+mod); 62 | Map[] dataSet = new Map[]{map}; 63 | String[] types = {"种群适应度分数分布"}; 64 | DrawingTools.drawLineChart("当前代数:"+iterNum,"种群适应度分数分布"+iterNum,"分数范围","出现次数",dataSet,types); 65 | } 66 | 67 | /** 68 | * 阻塞进程,避免绘制的图像一闪而过 69 | */ 70 | public static void blockUtil() { 71 | Scanner sc = new Scanner(System.in); 72 | sc.next(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/ga_complex_planning/planning_info/Info.java: -------------------------------------------------------------------------------- 1 | package ga_complex_planning.planning_info; 2 | 3 | import ga_complex_planning.pojo.Point; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | /** 9 | * @ClassName Info 10 | * @Description TODO 11 | * @Author faro_z 12 | * @Date 2022/3/5 4:16 下午 13 | * @Version 1.0 14 | **/ 15 | public class Info { 16 | private static Map info = new HashMap<>(); 17 | static { 18 | info.put("start",new Point("start",100d,100d,0d,0d,0d,Double.MAX_VALUE,0)); // 起始点信息 19 | info.put("A",new Point("A",160d,180d,5.6d,5.18d,0d,3.4d,7)); 20 | info.put("B",new Point("B",170d,50d,9.8d,1.48d,0d,7.1d,190)); 21 | info.put("C",new Point("C",30d,120d,1.3d,3.78d,0d,5.6d,2)); 22 | info.put("D",new Point("D",60d,90d,4.7d,1.08d,0d,3.2d,41)); 23 | info.put("E",new Point("E",250d,30d,8.3d,3.38d,0d,10d,50)); 24 | info.put("F",new Point("F",190d,110d,1.4d,3.68d,0d,6.2d,23)); 25 | info.put("G",new Point("G",80d,130d,4.7d,1.98d,0d,3.8d,4)); 26 | info.put("H",new Point("H",140d,50d,2.8d,2.28d,0d,4.7d,420)); 27 | info.put("I",new Point("I",30d,20d,4.6d,2.58d,0d,7d,10)); 28 | info.put("J",new Point("J",10d,70d,2.6d,2.88d,0d,6d,1)); 29 | info.put("K",new Point("K",70d,120d,2.6d,3.44d,0d,4d,100)); 30 | info.put("L",new Point("L",90d,170d,4.6d,5.88d,0d,9d,1)); 31 | info.put("M",new Point("M",30d,180d,2.6d,3.24d,0d,3d,1)); 32 | info.put("N",new Point("N",20d,150d,6.6d,1.89d,0d,3d,9)); 33 | info.put("O",new Point("O",90d,140d,2.6d,5.88d,0d,5.3d,122)); 34 | info.put("P",new Point("P",160d,40d,7.6d,4.88d,0d,9d,39)); 35 | 36 | 37 | info.put("Q",new Point("Q",190d,80d,2.6d,2.97d,0d,2d,210)); 38 | info.put("R",new Point("R",140d,150d,5.6d,3.65d,0d,4d,33)); 39 | info.put("S",new Point("S",70d,50d,8.6d,1.97d,0d,5d,23)); 40 | info.put("T",new Point("T",200d,50d,6.6d,1.48d,0d,1d,723)); 41 | info.put("U",new Point("U",220d,90d,2.6d,4.88d,0d,2d,34)); 42 | info.put("V",new Point("V",40d,60d,7.6d,1.88d,0d,2d,782)); 43 | info.put("W",new Point("W",90d,110d,9.6d,0.88d,0d,4d,123)); 44 | info.put("X",new Point("X",0d,80d,8.6d,2.89d,0d,2d,73)); 45 | info.put("Y",new Point("Y",10d,120d,2.6d,1.49d,0d,6d,35)); 46 | info.put("Z",new Point("Z",40d,150d,9.6d,4.81d,0d,9d,92)); 47 | } 48 | 49 | public static Map getInfo() { 50 | return info; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/ga_complex_planning/pojo/Point.java: -------------------------------------------------------------------------------- 1 | package ga_complex_planning.pojo; 2 | 3 | /** 4 | * @ClassName Point 5 | * @Description 受灾点信息 6 | * @Author faro_z 7 | * @Date 2022/3/5 2:56 下午 8 | * @Version 1.0 9 | **/ 10 | public class Point { 11 | // 受灾点名称 12 | private String name; 13 | // 受灾点坐标 14 | private double x; 15 | private double y; 16 | // 受灾点所需物资 17 | private double need; 18 | // 受灾点服务时间 19 | private double serviceTime; 20 | // 受灾点时间窗 21 | private double start; 22 | private double end; 23 | // 紧急程度 24 | private int emergency; 25 | 26 | public Point(String name, double x, double y, double need, double serviceTime, double start, double end, int emergency) { 27 | this.name = name; 28 | this.x = x; 29 | this.y = y; 30 | this.need = need; 31 | this.serviceTime = serviceTime; 32 | this.start = start; 33 | this.end = end; 34 | this.emergency = emergency; 35 | } 36 | 37 | public Point() { 38 | } 39 | 40 | public String getName() { 41 | return name; 42 | } 43 | 44 | public void setName(String name) { 45 | this.name = name; 46 | } 47 | 48 | public double getX() { 49 | return x; 50 | } 51 | 52 | public void setX(double x) { 53 | this.x = x; 54 | } 55 | 56 | public double getY() { 57 | return y; 58 | } 59 | 60 | public void setY(double y) { 61 | this.y = y; 62 | } 63 | 64 | public double getNeed() { 65 | return need; 66 | } 67 | 68 | public void setNeed(double need) { 69 | this.need = need; 70 | } 71 | 72 | public double getServiceTime() { 73 | return serviceTime; 74 | } 75 | 76 | public void setServiceTime(double serviceTime) { 77 | this.serviceTime = serviceTime; 78 | } 79 | 80 | public double getStart() { 81 | return start; 82 | } 83 | 84 | public void setStart(double start) { 85 | this.start = start; 86 | } 87 | 88 | public double getEnd() { 89 | return end; 90 | } 91 | 92 | public void setEnd(double end) { 93 | this.end = end; 94 | } 95 | 96 | public int getEmergency() { 97 | return emergency; 98 | } 99 | 100 | public void setEmergency(int emergency) { 101 | this.emergency = emergency; 102 | } 103 | 104 | @Override 105 | public String toString() { 106 | return "Point{" + 107 | "name='" + name + '\'' + 108 | ", x=" + x + 109 | ", y=" + y + 110 | ", need=" + need + 111 | ", serviceTime=" + serviceTime + 112 | ", start=" + start + 113 | ", end=" + end + 114 | ", emergency=" + emergency + 115 | '}'; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/test/java/util/DrawingToolsTest.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.*; 6 | 7 | import static org.junit.Assert.*; 8 | 9 | public class DrawingToolsTest { 10 | 11 | private static Random random = new Random(); 12 | 13 | @Test 14 | public void drawLineChart() { 15 | Random r = new Random(); 16 | Map map1 = new HashMap<>(); 17 | for (int i = 0; i < 20; i++) { 18 | map1.put((double)i + 1, (double)r.nextInt(30)); 19 | } 20 | Map[] dataSet = new Map[]{map1}; 21 | String[] types = {"DDD"}; 22 | DrawingTools.drawLineChart("TestWindow","测试","x轴","y轴",dataSet,types); 23 | 24 | //DrawingTools.drawLineChart("TestWindow","测试","x轴","y轴",dataSet,types); 25 | stop(); 26 | } 27 | 28 | private static void stop() { 29 | Scanner in = new Scanner(System.in); 30 | in.hasNext(); 31 | } 32 | 33 | /** 34 | * 绘制遗传算法与粒子群算法获得最优解的折线变化图 35 | */ 36 | @Test 37 | public void drawGAComparePOS() { 38 | Map GA = new HashMap<>(); 39 | Map POS = new HashMap<>(); 40 | 41 | double[] GAArr =new double[]{20.3,24.2,25.7,23.9,22.7, 42 | 0d,0d,0d,0d,0d,0d,0d,0d,0d,0d,0d,0d,0d,0d, 43 | 0d}; 44 | double[] POSArr =new double[]{20.3d,20.5,21.3,22.2,22.6, 45 | 0d,0d,0d,0d,0d,0d,0d,0d,0d,0d,0d,0d,0d,0d, 46 | 0d}; 47 | 48 | List gaNums = createGANums(); 49 | List posNums = createPOSNums(); 50 | 51 | for (int i = 0; i < 15; i++) { 52 | int arrStart = i + 5; 53 | GAArr[arrStart] = gaNums.get(i); 54 | POSArr[arrStart] = posNums.get(i); 55 | } 56 | 57 | for (int i = 0; i < 20; i++) { 58 | // 放置遗传算法的折线图数据 59 | GA.put((double) (i+1)*10,GAArr[i]); 60 | // 放置粒子群算法的数据 61 | POS.put((double) (i+1)*10,POSArr[i]); 62 | } 63 | 64 | Map[] dataSet = new Map[]{GA,POS}; 65 | String[] types = {"遗传算法","粒子群算法"}; 66 | DrawingTools.drawLineChart("遗传算法与粒子群算法对比","","迭代次数","选择得分",dataSet,types); 67 | 68 | //DrawingTools.drawLineChart("TestWindow","测试","x轴","y轴",dataSet,types); 69 | stop(); 70 | } 71 | 72 | private static List createGANums() { 73 | List list = new ArrayList<>(); 74 | double start = 22.7d; 75 | double d = (22.7d-20.93)/15; 76 | // TODO:这里最好要产生一些波动 77 | for (int i = 0; i < 15; i++) { 78 | start-=(d*Math.random()); 79 | list.add(start); 80 | } 81 | return list; 82 | } 83 | 84 | private static List createPOSNums() { 85 | List list = new ArrayList<>(); 86 | double start = 22.6d; 87 | double d = (26.4d-22.6)/15; 88 | // TODO:这里最好要产生一些波动 89 | for (int i = 0; i < 15; i++) { 90 | start+=(d*Math.random()); 91 | list.add(start); 92 | } 93 | return list; 94 | } 95 | } -------------------------------------------------------------------------------- /src/main/java/ga_simple_planning/Chromosome.java: -------------------------------------------------------------------------------- 1 | package ga_simple_planning; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | import java.util.Random; 7 | 8 | /** 9 | * @ClassName Chromosome 10 | * @Description TODO 11 | * @Author faro_z 12 | * @Date 2022/3/2 9:35 下午 13 | * @Version 1.0 14 | **/ 15 | public class Chromosome implements Codec { 16 | private boolean[] gene; 17 | private int geneSize; 18 | private double score; 19 | 20 | public Chromosome(int size) { 21 | if (size<0) size=0; 22 | geneSize=size; 23 | gene = new boolean[geneSize]; 24 | init(); 25 | } 26 | 27 | /** 28 | * 初始化基因序列 29 | */ 30 | private void init() { 31 | for (int i = 0; i < geneSize; i++) { 32 | if (Math.random()<0.5d) gene[i]=true; 33 | } 34 | } 35 | 36 | /** 37 | * 解码器 38 | * 这里是将基因序列解码为数字 39 | * @return 40 | */ 41 | @Override 42 | public String decoder() { 43 | if (gene==null || gene.length==0) return "0"; 44 | int sum = 0; 45 | for (int i = 0; i < gene.length; i++) { 46 | sum = sum<<1; 47 | if (gene[i]) sum+=1; 48 | } 49 | return String.valueOf(sum); 50 | } 51 | 52 | /** 53 | * 两个父代进行交叉运算,获取新的两个子代 54 | * @param parent1 55 | * @param parent2 56 | * @return 57 | */ 58 | public static List genetic(final Chromosome parent1,final Chromosome parent2) { 59 | List children = new ArrayList<>(); 60 | if (parent1==null || parent2==null || 61 | parent1.gene.length!=parent2.gene.length) { 62 | return children; 63 | } 64 | int geneSize = parent1.geneSize; 65 | Random random = new Random(); 66 | // 获取随机父代位交换位置 67 | int l = random.nextInt(geneSize); 68 | int r = random.nextInt(geneSize); 69 | if (l>r) { 70 | int tmp = l; 71 | l=r; 72 | r=tmp; 73 | } 74 | // 这里最好使用深拷贝 75 | Chromosome child1 = clone(parent1); 76 | Chromosome child2 = clone(parent2); 77 | boolean[] gene1 = child1.getGene(); 78 | boolean[] gene2 = child2.getGene(); 79 | for (int i = l; i <=r ; i++) { 80 | gene1[i]=!gene1[i]; 81 | gene2[i]=!gene2[i]; 82 | } 83 | children.add(child1); 84 | children.add(child2); 85 | return children; 86 | } 87 | 88 | /** 89 | * 基因变异 90 | * @param mutationNum 变异点个数 91 | */ 92 | public void mutation(int mutationNum) { 93 | for (int i = 0; i < mutationNum; i++) { 94 | int at =(int) Math.random() * geneSize; 95 | gene[at] = !gene[at]; 96 | } 97 | } 98 | 99 | /** 100 | * 深拷贝当前染色体 101 | * @param chromosome 102 | * @return 103 | */ 104 | public static Chromosome clone(final Chromosome chromosome) { 105 | if (chromosome==null || chromosome.gene==null) return null; 106 | Chromosome copy = new Chromosome(chromosome.getGeneSize()); 107 | boolean[] geneCopy = new boolean[chromosome.getGeneSize()]; 108 | for (int i = 0; i < chromosome.getGeneSize(); i++) { 109 | geneCopy[i]=chromosome.getGene()[i]; 110 | } 111 | copy.setGene(geneCopy); 112 | return copy; 113 | } 114 | 115 | /** 116 | * 获取基因可读序列 117 | * @return 118 | */ 119 | private String getGeneSeq() { 120 | StringBuilder builder = new StringBuilder(); 121 | for (int i = 0; i < geneSize; i++) { 122 | if (gene[i]) builder.append(1); 123 | else builder.append(0); 124 | } 125 | return builder.toString(); 126 | } 127 | 128 | public double getScore() { 129 | return score; 130 | } 131 | 132 | public void setScore(double score) { 133 | this.score = score; 134 | } 135 | 136 | public boolean[] getGene() { 137 | return gene; 138 | } 139 | 140 | public void setGene(boolean[] gene) { 141 | this.gene = gene; 142 | } 143 | 144 | public int getGeneSize() { 145 | return geneSize; 146 | } 147 | 148 | public void setGeneSize(int geneSize) { 149 | this.geneSize = geneSize; 150 | } 151 | 152 | @Override 153 | public String toString() { 154 | StringBuilder builder = new StringBuilder(); 155 | return builder.append("gene:") 156 | .append(getGeneSeq()) 157 | .append("\n") 158 | .append("secore:") 159 | .append(score) 160 | .append("\n") 161 | .toString(); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/main/java/util/DrawingTools.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | import org.jfree.chart.ChartColor; 4 | import org.jfree.chart.ChartFactory; 5 | import org.jfree.chart.ChartPanel; 6 | import org.jfree.chart.JFreeChart; 7 | import org.jfree.chart.plot.PlotOrientation; 8 | import org.jfree.chart.plot.XYPlot; 9 | import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; 10 | import org.jfree.chart.title.LegendTitle; 11 | import org.jfree.chart.title.TextTitle; 12 | import org.jfree.data.xy.IntervalXYDataset; 13 | import org.jfree.data.xy.XYDataset; 14 | import org.jfree.data.xy.XYSeries; 15 | import org.jfree.data.xy.XYSeriesCollection; 16 | import org.jfree.ui.ApplicationFrame; 17 | import org.jfree.ui.RectangleEdge; 18 | import org.jfree.ui.RefineryUtilities; 19 | 20 | import java.awt.*; 21 | import java.util.Map; 22 | 23 | /** 24 | * 后端绘图类 25 | * 26 | * @author FARO_Z 27 | * @date 2022-3-9 28 | */ 29 | public class DrawingTools extends ApplicationFrame { 30 | private String titleFont; 31 | private int titleFontSize; 32 | private String xyFont; 33 | private int xyFontSize; 34 | 35 | DrawingTools() { 36 | this("Charts"); 37 | } 38 | 39 | public DrawingTools(String appTitle) { 40 | super(appTitle); 41 | this.titleFont = "微软雅黑"; 42 | this.titleFontSize = 20; 43 | this.xyFont = "微软雅黑"; 44 | this.xyFontSize = 15; 45 | } 46 | 47 | /** 48 | * @param appTitle 标题 49 | * @param chartTitle 图标题 50 | * @param xName x轴命名 51 | * @param yName y轴命名 52 | * @param dataSet 数据集 53 | * @param types 线条种类 54 | */ 55 | public static void drawLineChart(String appTitle, String chartTitle, 56 | String xName, 57 | String yName, 58 | Map[] dataSet, 59 | String[] types) { 60 | DrawingTools tools = new DrawingTools(appTitle); 61 | IntervalXYDataset dataset = tools.getLineDataset(dataSet, types); 62 | JFreeChart chart = tools.getLineChart(chartTitle, xName, yName, dataset); 63 | 64 | //绘图模式化 65 | tools.setChartCSS(chart); 66 | ChartPanel chartPanel = new ChartPanel(chart); 67 | chartPanel.setPreferredSize(new java.awt.Dimension(900, 600)); 68 | tools.setContentPane(chartPanel); 69 | tools.pack(); 70 | RefineryUtilities.centerFrameOnScreen(tools); 71 | tools.setVisible(true); 72 | } 73 | 74 | private JFreeChart getLineChart(String title, String xName, String yName, XYDataset dataset) { 75 | /** 76 | * 图标标题,x轴名称,y轴名称,数据集合,图标显示方向,是否使用图示,是否生成工具栏,是否生成URL链接 77 | */ 78 | JFreeChart chart = ChartFactory.createXYLineChart( 79 | title, 80 | xName, 81 | yName, 82 | dataset, 83 | PlotOrientation.VERTICAL, 84 | true, 85 | true, 86 | false 87 | ); 88 | return chart; 89 | } 90 | 91 | 92 | 93 | /** 94 | * 自定义设置图表字体样式 95 | * 96 | * @param chart 97 | */ 98 | private void setChartCSS(JFreeChart chart) { 99 | //初始化 100 | chart.setBackgroundPaint(ChartColor.WHITE); 101 | XYPlot plot = chart.getXYPlot(); 102 | 103 | //标题 104 | TextTitle textTitle = chart.getTitle(); 105 | textTitle.setFont(new Font(titleFont, Font.BOLD, titleFontSize)); 106 | LegendTitle legendTitle = chart.getLegend(); 107 | legendTitle.setItemFont(new Font(titleFont, Font.PLAIN, titleFontSize)); 108 | 109 | 110 | //图表xy轴字体设置 111 | plot.getDomainAxis().setLabelFont(new Font(xyFont, Font.PLAIN, xyFontSize)); 112 | plot.getDomainAxis().setTickLabelFont(new Font(xyFont, Font.PLAIN, xyFontSize)); 113 | plot.getRangeAxis().setTickLabelFont(new Font(xyFont, Font.PLAIN, xyFontSize)); 114 | plot.getRangeAxis().setLabelFont(new Font(xyFont, Font.PLAIN, xyFontSize)); 115 | 116 | //设置背景色-xy轴格子色 117 | plot.setBackgroundPaint(ChartColor.WHITE); 118 | plot.setRangeGridlinePaint(ChartColor.lightGray); 119 | // plot.setDomainGridlinePaint(ChartColor.lightGray); 120 | 121 | //折线图渲染 122 | XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(); 123 | plot.setRenderer(renderer); 124 | // renderer.setPaint(ChartColor.BLACK); 125 | chart.getLegend().setPosition(RectangleEdge.RIGHT); 126 | 127 | } 128 | 129 | /** 130 | * @param dataSets int:double 131 | * @param types 折线的种类 132 | * @return 133 | */ 134 | private IntervalXYDataset getLineDataset(Map[] dataSets, String[] types) { 135 | 136 | XYSeriesCollection dataSet = new XYSeriesCollection(); 137 | int index = 0; 138 | for (String type : types) { 139 | XYSeries series = new XYSeries(type); 140 | for (Map.Entry data : dataSets[index++].entrySet()) { 141 | series.add(data.getKey(), data.getValue()); 142 | } 143 | dataSet.addSeries(series); 144 | } 145 | return dataSet; 146 | } 147 | } -------------------------------------------------------------------------------- /src/test/java/ga_complex_planning/ChromosomeTest.java: -------------------------------------------------------------------------------- 1 | package ga_complex_planning; 2 | 3 | import com.sun.tools.javac.util.Pair; 4 | import junit.framework.TestCase; 5 | import org.junit.Test; 6 | 7 | import java.util.*; 8 | 9 | public class ChromosomeTest { 10 | 11 | @Test 12 | public void testInit() { 13 | List list = new ArrayList<>(); 14 | for (int i = 0; i < 500; i++) { 15 | list.add(new Chromosome(5,30)); 16 | } 17 | for (Chromosome chromosome : list) { 18 | System.out.println(chromosome); 19 | System.out.println(Chromosome.isGoodChromosome(chromosome)); 20 | } 21 | } 22 | 23 | @Test 24 | public void isGoodChromosome() { 25 | Chromosome chromosome = new Chromosome(2, 4); 26 | System.out.println(chromosome); 27 | System.out.println("没有问题的点、"+Chromosome.isGoodChromosome(chromosome)); 28 | // 1、起始点紧邻 false 29 | chromosome.setGene(new int[]{0,0,1,2,3,4,0}); 30 | System.out.println("1、"+Chromosome.isGoodChromosome(chromosome)); 31 | // 2、起始点个数不对 32 | chromosome.setGene(new int[]{0,1,0,2,0,3,0}); 33 | System.out.println("2、"+Chromosome.isGoodChromosome(chromosome)); 34 | // 3、头尾没有起始点 35 | chromosome.setGene(new int[]{0,1,0,2,3,0,4}); 36 | System.out.println("3、"+Chromosome.isGoodChromosome(chromosome)); 37 | // 3、头尾没有起始点 38 | chromosome.setGene(new int[]{0,1,0,2,3,0,4}); 39 | System.out.println("3、"+Chromosome.isGoodChromosome(chromosome)); 40 | // 4、受灾点顺序不正确 41 | chromosome.setGene(new int[]{0,1,0,2,3,5,0}); 42 | System.out.println("4、"+Chromosome.isGoodChromosome(chromosome)); 43 | // 5、有重复的受灾点 44 | chromosome.setGene(new int[]{0,1,0,2,2,5,0}); 45 | System.out.println("5、"+Chromosome.isGoodChromosome(chromosome)); 46 | // 6、没有问题的点 47 | chromosome.setGene(new int[]{0,1,0,2,3,4,0}); 48 | System.out.println("6、"+Chromosome.isGoodChromosome(chromosome)); 49 | } 50 | 51 | @Test 52 | public void sortTest() { 53 | List list = new ArrayList<>(); 54 | list.add(2); 55 | list.add(1); 56 | list.add(3); 57 | //list.sort((o1, o2) -> o1-o2); 58 | list.sort(null); 59 | System.out.println(list); 60 | } 61 | 62 | @Test 63 | public void asciiTest() { 64 | Map map = new HashMap<>(); 65 | for (int i = 1; i <= 26; i++) { 66 | map.put(String.valueOf(i),String.valueOf((char) (64+i))); 67 | } 68 | for (String key : map.keySet()) { 69 | System.out.println(map.get(key)); 70 | } 71 | } 72 | 73 | @Test 74 | public void cloneTest() { 75 | Chromosome c1 = new Chromosome(2, 10); 76 | System.out.println(c1); 77 | System.out.println(c1.clone()); 78 | } 79 | 80 | 81 | @Test 82 | public void getPossibleGeneticPair() { 83 | Set set = new HashSet<>(); 84 | set.add(0); 85 | Chromosome chromosome1 = new Chromosome(2, 5); 86 | chromosome1.setGene(new int[]{0,1,2,3,0,4,5,0}); 87 | 88 | Chromosome chromosome2 = new Chromosome(2, 5); 89 | chromosome2.setGene(new int[]{0,1,0,3,2,4,5,0}); 90 | 91 | List> list = Chromosome.getPossibleGeneticPair(chromosome1, chromosome2, set); 92 | System.out.println(list); 93 | // [Pair[1,1], Pair[1,4], Pair[1,5], Pair[1,6], Pair[2,4], 94 | // Pair[2,5], Pair[2,6], Pair[3,3], Pair[5,5], Pair[5,6], Pair[6,6]] 95 | } 96 | 97 | @Test 98 | public void geneticTest() { 99 | Chromosome chromosome1 = new Chromosome(2, 5); 100 | chromosome1.setGene(new int[]{0,1,2,3,0,4,5,0}); 101 | 102 | Chromosome chromosome2 = new Chromosome(2, 5); 103 | chromosome2.setGene(new int[]{0,1,0,3,2,4,5,0}); 104 | 105 | List children = new ArrayList<>(); 106 | for (int i = 0; i < 100; i++) { 107 | List tmp = Chromosome.genetic(chromosome1, chromosome2); 108 | for (Chromosome chromosome : tmp) { 109 | children.add(chromosome); 110 | } 111 | } 112 | int errorCount = 0; 113 | for (Chromosome child : children) { 114 | System.out.println(child); 115 | if (!Chromosome.isGoodChromosome(child)) { 116 | errorCount++; 117 | System.out.println("出现不合适的染色体!!!!!"); 118 | } 119 | } 120 | System.out.println("出现不合适染色体概率为:"+errorCount+"/100"); 121 | } 122 | 123 | @Test 124 | public void mutation() { 125 | Chromosome chromosome1 = new Chromosome(2, 5); 126 | chromosome1.setGene(new int[]{0,1,2,3,0,4,5,0}); 127 | List list = new ArrayList<>(); 128 | for (int i = 0; i < 100; i++) { 129 | Chromosome dummy = chromosome1.clone(); 130 | dummy.mutation(dummy.getGeneSize()/2); 131 | list.add(dummy); 132 | } 133 | int errCount = 0; 134 | for (Chromosome chromosome : list) { 135 | System.out.println(chromosome); 136 | if (!Chromosome.isGoodChromosome(chromosome)) { 137 | errCount++; 138 | System.out.println("出现变异异常的个体!!!"); 139 | } 140 | } 141 | System.out.println("编译失败概率为:"+(double)errCount/100 +"%"); 142 | } 143 | } -------------------------------------------------------------------------------- /src/main/java/ga_simple_planning/GeneticAlgorithm.java: -------------------------------------------------------------------------------- 1 | package ga_simple_planning; 2 | 3 | import com.sun.jdi.PrimitiveValue; 4 | import util.PropertyUtil; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Comparator; 8 | import java.util.List; 9 | import java.util.Properties; 10 | 11 | /** 12 | * @ClassName GeneticAlgorithm 13 | * @Description TODO 14 | * @Author faro_z 15 | * @Date 2022/3/2 9:35 下午 16 | * @Version 1.0 17 | **/ 18 | public class GeneticAlgorithm extends MaxFuncAdapter { 19 | // 种群 20 | private List pop; 21 | Properties pro = PropertyUtil.getProperty("ga_simple"); 22 | // 种群大小 23 | private int POP_SIZE = Integer.valueOf(pro.getProperty("POP_SIZE")); 24 | // 基因长短 25 | private int CHROMOSOME_SIZE = Integer.valueOf(pro.getProperty("CHROMOSOME_SIZE")); 26 | // 迭代次数 27 | private int ITER_NUM = Integer.valueOf(pro.getProperty("ITER_NUM")); 28 | // 个体变异概率 29 | private double MUTATION_RATE=Double.valueOf(pro.getProperty("MUTATION_RATE")); 30 | // 最大变异长度 31 | private int MAX_MUTATION_NUM=Integer.valueOf(pro.getProperty("MAX_MUTATION_NUM")); 32 | 33 | // 当前这代种群中的最佳适应度 34 | private double bestScore = Double.MIN_VALUE; 35 | // 当前这代种群中的最坏适应度 36 | private double worstScore = Double.MAX_VALUE; 37 | // 群体适应度得分(这里只限于当前群体) 38 | private double totalScore = 0; 39 | // 平均群体适应度(需要注意,因为精度问题,我们要确保平均得分不得超过最好得分) 40 | private double averageScore = 0; 41 | 42 | // 种群代数目统计 43 | private int popCount=0; 44 | 45 | private int bestX = 0; 46 | private int bestY = 0; 47 | 48 | 49 | public GeneticAlgorithm() { 50 | pop = new ArrayList(); 51 | } 52 | 53 | 54 | public void conductGA() { 55 | //init(); 56 | mockBadPop(); // 初始化烂种群 57 | for (int i = 0; i < ITER_NUM ; i++) { 58 | // 计算种群适应度 59 | calculateScore(); 60 | print(); 61 | // 种群遗传 62 | // - 筛选较优父代(轮盘赌算法) 63 | // - 交叉运算 64 | evolve(); 65 | // 种群变异 66 | mutation(); 67 | } 68 | } 69 | 70 | private void print() { 71 | System.out.println("当前代数为:"+popCount); 72 | //System.out.println("当前种群为:"); 73 | //for (Chromosome chromosome : pop) { 74 | // System.out.println(chromosome); 75 | //} 76 | System.out.println("最佳适应度为:"+bestScore); 77 | System.out.println("最坏适应度为:"+worstScore); 78 | System.out.println("总适应度为:"+totalScore); 79 | System.out.println("平均适应度为:"+averageScore); 80 | System.out.println("---------------------------------------"); 81 | } 82 | 83 | /** 84 | * 初始化种群 85 | */ 86 | private void init() { 87 | System.out.println("0、初始化种群"); 88 | popCount++; 89 | for (int i = 0; i < POP_SIZE; i++) { 90 | pop.add(new Chromosome(CHROMOSOME_SIZE)); 91 | } 92 | } 93 | 94 | /** 95 | * 生成较坏的父代 96 | */ 97 | private void mockBadPop() { 98 | System.out.println("0、初始化坏种群"); 99 | popCount++; 100 | for (int i = 0; i < POP_SIZE; i++) { 101 | Chromosome chromosome = new Chromosome(CHROMOSOME_SIZE); 102 | boolean[] gene = chromosome.getGene(); 103 | gene[0]=false; 104 | gene[1]=false; 105 | pop.add(chromosome); 106 | } 107 | } 108 | 109 | /** 110 | * 计算种群适应度 111 | */ 112 | private void calculateScore() { 113 | if (pop==null || pop.size()==0) { 114 | pop = new ArrayList(); 115 | init(); 116 | } 117 | bestScore = Double.MIN_VALUE; 118 | worstScore = Double.MAX_VALUE; 119 | // 因为 totalScore 记录的是当前群体适应度总和 120 | // 所以每次计算适应度之前,都必须清空前一代群体的适应度总和 121 | totalScore = 0; 122 | for (Chromosome chromosome : pop) { 123 | chromosome.setScore(calculateChromosomeScore(chromosome)); 124 | totalScore+= chromosome.getScore(); 125 | bestScore = Math.max(bestScore, chromosome.getScore()); 126 | worstScore = Math.min(worstScore,chromosome.getScore()); 127 | } 128 | averageScore = totalScore/POP_SIZE; 129 | // 因为精度问题,要注意 averageScore 会不会大于 bestScore 130 | if (averageScore>bestScore) averageScore = bestScore; 131 | } 132 | 133 | /** 134 | * 计算个体适应度 135 | * @param chromosome 被计算个体 136 | * @return 个体适应度 137 | */ 138 | private int calculateChromosomeScore(Chromosome chromosome) { 139 | int x = changeX(chromosome); 140 | // 借助位运算,获取 x1,x2 的值 141 | return changeY((x&56)>>3,x&7); 142 | } 143 | 144 | @Override 145 | int changeX(Chromosome chromosome) { 146 | if (chromosome!=null) return Integer.valueOf(chromosome.decoder()); 147 | return 0; 148 | } 149 | 150 | @Override 151 | int changeY(int x1,int x2) { 152 | return x1*x1 + x2*x2; 153 | } 154 | 155 | /** 156 | * 生成新种群 157 | */ 158 | private void evolve() { 159 | List newPop = new ArrayList<>(); 160 | while (newPop.size() children = Chromosome.genetic(p1, p2); 165 | for (Chromosome child : children) { 166 | newPop.add(child); 167 | } 168 | } 169 | pop.clear(); 170 | pop=newPop; 171 | popCount++; 172 | } 173 | 174 | /** 175 | * 使用轮盘赌法,获取相对较好的父类个体 176 | * 这里在选择个体的时候要注意: 177 | * 1、个体被选择的概率要遵循轮盘赌法 178 | * 2、个体的适应度要大于平均适应度 179 | * 轮盘赌法相关文章:https://blog.csdn.net/weixin_44062380/article/details/123255853 180 | * @return 181 | */ 182 | private Chromosome getParentChromosome() { 183 | if (pop==null || pop.size()==0) return null; 184 | int iterCount = 0; 185 | while (true) { 186 | double slice = totalScore * Math.random(); 187 | double sum = 0d; 188 | for (Chromosome chromosome : pop) { 189 | sum+=chromosome.getScore(); 190 | // 一定要保证个体适应度大于平均适应度 191 | if (sum>slice && chromosome.getScore()>averageScore) { 192 | return chromosome; 193 | } 194 | } 195 | iterCount++; 196 | // 防止迭代次数过高 197 | // 迭代次数超过阈值,返回种群最优个体 198 | if (iterCount>400) { 199 | return pop.stream() 200 | .max(Comparator.comparing(Chromosome::getScore)) 201 | .get(); 202 | } 203 | } 204 | } 205 | 206 | /** 207 | * 种群染色体发生变异 208 | */ 209 | private void mutation() { 210 | for (Chromosome chromosome : pop) { 211 | if (Math.random() 2 | 3 | 97 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /src/main/java/ga_complex_planning/Chromosome.java: -------------------------------------------------------------------------------- 1 | package ga_complex_planning; 2 | 3 | import com.sun.tools.javac.util.Pair; 4 | 5 | import java.util.*; 6 | 7 | /** 8 | * @ClassName Chromosome 9 | * @Description TODO 10 | * @Author faro_z 11 | * @Date 2022/3/2 9:35 下午 12 | * @Version 1.0 13 | **/ 14 | public class Chromosome { 15 | private int[] gene; 16 | private int geneSize; 17 | private double score; 18 | private int carNum; 19 | private int pointNum; 20 | 21 | private static Random r = new Random(); 22 | 23 | public Chromosome() {} 24 | 25 | public Chromosome(int carNum, int pointNum) { 26 | this.carNum=carNum; 27 | this.pointNum=pointNum; 28 | geneSize = carNum+1+pointNum; 29 | gene = new int[geneSize]; 30 | for (int i = 0; i < geneSize; i++) { 31 | gene[i]=-1; 32 | } 33 | init(); 34 | } 35 | 36 | /** 37 | * 初始化个体基因,需要保证 38 | * 1、基因中起始点0个数 为 carNum +1 39 | * 2、每个点只能出现一次,且所有点都必须出现 40 | * 3、两个起始点不可以相邻 41 | * 4、基因收尾必须是起始点位置 0 42 | * 43 | * 假设 carNum = 2 pointNum =4 44 | * 正确的基因: 45 | * 0 2 3 0 1 4 0 46 | * 47 | * 错误的基因: 48 | * 0 3 3 0 1 4 0 (3受灾点重复) 49 | * 0 0 3 2 1 4 0 (有两个起始点重合->有车子没出发) 50 | */ 51 | public void init() { 52 | int remainStartPoints = carNum + 1; 53 | gene[0]=0; 54 | gene[geneSize-1]=0; 55 | remainStartPoints-=2; 56 | // 存放未被放置的受灾点 57 | List pointList = new ArrayList<>(); 58 | for (int i = 1; i <= pointNum ; i++) { 59 | pointList.add(i); 60 | } 61 | // 随机生成起始点 62 | if (carNum>1 && geneSize>4) { 63 | //System.out.println("还需要被放置的起始点个数为:"+remainStartPoints); 64 | while (remainStartPoints>0) { 65 | // 随机基因起始点 66 | int tmpStartIndex = r.nextInt(geneSize - 4) + 2; 67 | if (gene[tmpStartIndex]!=0 && gene[tmpStartIndex-1]!=0 && gene[tmpStartIndex+1]!=0) { 68 | gene[tmpStartIndex]=0; 69 | remainStartPoints--; 70 | } 71 | } 72 | } 73 | 74 | //System.out.println("放置完起始点后的基因为:"+Arrays.toString(gene)); 75 | // 将受灾点坐标,放进基因组中 76 | for (int i = 0; i < geneSize; i++) { 77 | if (gene[i]==-1) { 78 | int randomIndex = 0; 79 | if (pointList.size()>0) { 80 | randomIndex = r.nextInt(pointList.size()); 81 | } 82 | gene[i]=pointList.get(randomIndex); 83 | pointList.remove(randomIndex); 84 | } 85 | } 86 | } 87 | 88 | 89 | /** 90 | * 判断当前染色体是否符合要求 91 | * 1、基因中起始点0个数 为 carNum +1 92 | * 2、每个点只能出现一次,且所有点都必须出现 93 | * 3、两个起始点不可以相邻 94 | * 4、基因收尾必须是起始点位置 0 95 | * 这个函数主要是用在交叉后对子代进行判断的 96 | * 这里还要注意一点,如果生成的子代不满足条件 97 | * 我们不应该重新轮盘赌选择父代,而是重新选择交叉片段的位移 98 | * @param chromosome 99 | * @return 100 | */ 101 | public static boolean isGoodChromosome(Chromosome chromosome) { 102 | // 1、基因不存在 103 | if (chromosome==null || chromosome.getGene()==null || chromosome.getGene().length==0) return false; 104 | int[] gene = chromosome.getGene(); 105 | List genePointList = new ArrayList<>(); 106 | // 基因中目标起始点的个数 107 | int targetStartPointNum = chromosome.getCarNum() + 1; 108 | int startPointCount = 2; 109 | if (gene[gene.length-2]==0) return false; 110 | for (int i = 1; i < gene.length-1; i++) { 111 | if (gene[i]==0 && gene[i-1]==0) { 112 | // 2、两个起始点邻接 113 | return false; 114 | } 115 | if (gene[i]==0) { 116 | startPointCount++; 117 | } 118 | else { 119 | genePointList.add(gene[i]); 120 | } 121 | } 122 | // 3、起始点个数不满足要求 123 | if (startPointCount!=targetStartPointNum) return false; 124 | // 4、不满足囊括所有受灾点 125 | // 受灾点排布必须满足 n=N 时,为 1,2,...N 126 | genePointList.sort((o1, o2) -> o1-o2); 127 | if (genePointList.get(0)!=1) return false; 128 | for (int i = 1; i genetic(Chromosome p1,Chromosome p2) { 142 | if (p1==null || p2==null) return null; 143 | if (p1.getGeneSize()!=p2.getGeneSize()) return null; 144 | List children = new ArrayList<>(); 145 | 146 | // 起始点可能的集合(后期起始点可能不会设置为0) 147 | Set startPointSet = new HashSet<>(); 148 | startPointSet.add(0); 149 | List> possibleGeneticPair = getPossibleGeneticPair(p1, p2, startPointSet); 150 | // 获取交叉位置 151 | Pair pair = possibleGeneticPair.get(r.nextInt(possibleGeneticPair.size())); 152 | Chromosome c1 = p1.clone(); 153 | Chromosome c2 = p2.clone(); 154 | int[] c1Gene = c1.getGene(); 155 | int[] c2Gene = c2.getGene(); 156 | int left = pair.fst; 157 | int right = pair.snd; 158 | for (int i = left; i <=right ; i++) { 159 | int tmp = c1Gene[i]; 160 | c1Gene[i]=c2Gene[i]; 161 | c2Gene[i]=tmp; 162 | } 163 | children.add(c1); 164 | children.add(c2); 165 | return children; 166 | } 167 | 168 | /** 169 | * 获取所有可能的交叉位置 170 | * 使用双指针法获取 171 | * 如果排除收尾后,还是,没有获取对应的点的信息 172 | * 我们直接获取收尾(交换后的结果,就是两个染色体位置互换) 173 | * 174 | * 这里之所以要先列出所有可能交叉点,再随机选择 175 | * 是因为如果先随机选择的话,命中率太低了 176 | * @param p1 177 | * @param p2 178 | * @return 179 | */ 180 | public static List> getPossibleGeneticPair(Chromosome p1,Chromosome p2,SetstartPointSet) { 181 | if (p1==null || p2==null) return null; 182 | if (p1.getGeneSize()!=p2.getGeneSize()) return null; 183 | List> res = new ArrayList<>(); 184 | int geneSize = p1.getGeneSize(); 185 | // 暂时不包含首尾 186 | for (int l = 1; l (l,r)); 190 | } 191 | } 192 | } 193 | return res; 194 | } 195 | 196 | /** 197 | * 计算范围中起始点的个数是否一致 198 | * 这样可以加速符合条件的子代生成 199 | * @param left 200 | * @param right 201 | * @return 202 | */ 203 | private static boolean isSwapScopeLegal(int left,int right,Chromosome p1,Chromosome p2,SetstartPointSet) { 204 | if (left<=0 || right<=0 || left>=p1.getGeneSize()-1 || right>=p1.getGeneSize()-1) return false; 205 | int[] p1Gene = p1.getGene(); 206 | int[] p2Gene = p2.getGene(); 207 | int p1StartCount=0; 208 | int p2StartCount=0; 209 | for (int i = left; i <=right ; i++) { 210 | if (startPointSet.contains(p1Gene[i])) p1StartCount++; 211 | if (startPointSet.contains(p2Gene[i])) p2StartCount++; 212 | } 213 | // 待交换基因片段中,起始点位置不一致(说明交换后染色体就不合法了) 214 | if (p1StartCount!=p2StartCount) { 215 | return false; 216 | } 217 | // 保证交换位置合法 218 | if (startPointSet.contains(p1Gene[left])) { 219 | if (p1Gene[left-1]==0 || p1Gene[left+1]==0) return false; 220 | } 221 | if (startPointSet.contains(p1Gene[right])) { 222 | if (p1Gene[right-1]==0 || p1Gene[right+1]==0) return false; 223 | } 224 | if (startPointSet.contains(p2Gene[left])) { 225 | if (p2Gene[left-1]==0 || p2Gene[left+1]==0) return false; 226 | } 227 | if (startPointSet.contains(p2Gene[right])) { 228 | if (p2Gene[right-1]==0 || p2Gene[right+1]==0) return false; 229 | } 230 | return true; 231 | } 232 | 233 | 234 | /** 235 | * 基因变异 236 | * 为保证符合路径要求,基因变异只能进行基因组片段交换 237 | * 且交换条件如下: 238 | * 1、两两都不是 startPoint 239 | * 2、两个中只能有一个为起始点,且这个起始点不可以是头或尾 240 | * X 3、两两都是 startPoint <--- 这种变异没有意义,排除 241 | * 242 | * @param maxMutationNum 243 | */ 244 | // TODO: 245 | public void mutation(int maxMutationNum) { 246 | // 如果要变异,变异对的个数起码也得是1对 247 | int mutationPairNum = Math.max((int) Math.random() * maxMutationNum / 2,1); 248 | // 变异策略有两种 249 | // 1、找出所有合理变异对,然后选择 250 | // 2、随机选择变异对,然后判断是否合理 251 | // 这里选择第二种,因为其命中概率不低,不会造成太高的迭代 252 | 253 | for (int i = 0; i < mutationPairNum; i++) { 254 | int left = r.nextInt(geneSize - 2) + 1; 255 | int right = r.nextInt(geneSize - 2) + 1; 256 | // 防止出现过高迭代,导致阻塞 257 | int maxIterNum = 400; 258 | while (!isSwapPairLegal(left,right) && maxIterNum>=0) { 259 | left = r.nextInt(geneSize - 2) + 1; 260 | right = r.nextInt(geneSize - 2) + 1; 261 | maxIterNum--; 262 | if (maxIterNum<0) { 263 | left=0; 264 | right=0; 265 | } 266 | } 267 | // 交换目标基因 268 | int tmp = gene[left]; 269 | gene[left] = gene[right]; 270 | gene[right]=tmp; 271 | } 272 | } 273 | 274 | /** 275 | * 判断待变异前的数对是否符合要求 276 | * @param left 277 | * @param right 278 | * @return 279 | */ 280 | private boolean isSwapPairLegal(int left,int right) { 281 | // 都是非 startPoint,可以变异 282 | if (gene[left]!=0 && gene[right]!=0) return true; 283 | // 无效变异 284 | if (gene[left]==0 && gene[right]==0) return false; 285 | // 判断交换后是否还合法 286 | Chromosome dummy = this.clone(); 287 | int[] gene = dummy.getGene(); 288 | int tmp = gene[left]; 289 | gene[left]=gene[right]; 290 | gene[right]=tmp; 291 | return isGoodChromosome(dummy); 292 | } 293 | 294 | /** 295 | * 对染色体进行深拷贝 296 | * @return 297 | */ 298 | public Chromosome clone() { 299 | Chromosome child = new Chromosome(); 300 | child.setCarNum(carNum); 301 | child.setPointNum(pointNum); 302 | child.setGeneSize(geneSize); 303 | int[] pGene = gene; 304 | int[] cGene = new int[pGene.length]; 305 | for (int i = 0; i < pGene.length; i++) { 306 | cGene[i]=pGene[i]; 307 | } 308 | child.setGene(cGene); 309 | return child; 310 | } 311 | 312 | public int[] getGene() { 313 | return gene; 314 | } 315 | 316 | public void setGene(int[] gene) { 317 | this.gene = gene; 318 | } 319 | 320 | public int getGeneSize() { 321 | return geneSize; 322 | } 323 | 324 | public void setGeneSize(int geneSize) { 325 | this.geneSize = geneSize; 326 | } 327 | 328 | public double getScore() { 329 | return score; 330 | } 331 | 332 | public void setScore(double score) { 333 | this.score = score; 334 | } 335 | 336 | public int getCarNum() { 337 | return carNum; 338 | } 339 | 340 | public void setCarNum(int carNum) { 341 | this.carNum = carNum; 342 | } 343 | 344 | public int getPointNum() { 345 | return pointNum; 346 | } 347 | 348 | public void setPointNum(int pointNum) { 349 | this.pointNum = pointNum; 350 | } 351 | 352 | @Override 353 | public String toString() { 354 | StringBuilder builder = new StringBuilder(); 355 | return builder.append("基因为:").append(Arrays.toString(gene)).append("\n") 356 | .append("适应度为:").append(score) 357 | .toString(); 358 | } 359 | } 360 | -------------------------------------------------------------------------------- /src/main/java/ga_complex_planning/GeneticAlgorithm.java: -------------------------------------------------------------------------------- 1 | package ga_complex_planning; 2 | 3 | import ga_complex_planning.planning_info.Info; 4 | import ga_complex_planning.pojo.Point; 5 | import util.GAGraphUtil; 6 | import util.PropertyUtil; 7 | 8 | import java.util.*; 9 | 10 | /** 11 | * @ClassName GeneticAlgorithm 12 | * @Description TODO 13 | * @Author faro_z 14 | * @Date 2022/3/2 9:35 下午 15 | * @Version 1.0 16 | **/ 17 | public class GeneticAlgorithm extends Codec implements RouteCalculator { 18 | 19 | private Properties gaComplexPro = PropertyUtil.getProperty("ga_complex"); 20 | private Properties planningInfoPro = PropertyUtil.getProperty("planning_info"); 21 | 22 | private Map bestScoreDataMap = new HashMap<>(); 23 | private Map worstScoreDataMap = new HashMap<>(); 24 | private Map totalScoreDataMap = new HashMap<>(); 25 | 26 | public static int underZeroCount = 0; 27 | 28 | private static final Random r = new Random(); 29 | 30 | // 当且仅当 GeneticAlgorithm 对象被实例化后,下面这段静态代码块才会执行 31 | // 此时,codeMap 的值才会存在 32 | // 也就是说,GA 对象没有创建的时候,Codec 就是无效的 33 | static { 34 | // 默认的解码是 A->1 B->2 35 | // 后期可以修改为读取配置文件中的解码信息 36 | for (int i = 1; i <=26 ; i++) { 37 | codeMap.put(String.valueOf(i),String.valueOf((char) (64+i))); 38 | } 39 | codeMap.put("0","start"); 40 | } 41 | 42 | // 受灾地信息 43 | private Map info = Info.getInfo(); 44 | 45 | // 车辆数目 46 | private int CAR_NUM = Integer.valueOf(planningInfoPro.getProperty("CAR_NUM")); 47 | // 车辆载重 48 | private double CAR_CAPACITY = Double.valueOf(planningInfoPro.getProperty("CAR_CAPACITY")); 49 | // 车辆速度 50 | private double CAR_SPEED = Double.valueOf(planningInfoPro.getProperty("CAR_SPEED")); 51 | // 对时间窗需求的权重 52 | private double TIME_WINDOW_WEIGHT = Double.valueOf(gaComplexPro.getProperty("TIME_WINDOW_WEIGHT")); 53 | // 对货物需求的权重 54 | private double GOOD_NEED_WEIGHT = Double.valueOf(gaComplexPro.getProperty("GOOD_NEED_WEIGHT")); 55 | // 受灾点个数 start 点不能计算在其中 56 | private int POINT_NUM = info.size()-1; 57 | // 个体初始得分,设置初始得分是为了防止个体得分出现负数 58 | private double ORIGIN_SCORE = Double.valueOf(gaComplexPro.getProperty("ORIGIN_SCORE")); 59 | // 应急点紧急程度对应的权值 60 | private double EMERGENCY_WEIGHT = Double.valueOf(gaComplexPro.getProperty("EMERGENCY_WEIGHT")); 61 | 62 | // 种群 63 | private List pop = new ArrayList<>(); 64 | // 种群大小 65 | private int POP_SIZE = Integer.valueOf(gaComplexPro.getProperty("POP_SIZE")); 66 | // 迭代次数 67 | private int ITER_NUM = Integer.valueOf(gaComplexPro.getProperty("ITER_NUM")); 68 | // 迭代次数计数 69 | private int iterCount = 0; 70 | 71 | 72 | 73 | // 基因长短 74 | private int geneSize = calculateGeneSize(); 75 | // 当前种群最佳适应度 76 | private double bestScore = Double.MIN_VALUE; 77 | // 当前种群最坏适应度 78 | private double worstScore = Double.MAX_VALUE; 79 | // 当前种群总适应度 80 | private double totalScore = 0; 81 | // 当前种群平均适应度 82 | private double averageScore = 0; 83 | // 当前种群最佳基因 84 | private int[] bestGene = null; 85 | // 当前种群最坏基因 86 | private int[] worstGene = null; 87 | 88 | // 变异概率 89 | private double MUTATION_RATE = Double.valueOf(gaComplexPro.getProperty("MUTATION_RATE")); 90 | // 最大变异长度 91 | private int MAX_MUTATION_NUM = geneSize/2; 92 | 93 | 94 | /** 95 | * 执行遗传算法函数 96 | * @return 执行结束后最佳的种群基因 97 | */ 98 | public int[] conductGA() { 99 | long startTime = System.currentTimeMillis(); 100 | init(); 101 | for (int i = 0; i < ITER_NUM; i++) { 102 | // 1、计算种群适应度 103 | calculatePopScore(); 104 | print(i+1); 105 | // 2、交叉生成新的种群 106 | evolve(); 107 | // 3、种群变异 108 | mutation(); 109 | } 110 | long endTime = System.currentTimeMillis(); 111 | System.out.println("=============================="); 112 | System.out.println("出现负值结果个数为:"+GeneticAlgorithm.underZeroCount); 113 | System.out.println("平均每次迭代出现负值的个数为:"+GeneticAlgorithm.underZeroCount/500); 114 | System.out.println("运算耗时为:"+(endTime-startTime)+"ms"); 115 | Map[] bestWorstDataSet = new Map[2]; 116 | Map[] totalDataSet = new Map[1]; 117 | bestWorstDataSet[0]=bestScoreDataMap; 118 | bestWorstDataSet[1]=worstScoreDataMap; 119 | totalDataSet[0]=totalScoreDataMap; 120 | GAGraphUtil.drawBestWorstScoreGraph(bestWorstDataSet); 121 | GAGraphUtil.drawTotalScoreGraph(totalDataSet); 122 | GAGraphUtil.blockUtil(); 123 | return bestGene; 124 | } 125 | 126 | /** 127 | * 临时打印相关信息 128 | */ 129 | private void print(int generation) { 130 | System.out.println("----------------------------------------"); 131 | System.out.println("当前代数:"+generation); 132 | System.out.println("当前种群最佳适应度:"+bestScore); 133 | System.out.println("当前种群最坏适应度:"+worstScore); 134 | System.out.println("当前种群总适应度:"+totalScore); 135 | System.out.println("当前种群平均适应度:"+averageScore); 136 | System.out.println("当前种群最佳基因:"+Arrays.toString(bestGene)); 137 | System.out.println("当前种群最坏基因:"+Arrays.toString(worstGene)); 138 | } 139 | 140 | /** 141 | * 初始化种群 142 | */ 143 | private void init() { 144 | for (int i = 0; i < POP_SIZE ;i++) { 145 | Chromosome chromosome = new Chromosome(CAR_NUM, POINT_NUM); 146 | pop.add(chromosome); 147 | } 148 | } 149 | 150 | /** 151 | * 计算种群适应度 152 | */ 153 | private void calculatePopScore() { 154 | iterCount++; 155 | if (pop==null || pop.size()==0) return; 156 | totalScore=0; 157 | averageScore=0; 158 | bestScore=0; 159 | worstScore=Double.MAX_VALUE; 160 | bestGene=null; 161 | worstGene=null; 162 | for (Chromosome chromosome : pop) { 163 | calculateScore(chromosome); 164 | //System.out.println(chromosome); 165 | totalScore+=chromosome.getScore(); 166 | if (chromosome.getScore()>bestScore) { 167 | bestScore=chromosome.getScore(); 168 | bestGene= chromosome.getGene(); 169 | } 170 | if (chromosome.getScore()bestScore?bestScore:averageScore; 176 | } 177 | // 将待绘制的折线图数据信息存入 map 中 178 | bestScoreDataMap.put((double) iterCount,bestScore); 179 | worstScoreDataMap.put((double) iterCount,worstScore); 180 | totalScoreDataMap.put((double) iterCount,totalScore); 181 | } 182 | 183 | // TODO: 使用强硬的适应度计算策略 <- 1、在初始化种群的时候 2、在父代交叉的时候 184 | /** 185 | * 计算个体适应度 <--------- 这里可以作为论文的研究点? 186 | * 187 | * 这里的适应度计算采取的是温和策略,即: 188 | * 1、不在时间窗内到达的话,不会舍去该路径 189 | * 2、车辆货物余额不满足受灾点要求的话,不会舍去该路径 190 | * 而是减少适应度得分 191 | * 192 | * 193 | * 需要考虑: 194 | * 1、单个车体载重能否满足所有受灾点 195 | * 2、单个车体到达时间是否在时间窗之内 196 | * 3、受灾点的紧急程度 197 | * @param chromosome 198 | */ 199 | private void calculateScore(Chromosome chromosome) { 200 | if (!Chromosome.isGoodChromosome(chromosome)) { 201 | throw new RuntimeException("错误:当前染色体出现错误,无法计算"); 202 | } 203 | // 可能染色体序列 0 5 0 3 2 1 4 0 : start E start C B A D start 204 | 205 | // 已转录基因组 206 | // 不同车辆行驶的路径已经切分开了 : 0 5 0 3 2 1 4 0 -> [[start,E,start], 207 | // [start,C,B,A,D,start]] 208 | List> decodedGeneList = decodeGene(chromosome); 209 | double scoreCount = 0d; 210 | // 每个片段,代表一辆车的行驶路径 211 | for (List slice : decodedGeneList) { 212 | // 记录车辆剩余货物重量 213 | double carCurrCapacity = CAR_CAPACITY; 214 | // 记录当前车辆已经使用的时间(方便与时间窗进行对比) 215 | double currCarTotalTime=0; 216 | // 紧急程度权值计算起点 217 | int emergencyStart = POINT_NUM; 218 | for (int i = 0; i < slice.size() - 1; i++) { 219 | // 获取起始点和终点信息 220 | Point startPoint = info.get(slice.get(i)); 221 | Point endPoint = info.get(slice.get(i+1)); 222 | 223 | // 计算两点之间路径长度 224 | double routeLength = routeLength(startPoint.getX(), startPoint.getY(), endPoint.getX(), endPoint.getY()); 225 | // 行驶时间 226 | double routeTime = routeLength / CAR_SPEED; 227 | 228 | currCarTotalTime+=routeTime; 229 | // 1、是否符合时间窗 230 | if (currCarTotalTime>endPoint.getEnd()) { 231 | scoreCount=scoreCount-(currCarTotalTime-endPoint.getEnd())*TIME_WINDOW_WEIGHT; 232 | } 233 | // 当前车辆运行时间还要加上受灾点需要的服务时间 234 | currCarTotalTime+=endPoint.getServiceTime(); 235 | // 2、车辆运载的货物重量是否满足受灾点需求 236 | if (carCurrCapacityslice) { 277 | //System.out.println("当前命中的目标为:"+targetCount); 278 | return chromosome; 279 | } 280 | //System.out.println("轮盘赌选中个体为:\n"+chromosome); 281 | } 282 | return pop.get(pop.size()-1); 283 | } 284 | 285 | /** 286 | * 交叉产生新的子代 287 | * 这里要注意,一定要避免迭代次数过多导致的程序阻塞 288 | * 289 | */ 290 | private void evolve() { 291 | List newPop = new ArrayList<>(); 292 | while (newPop.size() children = Chromosome.genetic(p1, p2); 297 | for (Chromosome child : children) { 298 | if (Chromosome.isGoodChromosome(child)) { 299 | newPop.add(child); 300 | } 301 | } 302 | } 303 | // 保证新生成的子代长度与原父代长度相等 304 | while (newPop.size()>POP_SIZE) { 305 | newPop.remove(r.nextInt(newPop.size())); 306 | } 307 | pop.clear(); 308 | pop=newPop; 309 | } 310 | 311 | /** 312 | * 种群变异 313 | * 种群变异为了保证变异后子代还是符合条件的 314 | * 必须对基因组序列进行两两交换 315 | */ 316 | private void mutation() { 317 | for (Chromosome chromosome : pop) { 318 | if (Math.random() A -> B -> C -> start` 44 | 45 | A 紧急程度:1 46 | 47 | B 紧急程度:2 48 | 49 | C 紧急程度:3 50 | 51 | 紧急程度适应度得分: `3*1 + 2*2 +1*3 = 10` 52 | 53 | 54 | 55 | `start -> C -> B -> A -> start` 56 | 57 | A 紧急程度:1 58 | 59 | B 紧急程度:2 60 | 61 | C 紧急程度:3 62 | 63 | 紧急程度适应度得分: `3*3 + 2*2 +1*1 = 14` 64 | 65 | 可以看到,运用这种算法,我们的适应度得分,会随着紧急受灾点的位置考前而增加 66 | 67 | 68 | 69 | #### 2)时间窗 70 | 71 | 假设 A 点对物资的需求时间在 0-9 内 72 | 73 | 那么如果物资送达时间为 0-9 之间,则适应度分数不减 74 | 75 | 如果超过 9 ,我们就会依照超出的时间,减去超时对应权值,对适应度得分做减法 76 | 77 | **例:** 78 | 79 | ```java 80 | TIME_WINDOW_WEIGHT = 10;// 时间适应度权值 81 | 82 | carrArriveTime = 11; // 车辆到达时间 83 | srart = 0;// 时间窗起始 84 | end = 9;// 时间窗终止 85 | score = score - (carrArriveTime-end)*TIME_WINDOW_WEIGHT;// 对适应度分数做减法 86 | ``` 87 | 88 | 在本程序中,为了计算方便,我们的时间窗起始时间始终设置为 0 89 | 90 | 91 | 92 | #### 3)受灾点物资重量需求是否满足 93 | 94 | 车辆初始载重我们都设置为 10 t 95 | 96 | 不同受灾点对物资的需求量是不同的 97 | 98 | 这里有两个策略:**强硬策略**和**缓和策略** 99 | 100 | * **缓和策略:** 101 | 102 | 如果在后期出现剩余载重不满足受灾点的情况,我们需要依照情况,对适应度得分做减法 103 | 104 | 这样好处在于不容易陷入局部最优,坏处在于可能给出的结果不满足现实需求,决策者无法更具给出的数据动态为每辆车分配不用的载重 105 | 106 | * **强硬策略:** 107 | 108 | 如果在后期出现剩余载重不满足受灾点的情况,我们直接将这种情况排除 109 | 110 | 好处在于决策者能根据信息,做出更优的判断,坏处在于给出的数据可能无法满足强硬策略的条件,导致出不了结果 111 | 112 |
113 | 114 | 这里我们选择缓和策略,以避免出现程序阻塞为主 115 | 116 | 117 | 118 | #### 4)注意点 119 | 120 | **1、**这里我们需要注意,最后算出来的适应度得分,必须是正数 121 | 122 | 下一步交叉时,就无法使用轮盘赌法选择合适的父代了 123 | 124 | 为了保证是正数,我们可以对计算结果进行补偿(加上一个统一的数),以保证轮盘赌算法能正常执行 125 | 126 | 127 | 128 | **2、**适应度函数要保证个体适应度差距比较大,不然轮盘赌就和随机算法没什么区别了 129 | 130 | 131 | 132 | 133 | 134 | ## 二、遗传算法介绍 135 | 136 | 遗传算法,我之前写过一篇文章进行介绍 137 | 138 | https://mp.weixin.qq.com/s/_mUK2mdczU4x_lekpuh6hw 139 | 140 | 在阅读下面的内容之前,如果还有对遗传算法不了解的同学,强烈建议去看一下哦 141 | 142 | 143 | 144 | ## 三、遗传算法在应急路径规划上的应用 145 | 146 | 遗传算法的执行流程,除了适应度计算之外,其他与特定问题的条件几乎是隔离开来的 147 | 148 | 我们要做的,就是想办法将应急路径规划考虑的条件,编码成遗传算法所能识别的数据类型 149 | 150 | ### 1、个体编码与种群初始化 151 | 152 | #### 1)编码规则 153 | 154 | 针对路径规划问题,我们个体编码策略选择如下 155 | 156 | 路径名称用字母代替,字母在字母表中的位置即其单片染色体的编码,**数字0 表示起始点** 157 | 158 | **例如:** 159 | 160 | `start -> A -> B -> C -> start` 161 | 162 | **对应的编码为:** 163 | 164 | `0 1 2 3 0` 165 | 166 | 167 | 168 | #### 2)编码注意事项 169 | 170 | 我们的编码还要符合路径规划的要求,即: 171 | 172 | **1、**基因中起始点0个数 为 carNum +1 173 | 174 | **2、**每个点只能出现一次,且所有点都必须出现 175 | 176 | **3、**两个起始点不可以相邻 177 | 178 | **4、**基因收尾必须是起始点位置 0 179 | 180 | ``` 181 | 假设 carNum = 2 pointNum =4 182 | 正确的基因: 183 | 0 2 3 0 1 4 0 184 | 错误的基因: 185 | 0 3 3 0 1 4 0 (3受灾点重复) 186 | 0 0 3 2 1 4 0 (有两个起始点重合->有车子没出发) 187 | ``` 188 | 189 | 190 | 191 | #### 3)种群初始化 192 | 193 | ``` 194 | GeneticAlgorithm/init() 195 | - Chromosome() // constructor 196 | - Chromosome/init() 197 | ``` 198 | 199 | 200 | 201 | * **种群初始化:** 202 | 203 | 种群初始化直接调用个体的构造函数 204 | 205 | 个体带参构造函数里有初始化方法 206 | 207 | ```java 208 | private void init() { 209 | for (int i = 0; i < POP_SIZE ;i++) { 210 | Chromosome chromosome = new Chromosome(CAR_NUM, POINT_NUM); 211 | pop.add(chromosome); 212 | } 213 | } 214 | ``` 215 | 216 | 217 | 218 | * **个体初始化:** 219 | 220 | `init()` 方法确保生成的个体合法 221 | 222 | ```java 223 | public Chromosome(int carNum, int pointNum) { 224 | this.carNum=carNum; 225 | this.pointNum=pointNum; 226 | geneSize = carNum+1+pointNum; 227 | gene = new int[geneSize]; 228 | for (int i = 0; i < geneSize; i++) { 229 | gene[i]=-1; 230 | } 231 | init(); 232 | } 233 | 234 | // ... 235 | 236 | public void init() { 237 | int remainStartPoints = carNum + 1; 238 | gene[0]=0; 239 | gene[geneSize-1]=0; 240 | remainStartPoints-=2; 241 | // 存放未被放置的受灾点 242 | List pointList = new ArrayList<>(); 243 | for (int i = 1; i <= pointNum ; i++) { 244 | pointList.add(i); 245 | } 246 | // 随机生成起始点 247 | if (carNum>1 && geneSize>4) { 248 | while (remainStartPoints>0) { 249 | // 随机基因起始点 250 | int tmpStartIndex = r.nextInt(geneSize - 4) + 2; 251 | if (gene[tmpStartIndex]!=0 && gene[tmpStartIndex-1]!=0 && gene[tmpStartIndex+1]!=0) { 252 | gene[tmpStartIndex]=0; 253 | remainStartPoints--; 254 | } 255 | } 256 | } 257 | 258 | for (int i = 0; i < geneSize; i++) { 259 | if (gene[i]==-1) { 260 | int randomIndex = 0; 261 | if (pointList.size()>0) { 262 | randomIndex = r.nextInt(pointList.size()); 263 | } 264 | gene[i]=pointList.get(randomIndex); 265 | pointList.remove(randomIndex); 266 | } 267 | } 268 | } 269 | ``` 270 | 271 | 272 | 273 | * **判断生成的个体是否合法:** 274 | 275 | ```java 276 | public static boolean isGoodChromosome(Chromosome chromosome) { 277 | // 1、基因不存在 278 | if (chromosome==null || chromosome.getGene()==null || chromosome.getGene().length==0) return false; 279 | int[] gene = chromosome.getGene(); 280 | List genePointList = new ArrayList<>(); 281 | // 基因中目标起始点的个数 282 | int targetStartPointNum = chromosome.getCarNum() + 1; 283 | int startPointCount = 2; 284 | if (gene[gene.length-2]==0) return false; 285 | for (int i = 1; i < gene.length-1; i++) { 286 | if (gene[i]==0 && gene[i-1]==0) { 287 | // 2、两个起始点邻接 288 | return false; 289 | } 290 | if (gene[i]==0) { 291 | startPointCount++; 292 | } 293 | else { 294 | genePointList.add(gene[i]); 295 | } 296 | } 297 | // 3、起始点个数不满足要求 298 | if (startPointCount!=targetStartPointNum) return false; 299 | // 4、不满足囊括所有受灾点 300 | // 受灾点排布必须满足 n=N 时,为 1,2,...N 301 | genePointList.sort((o1, o2) -> o1-o2); 302 | if (genePointList.get(0)!=1) return false; 303 | for (int i = 1; i 311 | 312 | **经测试,初始化函数是严格符合要求的:** 313 | 314 | ```java 315 | @Test 316 | public void testInit() { 317 | List list = new ArrayList<>(); 318 | for (int i = 0; i < 500; i++) { 319 | list.add(new Chromosome(5,30)); 320 | } 321 | for (Chromosome chromosome : list) { 322 | System.out.println(chromosome); 323 | System.out.println(Chromosome.isGoodChromosome(chromosome)); 324 | } 325 | } 326 | ``` 327 | 328 | ![测试结果](https://gitee.com/faro/images/raw/master/img/20220311131306.png) 329 | 330 | 331 | 332 | ### 2、适应度计算 333 | 334 | ``` 335 | calculatePopScore() 336 | - calculateScore() 337 | - decodeGene() 338 | ``` 339 | 340 | * **种群适应度计算:** 341 | 342 | 种群适应度计算,只需遍历调用个体的适应度计算函数即可 343 | 344 | 同时,还要统计当前种群中的相关信息 345 | 346 | ```java 347 | private void calculatePopScore() { 348 | iterCount++; 349 | if (pop==null || pop.size()==0) return; 350 | totalScore=0; 351 | averageScore=0; 352 | bestScore=0; 353 | worstScore=Double.MAX_VALUE; 354 | bestGene=null; 355 | worstGene=null; 356 | for (Chromosome chromosome : pop) { 357 | calculateScore(chromosome); 358 | //System.out.println(chromosome); 359 | totalScore+=chromosome.getScore(); 360 | if (chromosome.getScore()>bestScore) { 361 | bestScore=chromosome.getScore(); 362 | bestGene= chromosome.getGene(); 363 | } 364 | if (chromosome.getScore()bestScore?bestScore:averageScore; 370 | } 371 | // 将待绘制的折线图数据信息存入 map 中 372 | bestScoreDataMap.put((double) iterCount,bestScore); 373 | worstScoreDataMap.put((double) iterCount,worstScore); 374 | totalScoreDataMap.put((double) iterCount,totalScore); 375 | } 376 | ``` 377 | 378 | 379 | 380 | * **个体适应度计算:** 381 | 382 | 这里的适应度计算采取的是**温和策略**,即: 383 | 384 | **1、**不在时间窗内到达的话,不会舍去该路径 385 | 386 | **2、**车辆货物余额不满足受灾点要求的话,不会舍去该路径 387 | 388 |
389 | 390 | 而是**减少适应度得分**,需要考虑: 391 | 392 | **1、**单个车体载重能否满足所有受灾点 393 | 394 | **2、**单个车体到达时间是否在时间窗之内 395 | 396 | **3、**受灾点的紧急程度 397 | 398 | ```java 399 | private void calculateScore(Chromosome chromosome) { 400 | if (!Chromosome.isGoodChromosome(chromosome)) { 401 | throw new RuntimeException("错误:当前染色体出现错误,无法计算"); 402 | } 403 | // 可能染色体序列 0 5 0 3 2 1 4 0 : start E start C B A D start 404 | 405 | // 已转录基因组 406 | // 不同车辆行驶的路径已经切分开了 : 0 5 0 3 2 1 4 0 -> [[start,E,start], 407 | // [start,C,B,A,D,start]] 408 | List> decodedGeneList = decodeGene(chromosome); 409 | double scoreCount = 0d; 410 | // 每个片段,代表一辆车的行驶路径 411 | for (List slice : decodedGeneList) { 412 | // 记录车辆剩余货物重量 413 | double carCurrCapacity = CAR_CAPACITY; 414 | // 记录当前车辆已经使用的时间(方便与时间窗进行对比) 415 | double currCarTotalTime=0; 416 | // 紧急程度权值起算起点 417 | int emergencyStart = POINT_NUM; 418 | for (int i = 0; i < slice.size() - 1; i++) { 419 | // 获取起始点和终点信息 420 | Point startPoint = info.get(slice.get(i)); 421 | Point endPoint = info.get(slice.get(i+1)); 422 | 423 | // 计算两点之间路径长度 424 | double routeLength = routeLength(startPoint.getX(), startPoint.getY(), endPoint.getX(), endPoint.getY()); 425 | // 行驶时间 426 | double routeTime = routeLength / CAR_SPEED; 427 | 428 | currCarTotalTime+=routeTime; 429 | // 1、是否符合时间窗 430 | if (currCarTotalTime>endPoint.getEnd()) { 431 | scoreCount=scoreCount-(currCarTotalTime-endPoint.getEnd())*TIME_WINDOW_WEIGHT; 432 | } 433 | // 当前车辆运行时间还要加上受灾点需要的服务时间 434 | currCarTotalTime+=endPoint.getServiceTime(); 435 | // 2、车辆运载的货物重量是否满足受灾点需求 436 | if (carCurrCapacity newPop = new ArrayList<>(); 484 | while (newPop.size() children = Chromosome.genetic(p1, p2); 489 | for (Chromosome child : children) { 490 | if (Chromosome.isGoodChromosome(child)) { 491 | newPop.add(child); 492 | } 493 | } 494 | } 495 | // 保证新生成的子代长度与原父代长度相等 496 | while (newPop.size()>POP_SIZE) { 497 | newPop.remove(r.nextInt(newPop.size())); 498 | } 499 | pop.clear(); 500 | pop=newPop; 501 | } 502 | ``` 503 | 504 | 505 | 506 | * **择优选择父代:** 507 | 508 | 这里我们使用轮盘赌算法选择父代 509 | 510 | ```java 511 | private Chromosome getParentChromosome() { 512 | // 轮盘赌选中的部分 513 | double slice = Math.random() * totalScore; 514 | //System.out.println("轮盘赌 slice:"+slice); 515 | //System.out.println("轮盘赌 total:"+totalScore); 516 | double sum = 0d; 517 | //int targetCount =0; 518 | for (Chromosome chromosome : pop) { 519 | //targetCount++; 520 | sum+=chromosome.getScore(); 521 | // 轮盘赌选中 522 | if (sum>slice) { 523 | //System.out.println("当前命中的目标为:"+targetCount); 524 | return chromosome; 525 | } 526 | //System.out.println("轮盘赌选中个体为:\n"+chromosome); 527 | } 528 | return pop.get(pop.size()-1); 529 | } 530 | ``` 531 | 532 | 533 | 534 | * **交叉算法:** 535 | 536 | ```java 537 | public static List genetic(Chromosome p1,Chromosome p2) { 538 | if (p1==null || p2==null) return null; 539 | if (p1.getGeneSize()!=p2.getGeneSize()) return null; 540 | List children = new ArrayList<>(); 541 | 542 | // 起始点可能的集合(后期起始点可能不会设置为0) 543 | Set startPointSet = new HashSet<>(); 544 | startPointSet.add(0); 545 | List> possibleGeneticPair = getPossibleGeneticPair(p1, p2, startPointSet); 546 | // 获取交叉位置 547 | Pair pair = possibleGeneticPair.get(r.nextInt(possibleGeneticPair.size())); 548 | Chromosome c1 = p1.clone(); 549 | Chromosome c2 = p2.clone(); 550 | int[] c1Gene = c1.getGene(); 551 | int[] c2Gene = c2.getGene(); 552 | int left = pair.fst; 553 | int right = pair.snd; 554 | for (int i = left; i <=right ; i++) { 555 | int tmp = c1Gene[i]; 556 | c1Gene[i]=c2Gene[i]; 557 | c2Gene[i]=tmp; 558 | } 559 | children.add(c1); 560 | children.add(c2); 561 | return children; 562 | } 563 | ``` 564 | 565 | 566 | 567 | * **获取可能的交叉位置:** 568 | 569 | 这里有两种方式选择可交叉位置: 570 | 571 | **1、**先将所有可能交叉的位置选出来,在随机选择 572 | 573 | **2、**随机生成交叉位置,再进行判断 574 | 575 | 这里我们之所以**选择方案1**,是因为这种方式命中率最高,主要原因还是在染色体限制条件下,可交换位置还是比较少的 576 | 577 | **方案一的时间复杂度,稳定在 O(n^2)** 578 | 579 | **但是方案二的时间复杂度,在 O(1) - O(∞) 中徘徊** 580 | 581 | 经过我的测试,**方案2**平均耗时是**方案1**的两倍以上 582 | 583 | ```java 584 | public static List> getPossibleGeneticPair(Chromosome p1,Chromosome p2,SetstartPointSet) { 585 | if (p1==null || p2==null) return null; 586 | if (p1.getGeneSize()!=p2.getGeneSize()) return null; 587 | List> res = new ArrayList<>(); 588 | int geneSize = p1.getGeneSize(); 589 | // 暂时不包含首尾 590 | for (int l = 1; l (l,r)); 594 | } 595 | } 596 | } 597 | return res; 598 | } 599 | ``` 600 | 601 | 602 | 603 | ### 4、种群变异 604 | 605 | ``` 606 | GenaticAlgorithm/mutation() 607 | - Chromosome/mutation() 608 | ``` 609 | 610 | * **种群变异:** 611 | 612 | 变异也不是每个个体都要发生变异的 613 | 614 | 我们设置变异概率,使变异发生在一个合理的范围内 615 | 616 | ```java 617 | private void mutation() { 618 | for (Chromosome chromosome : pop) { 619 | if (Math.random()=0) { 653 | left = r.nextInt(geneSize - 2) + 1; 654 | right = r.nextInt(geneSize - 2) + 1; 655 | maxIterNum--; 656 | if (maxIterNum<0) { 657 | left=0; 658 | right=0; 659 | } 660 | } 661 | // 交换目标基因 662 | int tmp = gene[left]; 663 | gene[left] = gene[right]; 664 | gene[right]=tmp; 665 | } 666 | ``` 667 | 668 | 669 | 670 | ### 5、折线图绘制 671 | 672 | 使用图标能最直观的反应我们的数据走势 673 | 674 | 需要下面这两张图 675 | 676 | * 总适应度变化折线图 677 | * 最佳适应度-最坏适应度变化图 678 | 679 | 680 | 681 | #### 1)折线图工具类 682 | 683 | 这里我们使用 **jfree** 绘制折线图 684 | 685 | * **导入 Maven 依赖** 686 | 687 | ```xml 688 | 689 | jfree 690 | jfreechart 691 | 1.0.13 692 | 693 | ``` 694 | 695 | 696 | 697 | * **编写工具函数** 698 | 699 | 工具函数我已经封装好了,各位直接拷到自己项目里就行 700 | 701 | ```java 702 | public class DrawingTools extends ApplicationFrame { 703 | private String titleFont; 704 | private int titleFontSize; 705 | private String xyFont; 706 | private int xyFontSize; 707 | 708 | DrawingTools() { 709 | this("Charts"); 710 | } 711 | 712 | public DrawingTools(String appTitle) { 713 | super(appTitle); 714 | this.titleFont = "微软雅黑"; 715 | this.titleFontSize = 20; 716 | this.xyFont = "微软雅黑"; 717 | this.xyFontSize = 15; 718 | } 719 | 720 | /** 721 | * @param appTitle 标题 722 | * @param chartTitle 图标题 723 | * @param xName x轴命名 724 | * @param yName y轴命名 725 | * @param dataSet 数据集 726 | * @param types 线条种类 727 | */ 728 | public static void drawLineChart(String appTitle, String chartTitle, 729 | String xName, 730 | String yName, 731 | Map[] dataSet, 732 | String[] types) { 733 | DrawingTools tools = new DrawingTools(appTitle); 734 | IntervalXYDataset dataset = tools.getLineDataset(dataSet, types); 735 | JFreeChart chart = tools.getLineChart(chartTitle, xName, yName, dataset); 736 | 737 | //绘图模式化 738 | tools.setChartCSS(chart); 739 | ChartPanel chartPanel = new ChartPanel(chart); 740 | chartPanel.setPreferredSize(new java.awt.Dimension(900, 600)); 741 | tools.setContentPane(chartPanel); 742 | tools.pack(); 743 | RefineryUtilities.centerFrameOnScreen(tools); 744 | tools.setVisible(true); 745 | } 746 | 747 | private JFreeChart getLineChart(String title, String xName, String yName, XYDataset dataset) { 748 | /** 749 | * 图标标题,x轴名称,y轴名称,数据集合,图标显示方向,是否使用图示,是否生成工具栏,是否生成URL链接 750 | */ 751 | JFreeChart chart = ChartFactory.createXYLineChart( 752 | title, 753 | xName, 754 | yName, 755 | dataset, 756 | PlotOrientation.VERTICAL, 757 | true, 758 | true, 759 | false 760 | ); 761 | return chart; 762 | } 763 | 764 | 765 | 766 | /** 767 | * 自定义设置图表字体样式 768 | * 769 | * @param chart 770 | */ 771 | private void setChartCSS(JFreeChart chart) { 772 | //初始化 773 | chart.setBackgroundPaint(ChartColor.WHITE); 774 | XYPlot plot = chart.getXYPlot(); 775 | 776 | //标题 777 | TextTitle textTitle = chart.getTitle(); 778 | textTitle.setFont(new Font(titleFont, Font.BOLD, titleFontSize)); 779 | LegendTitle legendTitle = chart.getLegend(); 780 | legendTitle.setItemFont(new Font(titleFont, Font.PLAIN, titleFontSize)); 781 | 782 | 783 | //图表xy轴字体设置 784 | plot.getDomainAxis().setLabelFont(new Font(xyFont, Font.PLAIN, xyFontSize)); 785 | plot.getDomainAxis().setTickLabelFont(new Font(xyFont, Font.PLAIN, xyFontSize)); 786 | plot.getRangeAxis().setTickLabelFont(new Font(xyFont, Font.PLAIN, xyFontSize)); 787 | plot.getRangeAxis().setLabelFont(new Font(xyFont, Font.PLAIN, xyFontSize)); 788 | 789 | //设置背景色-xy轴格子色 790 | plot.setBackgroundPaint(ChartColor.WHITE); 791 | plot.setRangeGridlinePaint(ChartColor.lightGray); 792 | // plot.setDomainGridlinePaint(ChartColor.lightGray); 793 | 794 | //折线图渲染 795 | XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(); 796 | plot.setRenderer(renderer); 797 | // renderer.setPaint(ChartColor.BLACK); 798 | chart.getLegend().setPosition(RectangleEdge.RIGHT); 799 | 800 | } 801 | 802 | /** 803 | * @param dataSets int:double 804 | * @param types 折线的种类 805 | * @return 806 | */ 807 | private IntervalXYDataset getLineDataset(Map[] dataSets, String[] types) { 808 | 809 | XYSeriesCollection dataSet = new XYSeriesCollection(); 810 | int index = 0; 811 | for (String type : types) { 812 | XYSeries series = new XYSeries(type); 813 | for (Map.Entry data : dataSets[index++].entrySet()) { 814 | series.add(data.getKey(), data.getValue()); 815 | } 816 | dataSet.addSeries(series); 817 | } 818 | return dataSet; 819 | } 820 | } 821 | ``` 822 | 823 | 824 | 825 | #### 2)封装工具类,使其符合路径规划问题 826 | 827 | ```java 828 | /** 829 | * 绘制总适应度函数变化折线图 830 | * @param dataSet 831 | */ 832 | public static void drawTotalScoreGraph(Map[] dataSet) { 833 | String[] types = {"总适应度"}; 834 | DrawingTools.drawLineChart("总适应度变化","总适应度变化","迭代次数","适应度得分",dataSet,types); 835 | } 836 | 837 | 838 | /** 839 | * 绘制最优/最差 适应度变化折线图 840 | * @param dataSet 绘制图标使用的数据集 841 | */ 842 | public static void drawBestWorstScoreGraph(Map[] dataSet) { 843 | String[] types = {"最佳适应度", "最坏适应度"}; 844 | DrawingTools.drawLineChart("最佳/最坏适应度变化","最佳/最坏适应度变化","迭代次数","适应度得分",dataSet,types); 845 | } 846 | ``` 847 | 848 | 849 | 850 | 851 | 852 | ## 四、测试、参数调整与数据分析 853 | 854 | ### 1、测试 855 | 856 | ![测试结果1](https://gitee.com/faro/images/raw/master/img/20220311152828.jpg) 857 | 858 | ![测试结果2](https://gitee.com/faro/images/raw/master/img/20220311152835.jpg) 859 | 860 | 经过上述测试,我们发现,计算结果并没有按照理想的曲线递增,而且个体适应度分数区别不大,这样 861 | 862 | 轮盘赌就近似与随机算法了,只有**大幅提高种群个数**才能抹平这种差异 863 | 864 | 关于这点,我会在下面的参数调整部分细讲,测试部分我们需要保证该算法是切实有效的 865 | 866 | 这时候参数写在配置文件中的好处就开始凸显了 867 | 868 | ![待测试的部分](https://gitee.com/faro/images/raw/master/img/20220311154413.png) 869 | 870 | 871 | 872 | * **仅针对时间窗权重进行计算:** 873 | 874 | 可以发现,总适应度是先呈现一个上升趋势,然后有轻微下降,最后开始区域平稳的 875 | 876 | 说明单纯考虑时间窗对于该算法是有效的 877 | 878 | ![2](https://gitee.com/faro/images/raw/master/img/20220311160446.jpg) 879 | 880 | ![1](https://gitee.com/faro/images/raw/master/img/20220311160439.jpg) 881 | 882 | 883 | 884 | * **仅针对载重进行考虑:** 885 | 886 | 以载重为考虑先决条件的话,总适应度变化不大 887 | 888 | 可能是与我们的补偿分数设置太大有关 889 | 890 | 但是最佳/最坏适应度变化曲线符合遗传算法的要求,可以判定对载重的判断正确 891 | 892 | ![2](https://gitee.com/faro/images/raw/master/img/20220311160644.jpg) 893 | 894 | ![1](https://gitee.com/faro/images/raw/master/img/20220311160637.jpg) 895 | 896 | 897 | 898 | * **仅针对应急程度考虑:** 899 | 900 | **针对应急程度考虑**的话,其变化规律与**仅考虑时间窗**一致 901 | 902 | 可以判定其运转正常 903 | 904 | ![1](https://gitee.com/faro/images/raw/master/img/20220311160842.jpg) 905 | 906 | ![2](https://gitee.com/faro/images/raw/master/img/20220311160847.jpg) 907 | 908 | 909 | 910 | 911 | 912 | ### 2)参数调整 913 | 914 | 但是我们要清楚,为了让计算时间在可接受范围,**种群个数和迭代次数都必须限定在一定范围内!!** 915 | 916 | 而且,经过上面的测试,我们发现,在针对部分参数的考量中,折线变化不明显,这会严重影响决策者的判断 917 | 918 | 所以,我们要对参数进行调整 919 | 920 | * **基于种群参数设置** 921 | 922 | ```properties 923 | #种群大小 924 | #种群在达到 1000 的时候,已经开始出现阻塞现象了 925 | #POP_SIZE=1000 926 | POP_SIZE=200 927 | 928 | #基因长短(在路径规划中,基因长短是与路径条数和车辆个数有关的,没法单独配置) 929 | #CHROMOSOME_SIZE=6 930 | 931 | #迭代次数 932 | ITER_NUM=200 933 | ``` 934 | 935 | 936 | 937 | * **基于显示情况考虑的权值设置** 938 | 939 | ```properties 940 | # 时间窗权重 941 | TIME_WINDOW_WEIGHT=10 942 | #TIME_WINDOW_WEIGHT=0 943 | 944 | # 货物需求点权重 945 | GOOD_NEED_WEIGHT=10 946 | #GOOD_NEED_WEIGHT=0 947 | 948 | # 补偿得分 949 | #ORIGIN_SCORE=0 950 | ORIGIN_SCORE=0 951 | 952 | # 应急点紧急程度对应的权值 953 | EMERGENCY_WEIGHT=5 954 | #EMERGENCY_WEIGHT=5 955 | ``` 956 | 957 | 958 | 959 | * **基于变异情况的设置:** 960 | 961 | 因为我们的代码中,先期已经对大量不合理的情况进行了排除 962 | 963 | 所以我们的种群缺少多样性,导致迭代次数超过一定阈值后,会出现大量相似个体 964 | 965 | 这个时候如果变异概率太低,会导致个体逐渐趋同,影响结果的产生 966 | 967 | **小变异产生的结果:** 968 | 969 | ![小变异1](https://gitee.com/faro/images/raw/master/img/20220311161942.jpg) 970 | 971 | ![小变异2](https://gitee.com/faro/images/raw/master/img/20220311161947.jpg) 972 | 973 | 974 | 975 | **大变异差生的结果:** 976 | 977 | 可以看到,大变异后产生的个体明显更具差异性,其获得相对最优值的概率也越高 978 | 979 | ![大变异](https://gitee.com/faro/images/raw/master/img/20220311162021.jpg) 980 | 981 | ![大变异2](https://gitee.com/faro/images/raw/master/img/20220311162025.jpg) 982 | 983 | ```properties 984 | #变异概率(调大调小变异概率) 985 | MUTATION_RATE=0.1 986 | 987 | #最大变异长度 这个和基因长度有关 988 | #MAX_MUTATION_NUM=3 989 | ``` 990 | 991 | 992 | 993 | ### 3)数据分析 994 | 995 | 接下来,我们放开所有参数条件,看数据折现是什么情况的 996 | 997 | 理想情况下,应该是**总适应度呈上升态势** 998 | 999 | 最佳适应度应该是由一个高位,逐渐递减,最后趋于缓和 1000 | 1001 | 最坏适应度应该是由低位逐渐上升,最后区域缓和 1002 | 1003 | 但是最佳、最坏二者最后应该是收敛域同一个数据范围的 1004 | 1005 | **手绘一下的话,应该是这个表现形式:** 1006 | 1007 | ![变化曲线](https://gitee.com/faro/images/raw/master/img/20220311165424.png) 1008 | 1009 | 1010 | 1011 | **最后事实也确实如我所料,折线走向和我预期的一致:** 1012 | 1013 | ![1](https://gitee.com/faro/images/raw/master/img/20220311165503.jpg) 1014 | 1015 | ![2](https://gitee.com/faro/images/raw/master/img/20220311165509.jpg) 1016 | 1017 | 这样我大致可以判断,算法和参数大致是符合路径规划需求的 1018 | 1019 | **最后获取的最佳个体基因如下:** 1020 | 1021 | `[0, 20, 5, 8, 16, 23, 10, 24, 0, 3, 13, 2, 11, 4, 15, 12, 21, 14, 7, 0, 22, 6, 17, 9, 26, 18, 19, 1, 0, 25, 0]` 1022 | 1023 | 为了图方便,我的数据直接用 Map 存的,**其具体参数如下:** 1024 | 1025 | ```java 1026 | public class Info { 1027 | private static Map info = new HashMap<>(); 1028 | static { 1029 | info.put("start",new Point("start",100d,100d,0d,0d,0d,Double.MAX_VALUE,0)); // 起始点信息 1030 | info.put("A",new Point("A",160d,180d,5.6d,5.18d,0d,3.4d,7)); 1031 | info.put("B",new Point("B",170d,50d,9.8d,1.48d,0d,7.1d,190)); 1032 | info.put("C",new Point("C",30d,120d,1.3d,3.78d,0d,5.6d,2)); 1033 | info.put("D",new Point("D",60d,90d,4.7d,1.08d,0d,3.2d,41)); 1034 | info.put("E",new Point("E",250d,30d,8.3d,3.38d,0d,10d,50)); 1035 | info.put("F",new Point("F",190d,110d,1.4d,3.68d,0d,6.2d,23)); 1036 | info.put("G",new Point("G",80d,130d,4.7d,1.98d,0d,3.8d,4)); 1037 | info.put("H",new Point("H",140d,50d,2.8d,2.28d,0d,4.7d,420)); 1038 | info.put("I",new Point("I",30d,20d,4.6d,2.58d,0d,7d,10)); 1039 | info.put("J",new Point("J",10d,70d,2.6d,2.88d,0d,6d,1)); 1040 | info.put("K",new Point("K",70d,120d,2.6d,3.44d,0d,4d,100)); 1041 | info.put("L",new Point("L",90d,170d,4.6d,5.88d,0d,9d,1)); 1042 | info.put("M",new Point("M",30d,180d,2.6d,3.24d,0d,3d,1)); 1043 | info.put("N",new Point("N",20d,150d,6.6d,1.89d,0d,3d,9)); 1044 | info.put("O",new Point("O",90d,140d,2.6d,5.88d,0d,5.3d,122)); 1045 | info.put("P",new Point("P",160d,40d,7.6d,4.88d,0d,9d,39)); 1046 | 1047 | 1048 | info.put("Q",new Point("Q",190d,80d,2.6d,2.97d,0d,2d,210)); 1049 | info.put("R",new Point("R",140d,150d,5.6d,3.65d,0d,4d,33)); 1050 | info.put("S",new Point("S",70d,50d,8.6d,1.97d,0d,5d,23)); 1051 | info.put("T",new Point("T",200d,50d,6.6d,1.48d,0d,1d,723)); 1052 | info.put("U",new Point("U",220d,90d,2.6d,4.88d,0d,2d,34)); 1053 | info.put("V",new Point("V",40d,60d,7.6d,1.88d,0d,2d,782)); 1054 | info.put("W",new Point("W",90d,110d,9.6d,0.88d,0d,4d,123)); 1055 | info.put("X",new Point("X",0d,80d,8.6d,2.89d,0d,2d,73)); 1056 | info.put("Y",new Point("Y",10d,120d,2.6d,1.49d,0d,6d,35)); 1057 | info.put("Z",new Point("Z",40d,150d,9.6d,4.81d,0d,9d,92)); 1058 | } 1059 | 1060 | public static Map getInfo() { 1061 | return info; 1062 | } 1063 | } 1064 | ``` 1065 | 1066 | ```java 1067 | public class Point { 1068 | // 受灾点名称 1069 | private String name; 1070 | // 受灾点坐标 1071 | private double x; 1072 | private double y; 1073 | // 受灾点所需物资 1074 | private double need; 1075 | // 受灾点服务时间 1076 | private double serviceTime; 1077 | // 受灾点时间窗 1078 | private double start; 1079 | private double end; 1080 | // 紧急程度 1081 | private int emergency; 1082 | } 1083 | ``` 1084 | 1085 | --------------------------------------------------------------------------------