Genetic Algorithm遗传算法学习

参考资料:http://blog.csdn.net/b2b160/article/details/4680853/#comments(冒昧的用了链接下的几张图)

百度百科:http://baike.baidu.com/link?url=FcwTBx_yPcD5DDEnN1FqvTkG4QNllkB7Yis6qFOL65wpn6EdT5LXFxUCmv4JlUfV3LUPHQGdYbGj8kHVs3GuaK

算法介绍

遗传算法是模拟达尔文生物进化论的自然选择和遗传学进化机理的计算模型。运用到了生物学中“适者生存,优胜劣汰”的原理。在每一次的进化过程中,把拥有更好环境适应性的基因传给下一代,直到最后的个体满足特定的条件,代表进化的结束,GA(后面都以GA代称为遗传算法的意思)算法是一种利用生物进化理论来搜索最优解的一种算法。

算法原理

算法的基本框架

了解算法的基本框架是理解整个算法的基础,算法的框架包含编码、适应度函数、初始群体的选择。先假设本例子的目标函数如下,求出他的最大值

f(x) = x1 * x1 + x2 * x2; 1<= x1 <=7, 1<= x2<=7

1、适应度函数。适应度函数是用来计算个体的适应值计算,顾名思义,适应值越高的个体,环境适应性越好,自然就要有更多的机会把自己的基因传给下一代,所以,其实适应度函数在这里起着一个过滤条件的作用。在本例子,目标函数总为非负值,并且以函数最大化为优化目标,所以可以将函数的值作为适应值。

2、编码。编码指的是将个体的信息表示成编码的字符串形式。如果个体变量时数字的形式,可以转为二进制的方式。

算法的群体选择过程

这个过程是遗传算法的核心过程,在里面分为了3个小的步骤,选择,交叉,变异。

1、初始个体的选择过程。就是挑选哪些个体作为即将产生下一代的个体呢。过程如下:

(1).利用适值函数,计算每个个体的适值,计算每个个体的适值占总和的百分比。

(2).根据百分比为每个个体划定一定的所属区间。

(3).产生一个[0, 1]的小数,判断这个小数点落在哪个个体的区间内,就表明要选出这个个体。这里其实就已经蕴含着把高适值的个体优先传入下一代,因为适值高,有更高的几率小数是落在自己的区间内的。

用图示范的形式表现如下:

2、交叉运算。个体的交叉运算过程的步骤细节如下:

(1).首先对于上个选择步骤选择来的个体进行随机的两两配对。

(2).取出其中配对的一对个体,随机设定一个交叉点,2个个体的编码的交叉点后的编码值进行对调,生成新的2个个体编码。

(3).所有的配对的个体都执行步骤(2)操作,最后加入到一个结果集合中。

交叉运算的方式又很多,上面用的方法是其中比较常用的单点交叉方式。

用图示范的形式表现如下:

3.变异运算。变异运算过程的步骤细节如下:

(1).遍历从交叉运算所得结果的结果集,取出集中一个个体编码,准备做变异操作

(2).产生随机的一个变异点位置。所选个体的变异点位置的值做变异操作,将他的值取为反向的值。

(3).将所有的交叉运算所得的结果集中的元素都执行步骤(2)操作。

用图示范的形式如下:

整个遗传算法的原理过程,用一个流程图的表现形式如下:

算法代码实现

算法代码的测试用例正如算法原理所举的一样,遗传进化的阈值条件为:个体中产生了使目标函数最大化值的个体,就是基因为111111。

GATool.java:

package GA;

import java.util.ArrayList;
import java.util.Random;

/**
 * 遗传算法工具类
 *
 * @author lyq
 *
 */
public class GATool {
	// 变量最小值
	private int minNum;
	// 变量最大值
	private int maxNum;
	// 单个变量的编码位数
	private int codeNum;
	// 初始种群的数量
	private int initSetsNum;
	// 随机数生成器
	private Random random;
	// 初始群体
	private ArrayList<int[]> initSets;

	public GATool(int minNum, int maxNum, int initSetsNum) {
		this.minNum = minNum;
		this.maxNum = maxNum;
		this.initSetsNum = initSetsNum;

		this.random = new Random();
		produceInitSets();
	}

	/**
	 * 产生初始化群体
	 */
	private void produceInitSets() {
		this.codeNum = 0;
		int num = maxNum;
		int[] array;

		initSets = new ArrayList<>();

		// 确定编码位数
		while (num != 0) {
			codeNum++;
			num /= 2;
		}

		for (int i = 0; i < initSetsNum; i++) {
			array = produceInitCode();
			initSets.add(array);
		}
	}

