利用Java代码实现用遗传算法(EA/GA)求cos(x)在 0~Π上的最大值(最小值只需要该适应度函数即可)

利用Java代码实现用遗传算法(EA/GA)求cos(x)在 0~Π上的最大值(最小值只需要该适应度函数即可)

com.tylg.gascosx包下代码

gaMain.java主方法入口

package com.tylg.gascosx;

public class Choose {
	// 传入一个二进制数组,通过该函数返回一个选择之后的数组
	public String[] choose(String[] genes, double ACCURACY,int GROUPSIZE) // 传入一个二进制字符串,传入精度
	{
		Code code = new Code();
		Fitness fi = new Fitness();
		int bit = code.weiShu(ACCURACY); // 二进制染色体的位数
		// 未经选择的种群(二进制形式)
		double fall = 0;
		for (String string : genes) {
			double v = code.turevalue(string, bit); // 产生的这个二进制数转化为0~3.1415的十进制数
			fall += fi.fitness(v); // f表示总适应度大小
		}

		// 把种群从二进制变为十进制,再计算出各自的适应度值
		double[] farr = new double[GROUPSIZE * 2];
		for (int i = 0; i < GROUPSIZE * 2; i++) {
			double v = code.turevalue(genes[i], bit);
			farr[i] = fi.fitness(v); // 适应度数组
		}

		// 通过下列的for循环,可以得到一个适应度大小从高到低排序的genes染色体数组
		for (int i = 0; i < GROUPSIZE * 2 - 1; i++) {
			for (int j = i + 1; j < GROUPSIZE * 2; j++) {
				if (farr[i] < farr[j]) {
					double c1 = farr[i];
					farr[i] = farr[j];
					farr[j] = c1;
					// 让适应度数组的变化同步至二进制字符串染色体的变化
					String c2 = genes[i];
					genes[i] = genes[j];
					genes[j] = c2;
				}
			}
		}

		// 计算出每个染色体的相对适应度,即( fc[i] = farr[i] / fall ),且fc数组中存放的相对适应度从高到低排序的值
		double[] fc = new double[GROUPSIZE * 2];
		for (int i = 0; i < GROUPSIZE * 2; i++) {
			fc[i] = farr[i] / fall;
		}

		// 轮盘赌---- 可得到一组新的newgenes[]染色体数组
		String[] newgenes = new String[GROUPSIZE];
		for (int i = 0; i < GROUPSIZE; i++) {                // 扔骰子次数为GROUPSIZE次
			double dice = Math.random();
			double chip1 = 0.0, chip2 = fc[0];
			for (int j = 0; j < GROUPSIZE * 2; j++) {        // 骰子的值dice与从高到底进行排序的适应度值相比较,
				if ((chip1 < dice || chip1 == dice) && (dice < chip2)) {
					newgenes[i] = genes[j];
					break;
				} else {
					chip1 = chip2;
					chip2 += fc[j + 1];
				}
			}
		}

		return newgenes;
	}

}

Fitness.java为适应度函数的代码

package com.tylg.gascosx;

public class Fitness {

	// 计算单个个体的适应度,输入为一个0~3.1415之间的值
	public double fitness(double x) {
		double f = 0;
		f = (Math.cos(x) + 1) / 2;
		return f;
	}
	/*
	 * // 批量计算数组的适应度 public double[] fitAll(String str[], int GENE) { double []
	 * fit = new double[str.length]; for(int i = 0;i < str.length; i++) { fit[i]
	 * = fitSingle(str[i],GENE); } return fit; }
	 * 
	 * // 适应度最值(返回序号) public int mFitNum(double fit[]) { double m = fit[0]; int
	 * n = 0; for(int i = 0; i < fit.length; i++) { if(fit[i] > m) { m = fit[i];
	 * n = i; } } return n; }
	 * 
	 * // 适应度最值(返回适应度) public double mFitVal(double fit[]) { double m = fit[0];
	 * for(int i = 0; i < fit.length; i++) { if(fit[i] > m) { m = fit[i]; } }
	 * return m; }
	 */
}

