1. 程式人生 > >遺傳演算法框架-基於java jenetics庫實現

遺傳演算法框架-基於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>

 

 

先看看我們提供的這個框架,能怎樣簡化業務側程式碼吧:

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,這個是每個業務都不同的地方,需要單獨業務單獨編碼

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類,既:適應度函式指向類:

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; } }

  

看看程式的輸出:

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演算法的問題在於不太穩定,只能代表一種優化趨勢,導致這樣的原因有很多,最重要的原因還是在於智慧演算法一般使用的場景都是由於傳統演算法無能為力的場景,或者無法窮舉的場景。

下面看看加大種群迭代次數是否能解決:

options.setMaxLimits(100);
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個數字,會怎樣:

@Override
	public Genotype<IntegerGene> loadGenotype() {
		Genotype<IntegerGene> genotype=Genotype.of(
				IntegerChromosome.of(0, 1, IntRange.of(50))
		);

		return genotype;
	}
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, 

  

哇塞,簡直了,這結果。。。

再來:

options.setMaxLimits(2000);
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,   

  

體會到了不確定的痛苦了嗎?

再來:

//        options.setEnableMaxLimits(true);
//        options.setMaxLimits(2000);
        options.setEnableSteadyFitness(true);         //設定為穩定適應度函式值預設
        options.setSteadyFitnessValue(1000);          //當連續1000次的種群迭代的最優適應度函式值都穩定的時候,穩定就是分值沒有超越當前的最好分值,然後才停止演算法
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

完整下載