	/**
	 * 产生初始个体的编码
	 *
	 * @return
	 */
	private int[] produceInitCode() {
		int num = 0;
		int num2 = 0;
		int[] tempArray;
		int[] array1;
		int[] array2;

		tempArray = new int[2 * codeNum];
		array1 = new int[codeNum];
		array2 = new int[codeNum];

		num = 0;
		while (num < minNum || num > maxNum) {
			num = random.nextInt(maxNum) + 1;
		}
		numToBinaryArray(array1, num);

		while (num2 < minNum || num2 > maxNum) {
			num2 = random.nextInt(maxNum) + 1;
		}
		numToBinaryArray(array2, num2);

		// 组成总的编码
		for (int i = 0, k = 0; i < tempArray.length; i++, k++) {
			if (k < codeNum) {
				tempArray[i] = array1[k];
			} else {
				tempArray[i] = array2[k - codeNum];
			}
		}

		return tempArray;
	}

	/**
	 * 选择操作,把适值较高的个体优先遗传到下一代
	 *
	 * @param initCodes
	 *            初始个体编码
	 * @return
	 */
	private ArrayList<int[]> selectOperate(ArrayList<int[]> initCodes) {
		double randomNum = 0;
		double sumAdaptiveValue = 0;
		ArrayList<int[]> resultCodes = new ArrayList<>();
		double[] adaptiveValue = new double[initSetsNum];

		for (int i = 0; i < initSetsNum; i++) {
			adaptiveValue[i] = calCodeAdaptiveValue(initCodes.get(i));
			sumAdaptiveValue += adaptiveValue[i];
		}

		// 转成概率的形式,做归一化操作
		for (int i = 0; i < initSetsNum; i++) {
			adaptiveValue[i] = adaptiveValue[i] / sumAdaptiveValue;
		}

		for (int i = 0; i < initSetsNum; i++) {
			randomNum = random.nextInt(100) + 1;
			randomNum = randomNum / 100;

			sumAdaptiveValue = 0;
			// 确定区间
			for (int j = 0; j < initSetsNum; j++) {
				if (randomNum > sumAdaptiveValue
						&& randomNum <= sumAdaptiveValue + adaptiveValue[j]) {
					//采用拷贝的方式避免引用重复
					resultCodes.add(initCodes.get(j).clone());
					break;
				} else {
					sumAdaptiveValue += adaptiveValue[j];
				}
			}
		}

		return resultCodes;
	}

	/**
	 * 交叉运算
	 *
	 * @param selectedCodes
	 *            上步骤的选择后的编码
	 * @return
	 */
	private ArrayList<int[]> crossOperate(ArrayList<int[]> selectedCodes) {
		int randomNum = 0;
		// 交叉点
		int crossPoint = 0;
		ArrayList<int[]> resultCodes = new ArrayList<>();
		// 随机编码队列,进行随机交叉配对
		ArrayList<int[]> randomCodeSeqs = new ArrayList<>();

		// 进行随机排序
		while (selectedCodes.size() > 0) {
			randomNum = random.nextInt(selectedCodes.size());

			randomCodeSeqs.add(selectedCodes.get(randomNum));
			selectedCodes.remove(randomNum);
		}

		int temp = 0;
		int[] array1;
		int[] array2;
		// 进行两两交叉运算
		for (int i = 1; i < randomCodeSeqs.size(); i++) {
			if (i % 2 == 1) {
				array1 = randomCodeSeqs.get(i - 1);
				array2 = randomCodeSeqs.get(i);
				crossPoint = random.nextInt(2 * codeNum - 1) + 1;

				// 进行交叉点位置后的编码调换
				for (int j = 0; j < 2 * codeNum; j++) {
					if (j >= crossPoint) {
						temp = array1[j];
						array1[j] = array2[j];
						array2[j] = temp;
					}
				}

				// 加入到交叉运算结果中
				resultCodes.add(array1);
				resultCodes.add(array2);
			}
		}

		return resultCodes;
	}

	/**
	 * 变异操作
	 *
	 * @param crossCodes
	 *            交叉运算后的结果
	 * @return
	 */
	private ArrayList<int[]> variationOperate(ArrayList<int[]> crossCodes) {
		// 变异点
		int variationPoint = 0;
		ArrayList<int[]> resultCodes = new ArrayList<>();

		for (int[] array : crossCodes) {
			variationPoint = random.nextInt(codeNum * 2);

			for (int i = 0; i < array.length; i++) {
				// 变异点进行变异
				if (i == variationPoint) {
					array[i] = (array[i] == 0 ? 1 : 0);
					break;
				}
			}

			resultCodes.add(array);
		}

		return resultCodes;
	}