Encoding.java 为初始化染色体的代码,先生成一条染色体,在生成一组染色体。

package com.tylg.gascosx;

public class Encoding {
	// 初始化一条染色体,gene表示染色体的长度,即二进制码的位数
	public String initSingle(int gene) {
		String res = "";
		for (int i = 0; i < gene; i++) {
			if (Math.random() < 0.5) {
				res += 0;
			} else {
				res += 1;
			}
		}
		return res;
	}

	// 初始化一组染色体-----(调用初始化一条染色体函数)gene表示染色体的长度,即二进制码的位数,groupsize即为种群个数
	public String[] initAll(int gene, int groupsize) {

		String[] initall = new String[groupsize];
		for (int i = 0; i < groupsize; i++) {
			initall[i] = initSingle(gene);
		}
		return initall; // 该数组当中包含多个字符串,即包含多条(groupsize)染色体
	}
}

Code.java为间接二进制编码及其相对应解码的java代码

package com.tylg.gascosx;

import java.lang.Integer;

/*
 * 间接二进制码全部为正数,在此定义域为:0~3.1415,码的位数就是m+1
 */
public class Code {

	// 码的位数公式,传入的d为精度(例如:0.00001),输出的为二进制码的位数
	public int weiShu(double d) {
		double x = (3.1415) / d;
		int m = 0;
		while (true) {
			double low = Math.pow(2, m);
			double high = Math.pow(2, m + 1);
			if ((low < x) && (!(x > high))) {
				return m + 1;
			} else {
				m++;
			}
		}

	}

	// 解码公式, 其中s为某一条染色体的二进制编码字符串
	public double turevalue(String s, int m) {
		double value = 0;
		int x = Integer.parseUnsignedInt(s, 2);      // 将二进制数的字符串s变为一个2进制的无符号十进制整数
		value = (3.1415 / (Math.pow(2, m) - 1)) * x; // 利用一定的比例此,把一个范围很大十进制数转化为一个范围在0~3.1415之间的数
		return value;								 //计算cos函数值的大小就是用value的值
	}

}

Choose.java为选择算子

package com.tylg.gascosx;

public class Choose {
	// 传入一个二进制数组,通过该函数返回一个选择之后的数组
	public String[] choose(String[] genes, double ACCURACY,int GROUPSIZE) // 传入一个二进制字符串,传入精度
	{
		Code code = new Code();
		Fitness fi = new Fitness();
		int bit = code.weiShu(ACCURACY); // 二进制染色体的位数
		// 未经选择的种群(二进制形式)
		double fall = 0;
		for (String string : genes) {
			double v = code.turevalue(string, bit); // 产生的这个二进制数转化为0~3.1415的十进制数
			fall += fi.fitness(v); // f表示总适应度大小
		}

		// 把种群从二进制变为十进制,再计算出各自的适应度值
		double[] farr = new double[GROUPSIZE * 2];
		for (int i = 0; i < GROUPSIZE * 2; i++) {
			double v = code.turevalue(genes[i], bit);
			farr[i] = fi.fitness(v); // 适应度数组
		}

		// 通过下列的for循环,可以得到一个适应度大小从高到低排序的genes染色体数组
		for (int i = 0; i < GROUPSIZE * 2 - 1; i++) {
			for (int j = i + 1; j < GROUPSIZE * 2; j++) {
				if (farr[i] < farr[j]) {
					double c1 = farr[i];
					farr[i] = farr[j];
					farr[j] = c1;
					// 让适应度数组的变化同步至二进制字符串染色体的变化
					String c2 = genes[i];
					genes[i] = genes[j];
					genes[j] = c2;
				}
			}
		}

		// 计算出每个染色体的相对适应度,即( fc[i] = farr[i] / fall ),且fc数组中存放的相对适应度从高到低排序的值
		double[] fc = new double[GROUPSIZE * 2];
		for (int i = 0; i < GROUPSIZE * 2; i++) {
			fc[i] = farr[i] / fall;
		}

		// 轮盘赌---- 可得到一组新的newgenes[]染色体数组
		String[] newgenes = new String[GROUPSIZE];
		for (int i = 0; i < GROUPSIZE; i++) {                // 扔骰子次数为GROUPSIZE次
			double dice = Math.random();
			double chip1 = 0.0, chip2 = fc[0];
			for (int j = 0; j < GROUPSIZE * 2; j++) {        // 骰子的值dice与从高到底进行排序的适应度值相比较,
				if ((chip1 < dice || chip1 == dice) && (dice < chip2)) {
					newgenes[i] = genes[j];
					break;
				} else {
					chip1 = chip2;
					chip2 += fc[j + 1];
				}
			}
		}

		return newgenes;
	}

}

