遗传算法框架-基于java jenetics库实现

 本篇并非介绍如何从0开始开发遗传算法框架,反而推荐各位使用已有的GA库jenetics来做遗传算法。 

 GA算法的逻辑还是贴下:

 

好了,下面介绍的是基于jenetics开发的更贴近业务侧的框架,以及使用方法。

pom依赖,毕竟java的嘛,就不要用matlab、R、python这些了

复制代码
<!-- https://mvnrepository.com/artifact/io.jenetics/jenetics -->
        <dependency>
            <groupId>io.jenetics</groupId>
            <artifactId>jenetics</artifactId>
            <version>5.1.0</version>
        </dependency>
        <dependency>
            <groupId>io.jenetics</groupId>
            <artifactId>jenetics.ext</artifactId>
            <version>5.1.0</version>
        </dependency>
复制代码

 

 

先看看我们提供的这个框架,能怎样简化业务侧代码吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(5);                            //需要指定一个线程池,因为GA算法需要很多运算
 
        Demo1SolutionConverter demo1SolutionConverter = new Demo1SolutionConverter();                 //这个class中定义了GA算法所需的底层基因序列的定义、以及业务解决方案class与基因的互相转换逻辑
        Demo1FitnessBuilder fitnessBuilder = new Demo1FitnessBuilder();                               //这个class定义了适应度函数的指向(遗传算法非常依赖适应度函数,非常非常依赖,重中之重)
 
        EngineInvokeOptions options = new EngineInvokeOptions();                                      //引擎调用默认参数,如需改写默认参数单独修改即可
        options.setFitnessBuilder(fitnessBuilder);
        options.setEnableMaxLimits(true);                                                             //设置为停止条件为最大种群次数限制
        options.setMaxLimits(20);                                                                     //设置为最大次数限制为20次,既:20次种群迭代
 
        for(int idx=0;idx<20;idx++) {                                                                 //连续执行20次看效果
            MinMaxScaler minMaxScaler = new MinMaxScaler(100, false);
 
            GAEngine gaEngine = new GAEngine(50, executorService);
 
            Demo1SolutionConverter.Demo1Solution solution = (Demo1SolutionConverter.Demo1Solution) gaEngine.generate(demo1SolutionConverter, minMaxScaler, options);
            System.out.println(solution.toString());
        }
 
        executorService.shutdown();
    }  

 

我们先看看核心class:Demo1SolutionConverter,这个是每个业务都不同的地方,需要单独业务单独编码

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
public class Demo1SolutionConverter extends DefaultSolutionConverter {                                         //DefaultSolutionConverter是框架里的默认实现,已经实现了很多默认方法
    @Override
    public AbstractSolution convert2Solution(EvolvingSolutionInfo solutionInfo) {
        Demo1Solution solution=new Demo1Solution();
        solution.setFitnessValues(solutionInfo.getFitnessValues());
 
        Genotype<IntegerGene> geneMap=solutionInfo.getMap();                                           //这个是获取基因架
 
        Chromosome<IntegerGene> chromosome=geneMap.getChromosome();                                    //获取默认的也就是第一个基因条
 
        for(int idx=0;idx<chromosome.length();idx++)                                                   //开始业务解码
        {
            IntegerGene integerGene=chromosome.getGene(idx);
            if(integerGene.intValue()==1)
            {
                solution.getSelectedIds().add(idx);                                            //这里实现的解码逻辑:共10个数字,有2种可能0和1,0代表不选中,1代表选中,选中了就会加入selectedIds这个List里
            }
        }
 
        return solution;
    }
 
    @Override
    public Genotype<IntegerGene> loadGenotype() {                                                          //这个是唯一一个DefaultSolutionConverter种的抽象方法,必须实现,用来定义基因序列的组成
        Genotype<IntegerGene> genotype=Genotype.of(
                IntegerChromosome.of(0, 1, IntRange.of(10))                                    //10个基因,每个基因只有2种可能,0或者1
        );
 
        return genotype;
    }
 
    public double test(AbstractSolution solution)                                                        //这个是适应度函数定义,要注意入参和出参,这2个是固定的
    {                                                                                                    //test是method名,待会会在后面介绍,会在其他class中指定过来
        Demo1Solution s=(Demo1Solution)solution;
 
        long 偶数个数=s.getSelectedIds().stream().filter(f->f%2==0).count();
        long 奇数个数=s.getSelectedIds().stream().filter(f->f%2!=0).count();
 
        double score=(-偶数个数)+(+奇数个数);                                                          //score是适应度函数的分值,越小越优,由于我们希望不要奇数、只要偶数,所以偶数部分为负号、奇数部分为正号
        return score;
    }
 
    @Data
    public class Demo1Solution extends DefaultSolution {                                                   //这个是业务解码后存放的class,很好理解
 
        private List<Integer> selectedIds=new ArrayList<>();
 
        @Override
        public String toString() {
            StringBuilder stringBuilder=new StringBuilder();
 
            for(int id:selectedIds)
            {
                stringBuilder.append(id);
                stringBuilder.append(", ");
            }
 
            return stringBuilder.toString();
        }
    }
}  

 