	/**
	 * 数字转为二进制形式
	 *
	 * @param binaryArray
	 *            转化后的二进制数组形式
	 * @param num
	 *            待转化数字
	 */
	private void numToBinaryArray(int[] binaryArray, int num) {
		int index = 0;
		int temp = 0;
		while (num != 0) {
			binaryArray[index] = num % 2;
			index++;
			num /= 2;
		}

		//进行数组前和尾部的调换
		for(int i=0; i<binaryArray.length/2; i++){
			temp = binaryArray[i];
			binaryArray[i] = binaryArray[binaryArray.length - 1 - i];
			binaryArray[binaryArray.length - 1 - i] = temp;
		}
	}

	/**
	 * 二进制数组转化为数字
	 *
	 * @param binaryArray
	 *            待转化二进制数组
	 */
	private int binaryArrayToNum(int[] binaryArray) {
		int result = 0;

		for (int i = binaryArray.length-1, k=0; i >=0 ; i--, k++) {
			if (binaryArray[i] == 1) {
				result += Math.pow(2, k);
			}
		}

		return result;
	}

	/**
	 * 计算个体编码的适值
	 *
	 * @param codeArray
	 */
	private int calCodeAdaptiveValue(int[] codeArray) {
		int result = 0;
		int x1 = 0;
		int x2 = 0;
		int[] array1 = new int[codeNum];
		int[] array2 = new int[codeNum];

		for (int i = 0, k = 0; i < codeArray.length; i++, k++) {
			if (k < codeNum) {
				array1[k] = codeArray[i];
			} else {
				array2[k - codeNum] = codeArray[i];
			}
		}

		// 进行适值的叠加
		x1 = binaryArrayToNum(array1);
		x2 = binaryArrayToNum(array2);
		result = x1 * x1 + x2 * x2;

		return result;
	}

	/**
	 * 进行遗传算法计算
	 */
	public void geneticCal() {
		// 最大适值
		int maxFitness;
		//迭代遗传次数
		int loopCount = 0;
		boolean canExit = false;
		ArrayList<int[]> initCodes;
		ArrayList<int[]> selectedCodes;
		ArrayList<int[]> crossedCodes;
		ArrayList<int[]> variationCodes;

		int[] maxCode = new int[2*codeNum];
		//计算最大适值
		for(int i=0; i<2*codeNum; i++){
			maxCode[i] = 1;
		}
		maxFitness = calCodeAdaptiveValue(maxCode);

		initCodes = initSets;
		while (true) {
			for (int[] array : initCodes) {
				// 遗传迭代的终止条件为存在编码达到最大适值
				if (maxFitness == calCodeAdaptiveValue(array)) {
					canExit = true;
					break;
				}
			}

			if (canExit) {
				break;
			}

			selectedCodes = selectOperate(initCodes);
			crossedCodes = crossOperate(selectedCodes);
			variationCodes = variationOperate(crossedCodes);
			initCodes = variationCodes;

			loopCount++;
		}

		System.out.println("总共遗传进化了" + loopCount +"次" );
		printFinalCodes(initCodes);
	}

	/**
	 * 输出最后的编码集
	 *
	 * @param finalCodes
	 *            最后的结果编码
	 */
	private void printFinalCodes(ArrayList<int[]> finalCodes) {
		int j = 0;

		for (int[] array : finalCodes) {
			System.out.print("个体" + (j + 1) + ":");
			for (int i = 0; i < array.length; i++) {
				System.out.print(array[i]);
			}
			System.out.println();
			j++;
		}
	}

}

算法调用类Client.java:

package GA;

/**
 * Genetic遗传算法测试类
 * @author lyq
 *
 */
public class Client {
	public static void main(String[] args){
		//变量最小值和最大值
		int minNum = 1;
		int maxNum = 7;
		//初始群体规模
		int initSetsNum = 4;

		GATool tool = new GATool(minNum, maxNum, initSetsNum);
		tool.geneticCal();
	}
}

算法多次测试的输出结果:

测试1:

总共遗传进化了0次
个体1:111001
个体2:101010
个体3:101110
个体4:111111

测试2:

总共遗传进化了1次
个体1:101101
个体2:111111
个体3:100111
个体4:100111

测试3:

总共遗传进化了14次
个体1:110101
个体2:111111
个体3:110101
个体4:110011

算法结果分析

可以看到,遗传进化的循环次数还是存在着不确定定性的,原因在于测试的个体数太少,如果个体数比较多的话,几轮就可以出现111111这样的个体编码组了。从结果可以看出,总的还是能够向1多的方向发展的。