Cross.java为交叉算子

package com.tylg.gascosx;

/*
 *  step1:先通过遍历数组的方式,在每次遍历中用随机函数生成一个0~1范围大小的数,再判断交叉概率值cp与其的大小。
 *  step2:若被选中则与其另外一染色体进行中点交叉,适用字符串交换方法
 *  step3:把交叉后的数组与原来传入的数组genes数组进行适应度比较,选择其中适应度排名前GROUPSIZE个数组,作为新的染色体
 *  	      以便执行之后的变异操作
 * 
 */
public class Cross {
	/*private static final int GROUPSIZE = 10;
	public final static double ACCURACY = 0.0001; // 十进制的精准度(小数点后几位)
	Code code = new Code();
	int bit = code.weiShu(ACCURACY); // 二进制染色体的位数*/
	Code code = new Code();
	Fitness fitness = new Fitness();

	public String[] cross(String[] genes, int GROUPSIZE, double ACCURACY) {
		int bit = code.weiShu(ACCURACY); // 二进制染色体的位数
		String[] genes1 = new String[GROUPSIZE];
		String[] genes2 = new String[GROUPSIZE];

		int c1 = 0, c2 = 0;
		for (int i = 0; i < GROUPSIZE; i++) {          // for循环就是让genes1与genes2中各分得若干个基因,但其基因总和数为GROUPSIZE个
			double x = Math.random();                  // c1 , c2中记录的值就是genes1 与 genes2各自的实际长度
			if (x < 0.5) {
				genes1[c1] = genes[i];
				c1++;
			} else {
				genes2[c2] = genes[i];
				c2++;
			}
		}
		// 对genes1或者genes2中的基因进行交叉操作
		if (c1 % 2 == 0) { // if else语句就是选择一个染色体数组内染色体个数为一半染色体长度(或者一般少一个)的进行交叉~~~该位置的代码有待优化
			int x1 = c1;
			for (int i = 0; i < (x1 / 2); i++) {
				genes1[i] = genes1[i].substring(0, bit / 2)
						+ genes1[x1 - 1 - i].substring(bit / 2, bit);
				genes1[x1 - 1 - i] = genes1[i].substring(bit / 2, bit) 
						+ genes1[x1 - 1 - i].substring(0, bit / 2);
			}
		} else {
			int x1 = c2;
			for (int i = 0; i < (x1 / 2); i++) {
				genes2[i] = genes2[i].substring(0, bit / 2)
						+ genes2[x1 - 1 - i].substring(bit / 2, bit);
				genes2[x1 - 1 - i] = genes2[i].substring(bit / 2, bit) 
						+ genes2[x1 - 1 - i].substring(0, bit / 2);
			}
		}
		String[] genes12 = new String[GROUPSIZE];         //新串genes12就是把经过交叉操作后的genes1与genes2连接在一块
		for (int i = 0; i < c1; i++) {
			genes12[i] = genes1[i];
		}
		for (int j1 = 0; j1 < c2; j1++) {
			genes12[j1 + c1] = genes2[j1];
		}
		/*
		 * 到目前为止,新的基因数组已经产生,他们是genes1与genes2的组合genes12,总数为GROUPSIZE个,
		 * 需要和他们的父本母本的染色体genes一起进行一个总基因为GROUPSIZE*2个的排序,排序的原则还是按照各自的适应度大小进行排序
		 */
		String[] tgenes = new String[GROUPSIZE * 2];

		for (int i1 = 0; i1 < GROUPSIZE; i1++) {
			tgenes[i1] = genes[i1];
		}
		for (int j1 = 0; j1 < GROUPSIZE; j1++){
			tgenes[j1 + GROUPSIZE] = genes12[j1];
		}
		/*for (String string : tgenes) {
			System.out.println("~~~~~~~~~~~~~~~~~~");
			System.out.println(string);
		} */

		// 对新的染色体数组tgenes进行排序,排序规则就是适应度大的排在前面,适应度小的排在后面
		Code code = new Code();
		for (int m = 0; m < GROUPSIZE * 2 - 1; m++) {
			for (int n = m + 1; n < GROUPSIZE * 2; n++) {
				if (fitness.fitness(code.turevalue(tgenes[m], bit)) < fitness.fitness(code.turevalue(tgenes[n], bit))) {
					String ch = tgenes[m];
					tgenes[m] = tgenes[n];
					tgenes[n] = ch;
				}
			}
		}
		String[] newgenes2 = new String[GROUPSIZE * 2];
		for (int j1 = 0; j1 < GROUPSIZE * 2; j1++) {
			newgenes2[j1] = tgenes[j1];
		}

		return newgenes2;
	}
}