然后再看看Demo1FitnessBuilder类,既:适应度函数指向类:

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
public class Demo1FitnessBuilder implements FitnessBuilder {
    @Override
    public Tuple2<Map<String, Object>, List<Tuple3<String, String, Double>>> build() {
 
        Map<String, Object> objConfigs= buildObjectAndConfigs();
        List<Tuple3<String, String, Double>> fitnessConfigs=buildFitnessConfigs();
 
        return Tuples.of(objConfigs, fitnessConfigs);
    }
 
 
    private Map<String, Object> buildObjectAndConfigs() {
 
        Map<String, Object> configs=new HashMap<>();
 
        Demo1SolutionConverter obj=new Demo1SolutionConverter();                             //这里定义的是n个适应度函数所在的对象(此处只定义了1个,也可以多个)
        configs.put("obj", obj);
 
        return configs;
    }
 
    private List<Tuple3<String, String, Double>> buildFitnessConfigs() {
        List<Tuple3<String, String, Double>> fitnessConfigs = new ArrayList<>();
 
        fitnessConfigs.add(Tuples.of("obj", "test", 1D));                                    //obj和上面的对应,test指明了适应度函数是在obj这个对象中方法名为test的函数                                                                                             //此处也可以多个                                                                                             //1D代表默认权重,也可以定期较低的权重,比如:0.5D这种  
 
        return fitnessConfigs;
    }
}

  

看看程序的输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 3, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,

  

如同大家所见,GA算法的问题在于不太稳定,只能代表一种优化趋势,导致这样的原因有很多,最重要的原因还是在于智能算法一般使用的场景都是由于传统算法无能为力的场景,或者无法穷举的场景。

下面看看加大种群迭代次数是否能解决:

1
options.setMaxLimits(100);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,
0, 2, 4, 6, 8,

  

算是表面解决了,但其实还有很多因素能导致不稳定,比如,我们从当前的10个数字,enlarge到50个数字,会怎样:

1
2
3
4
5
6
7
8
@Override
    public Genotype<IntegerGene> loadGenotype() {
        Genotype<IntegerGene> genotype=Genotype.of(
                IntegerChromosome.of(0, 1, IntRange.of(50))
        );
 
        return genotype;
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
0, 2, 4, 5, 6, 8, 10, 12, 14, 15, 16, 17, 18, 20, 22, 24, 26, 28, 30, 32, 34, 35, 36, 38, 40, 41, 42, 44, 46,
0, 2, 4, 6, 8, 10, 11, 12, 13, 14, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
2, 4, 5, 6, 8, 10, 12, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 6, 8, 10, 12, 14, 16, 18, 22, 24, 26, 28, 30, 32, 34, 35, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 8, 14, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 41, 42, 44, 46, 48,
0, 2, 4, 5, 6, 8, 10, 12, 14, 15, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 47, 48, 49,
0, 2, 4, 6, 8, 10, 12, 13, 14, 16, 18, 20, 22, 24, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46,
0, 2, 4, 6, 8, 10, 12, 13, 16, 18, 19, 22, 24, 26, 28, 30, 31, 32, 36, 38, 40, 42, 43, 44, 45, 46, 48,
0, 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 21, 22, 24, 25, 26, 28, 30, 32, 34, 38, 40, 42, 44, 45, 46, 48,
0, 1, 2, 4, 5, 6, 8, 10, 11, 12, 14, 16, 18, 20, 21, 22, 23, 24, 26, 28, 30, 32, 36, 38, 40, 42, 44, 46, 48,
2, 4, 6, 8, 10, 12, 14, 16, 17, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 44, 46, 48,
0, 2, 6, 8, 10, 12, 14, 18, 20, 22, 24, 26, 28, 32, 34, 36, 38, 40, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 38, 40, 42, 43, 44, 46, 48, 49,
0, 2, 4, 6, 8, 9, 10, 12, 14, 18, 22, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 45, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 22, 24, 26, 32, 34, 36, 38, 40, 42, 44, 45, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 24, 26, 28, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 13, 14, 16, 18, 20, 22, 24, 26, 28, 31, 32, 34, 38, 40, 42, 45, 46, 48,
0, 2, 4, 6, 8, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 27, 29, 30, 32, 34, 36, 38, 40, 46, 48,
0, 4, 6, 8, 10, 12, 14, 16, 20, 22, 24, 25, 26, 28, 30, 32, 34, 36, 38, 40, 44, 46, 48,
0, 2, 4, 8, 10, 12, 14, 16, 18, 20, 21, 22, 24, 26, 28, 32, 34, 36, 38, 40, 42, 44, 46, 48,

  

哇塞,简直了,这结果。。。

再来:

1
options.setMaxLimits(2000);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,   

  

体会到了不确定的痛苦了吗?

再来:

1
2
3
4
//        options.setEnableMaxLimits(true);
//        options.setMaxLimits(2000);
        options.setEnableSteadyFitness(true);         //设置为稳定适应度函数值默认
        options.setSteadyFitnessValue(1000);          //当连续1000次的种群迭代的最优适应度函数值都稳定的时候,稳定就是分值没有超越当前的最好分值,然后才停止算法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48,

  

遗传算法是非常耗费算力的,能其他算法就尽量其他算法来做,真的。虽然GA也有很多优点,比如:

  1. 当无法穷举时,这个算法就是好的
  2. 当数据量较少时,这个算法也挺稳定(要调好参数)
  3. 当计算什么是好,什么是坏时的逻辑是非线性的时候,GA算法也是个很好的选项

完整代码下载,带demo+自研框架+底层是jenetics

完整下载 

 

posted @   McKay  阅读(2260)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示