说说我对遗传算法的理解

通过实现了GA算法,觉得这有点集成算法的味道,因为这其实用到了跨学科的知识,用生物进化理论的知识,去作为一个搜索最优解的解决方案,而且算法本身理解和实现也不是特别的难。

时间: 2024-10-12 12:36:43

Genetic Algorithm遗传算法学习的相关文章

2. Genetic Algorithm(1) ——进化算法

本篇博文讲述基因算法(Genetic Algorithm),基因算法是最著名的进化算法. 内容依然来自博主的听课记录和教授的PPT. Outline 简单基因算法 个体表示 变异 重组 1. 简单基因算法(Simple Genetic Algorithm) Holland's早期的基因算法被认为是“简单的基因算法”或是“权威的基因算法”.(simple genetic algorithm or canonical genetic algorithm) 1. 直接举例说明 问题描述:利用遗传算法求

Genetic Algorithm Primary

Genetic algorithm is an algorithm which imitate the law of natural selection. The main step: Step 1:      Initialization     (Set Max evolutionary algebra and Create new individuals randomly) Step 2:      Individual evaluation (Evaluate the fitness o

遗传算法学习(解决寻路问题)

算法描述: 检查每个基因解决问题的能力,并量化此能力值 选出当前记忆库中的基因作为父代.选择原则是:解决能力越强的别选到的概率越大. 将选出的两者根据杂交率进行杂交,生成子代 根据变异率对子代进行变异 重复2.3.4,直到新的世代产生完毕 现在要使用这个算法来解决下列寻路问题: 有一个如图所示的随机生成的迷宫,在里面确定一个起点和一个重点,找到一条重起点到重点的通路. 类似的问题解决方法很多不再赘述,此处仅仅尝试使用遗传算法来解决. 基本思路就是把解决方案编码为基因,然后通过遗传算法的演进,找出

遗传算法学习--多目标优化中的遗传算法

转:https://www.cnblogs.com/lomper/p/3831428.html 在工程运用中,经常是多准则和对目标的进行择优设计.解决含多目标和多约束的优化问题称为:多目标优化问题.经常,这些目标之间都是相互冲突的.如投资中的本金最少,收益最好,风险最小~~ 多目标优化问题的一般数学模型可描述为: Pareto最优解(Pareto Optimal Solution) 使用遗传算法进行求解Pareto最优解: 权重系数变换法: 并列选择法: 基本思想: 将种群全体按子目标函数的数目

常用的机器学习知识(点)

常用的机器学习&数据挖掘知识(点) 声明:想做机器学习&数据挖掘的学弟学妹,可以看看,转载请说明出处... 常用的机器学习知识(点) Basis(基础): MSE(Mean Square Error 均方误差),LMS(LeastMean Square 最小均方),LSM(Least Square Methods 最小二乘法),MLE(MaximumLikelihood Estimation最大似然估计),QP(Quadratic Programming 二次规划), CP(Conditi

常用的机器学习&amp;数据挖掘知识点

Basis(基础):MSE(Mean Square Error 均方误差),LMS(LeastMean Square 最小均方),LSM(Least Square Methods 最小二乘法),MLE(MaximumLikelihood Estimation最大似然估计),QP(Quadratic Programming 二次规划), CP(Conditional Probability条件概率),JP(Joint Probability 联合概率),MP(Marginal Probabilit

常见的机器学习&amp;数据挖掘知识点

常见的机器学习&数据挖掘知识点 转载请说明出处 Basis(基础): MSE(Mean Squared Error, 均方误差) RMSE(Root Mean Squared Error, 均方根误差) RRSE(Root Relative Squared Error, 相对平方根误差) MAE(Mean Absolute Error, 平均绝对误差) RAE(Root Absolute Error, 平均绝对误差平方根) LSM(Least Mean Squared, 最小均方) LSM(Le

【基础】常用的机器学习&amp;数据挖掘知识点

转自http://www.36dsj.com/archives/20135 Basis(基础): MSE(Mean Square Error 均方误差),LMS(LeastMean Square 最小均方),LSM(Least Square Methods 最小二乘法),MLE(MaximumLikelihood Estimation最大似然估计),QP(Quadratic Programming 二次规划), CP(Conditional Probability条件概率),JP(Joint P

机器学习基本算法

Basis(基础): MSE(Mean Square Error 均方误差),LMS(LeastMean Square 最小均方),LSM(Least Square Methods 最小二乘法),MLE(MaximumLikelihood Estimation最大似然估计),QP(Quadratic Programming 二次规划), CP(Conditional Probability条件概率),JP(Joint Probability 联合概率),MP(Marginal Probabili