Mutation.java为变异算子

package com.tylg.gascosx;

public class Mutation {
	// private String str;

	public String[] mutation(String[] genes, double MP, int GROUPSIZE, double d) {
		/*
		 * 对染色体进行变异操作,步骤如下:
		 * (1)利用random函数随机生成一个数,利用该数与变异概率进行比较,若该数小于或者等于MP,则选择这个基因作为变异基因
		 * (2)对被选择的这个变异基因进行变异操作,操作手法为利用random函数随机生成一个 (1~基因长度)数,并对该位进行取反操作
		 * (3)最后返回该变异过后的数组
		 */
		Code code = new Code();
		int lensingle = code.weiShu(d); // 单个染色体的长度为lensingle
		for (int i = 0; i < GROUPSIZE * 2; i++) {
			double x = Math.random();
			if (x < MP || x == MP) {
				String str = genes[i];
				int c = (int) (lensingle * Math.random()); // 随机获得一个染色体上的某一位
				// 对该位上的0或1进行取反操作
				String newstr = "";
				if (str.charAt(c) == '0') {
					// String newstr = ""; //该newstr中存放的是进行过变异操作后的那个基因
					for (int j = 0; j < lensingle; j++) {
						if (j == c) {
							newstr += '1';
						} else {
							newstr += str.charAt(j);
						}
					}

				} else {
					newstr = "";
					c = (int) (lensingle * Math.random());
					for (int j1 = 0; j1 < lensingle; j1++) {
						if (j1 == c) {
							newstr += '0';
						} else {
							newstr += str.charAt(j1);
						}
					}
					// 现将newstr 替换掉原染色体字符数组中本该进行变异那个基因字符串
					genes[i] = newstr;
				}

			}

		}
		return genes;
	}
}

代码中有些地方可以进行改进,该程序可以根据输入的相应常量值,如(种群数目,迭代次数,十进制精度:用于改变染色体的长度,变异概率)对整个遗传算法进行调整;目前该遗传算法的交叉概率不可调,默认为中点交叉

选择算子为轮盘赌的方式,变异为对染色体上的某一位进行变异,没有采用精英策略

以下是进化算法的普遍设置和现象:

  • 1.种群规模20-100个,进化代数2000-3000代,交叉概率0.6-0.8, 变异概率0.01-0.05;
  • 2.没有精英策略的EA不能保证在有限代数的进化过程中收敛到最优解;加入精英策略的EA可以保证精英解是最优解,但是种群不收敛;
  • 3.进化计算的结果一般是30次独立运行结果的均值。

需注意! EA的两个最严重的问题: 1.不收敛,2. 早熟收敛。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值