├── 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 |
5 |
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 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
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 |
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 |
--------------------------------------------------------------------------------
/.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 | 
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 | 
857 |
858 | 
859 |
860 | 经过上述测试,我们发现,计算结果并没有按照理想的曲线递增,而且个体适应度分数区别不大,这样
861 |
862 | 轮盘赌就近似与随机算法了,只有**大幅提高种群个数**才能抹平这种差异
863 |
864 | 关于这点,我会在下面的参数调整部分细讲,测试部分我们需要保证该算法是切实有效的
865 |
866 | 这时候参数写在配置文件中的好处就开始凸显了
867 |
868 | 
869 |
870 |
871 |
872 | * **仅针对时间窗权重进行计算:**
873 |
874 | 可以发现,总适应度是先呈现一个上升趋势,然后有轻微下降,最后开始区域平稳的
875 |
876 | 说明单纯考虑时间窗对于该算法是有效的
877 |
878 | 
879 |
880 | 
881 |
882 |
883 |
884 | * **仅针对载重进行考虑:**
885 |
886 | 以载重为考虑先决条件的话,总适应度变化不大
887 |
888 | 可能是与我们的补偿分数设置太大有关
889 |
890 | 但是最佳/最坏适应度变化曲线符合遗传算法的要求,可以判定对载重的判断正确
891 |
892 | 
893 |
894 | 
895 |
896 |
897 |
898 | * **仅针对应急程度考虑:**
899 |
900 | **针对应急程度考虑**的话,其变化规律与**仅考虑时间窗**一致
901 |
902 | 可以判定其运转正常
903 |
904 | 
905 |
906 | 
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 | 
970 |
971 | 
972 |
973 |
974 |
975 | **大变异差生的结果:**
976 |
977 | 可以看到,大变异后产生的个体明显更具差异性,其获得相对最优值的概率也越高
978 |
979 | 
980 |
981 | 
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 | 
1008 |
1009 |
1010 |
1011 | **最后事实也确实如我所料,折线走向和我预期的一致:**
1012 |
1013 | 
1014 |
1015 | 
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 |
--------------------------------------------------------------------------------