用遗传算法走出迷宫(Java版)

在百度文库上看到一篇文章《遗传算法及神经网络在游戏开发中的应用》,里面讲到了用遗传算法走迷宫的小游戏,我自己编程实现了一下,并用SWT把游戏的界面做出来了。

算法我就不多说了,上述文章里面讲得很清楚。直接看我做的小软件吧。

刚开始初始化一个迷宫,一般是走不通的,但你点击一个格子它就会变色,由路变为墙,或由墙变为路。

点击“Run"之后,遗传算法在后台运行,把出路给你找出来,然后在迷宫上把路径标出来。

点击"Evolution Chart"查看进入情况,即每一代中最优的个体的适应度是多少。

点击"Options"对遗传算法的参数进行重新设置。

换用更大规模的迷宫进行实验。

FAIL了,查看一下进化情况。

当然也有可能成功

对于遗传找出的结果如果存在原路返回的情况,我的代码可以检测出来,将这段路径剪去。但是如果出现绕圈子的情况检测不出来,比如下面的结果明显就是绕圈子了

下面给出核心算法GA类:

package GA;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

/**
 * 用遗传算法走迷宫
 * 
 * @author Orisun
 * 
 */
public class GA {

	int gene_len; // 基因长度
	int chrom_len; // 染色体长度
	int population; // 种群大小
	double cross_ratio; // 交叉率
	double muta_ratio; // 变异率
	int iter_limit; // 最多进化的代数
	List<boolean[]> individuals; // 存储当代种群的染色体

	Labyrinth labyrinth;
	int width;		//迷宫一行有多少个格子
	int height;		//迷宫有多少行

	public class BI {
		double fitness;
		boolean[] indv;

		public BI(double f, boolean[] ind) {
			fitness = f;
			indv = ind;
		}

		public double getFitness() {
			return fitness;
		}

		public boolean[] getIndv() {
			return indv;
		}
	}

	List<BI> best_individual; // 存储每一代中最优秀的个体

	public GA(Labyrinth labyrinth) {
		this.labyrinth=labyrinth;	
		this.width = labyrinth.map[0].length;
		this.height = labyrinth.map.length;
		chrom_len = 4 * (width+height);
		gene_len = 2;
		population = 20;
		cross_ratio = 0.83;
		muta_ratio = 0.002;
		iter_limit = 300;
		individuals = new ArrayList<boolean[]>(population);
		best_individual = new ArrayList<BI>(iter_limit);
	}

	public int getWidth() {
		return width;
	}

	public void setWidth(int width) {
		this.width = width;
	}

	public double getCross_ratio() {
		return cross_ratio;
	}

	public List<BI> getBest_individual() {
		return best_individual;
	}

	public Labyrinth getLabyrinth() {
		return labyrinth;
	}
	public void setLabyrinth(Labyrinth labyrinth) {
		this.labyrinth = labyrinth;
	}
	public void setChrom_len(int chrom_len) {
		this.chrom_len = chrom_len;
	}

	public void setPopulation(int population) {
		this.population = population;
	}

	public void setCross_ratio(double cross_ratio) {
		this.cross_ratio = cross_ratio;
	}

	public void setMuta_ratio(double muta_ratio) {
		this.muta_ratio = muta_ratio;
	}

	public void setIter_limit(int iter_limit) {
		this.iter_limit = iter_limit;
	}

	// 初始化种群
	public void initPopulation() {
		Random r = new Random(System.currentTimeMillis());
		for (int i = 0; i < population; i++) {
			int len = gene_len * chrom_len;
			boolean[] ind = new boolean[len];
			for (int j = 0; j < len; j++)
				ind[j] = r.nextBoolean();
			individuals.add(ind);
		}
	}

	// 交叉
	public void cross(boolean[] arr1, boolean[] arr2) {
		Random r = new Random(System.currentTimeMillis());
		int length = arr1.length;
		int slice = 0;
		do {
			slice = r.nextInt(length);
		} while (slice == 0);
		if (slice < length / 2) {
			for (int i = 0; i < slice; i++) {
				boolean tmp = arr1[i];
				arr1[i] = arr2[i];
				arr2[i] = tmp;
			}
		} else {
			for (int i = slice; i < length; i++) {
				boolean tmp = arr1[i];
				arr1[i] = arr2[i];
				arr2[i] = tmp;
			}
		}
	}

	// 变异
	public void mutation(boolean[] individual) {
		int length = individual.length;
		Random r = new Random(System.currentTimeMillis());
		individual[r.nextInt(length)] ^= false;
	}

	// 轮盘法选择下一代,并返回当代最高的适应度值
	public double selection() {
		boolean[][] next_generation = new boolean[population][]; // 下一代
		int length = gene_len * chrom_len;
		for (int i = 0; i < population; i++)
			next_generation[i] = new boolean[length];
		double[] cumulation = new double[population];
		int best_index = 0;
		double max_fitness = getFitness(individuals.get(best_index));
		cumulation[0] = max_fitness;
		for (int i = 1; i < population; i++) {
			double fit = getFitness(individuals.get(i));
			cumulation[i] = cumulation[i - 1] + fit;
			// 寻找当代的最优个体
			if (fit > max_fitness) {
				best_index = i;
				max_fitness = fit;
			}
		}
		Random rand = new Random(System.currentTimeMillis());
		for (int i = 0; i < population; i++)
			next_generation[i] = individuals.get(findByHalf(cumulation,
					rand.nextDouble() * cumulation[population - 1]));
		// 把当代的最优个体及其适应度放到best_individual中
		BI bi = new BI(max_fitness, individuals.get(best_index));
		// printPath(individuals.get(best_index));
		//System.out.println(max_fitness);
		best_individual.add(bi);
		// 新一代作为当前代
		for (int i = 0; i < population; i++)
			individuals.set(i, next_generation[i]);
		return max_fitness;
	}

	// 折半查找
	public int findByHalf(double[] arr, double find) {
		if (find < 0 || find == 0 || find > arr[arr.length - 1])
			return -1;
		int min = 0;
		int max = arr.length - 1;
		int medium = min;
		do {
			if (medium == (min + max) / 2)
				break;
			medium = (min + max) / 2;
			if (arr[medium] < find)
				min = medium;
			else if (arr[medium] > find)
				max = medium;
			else
				return medium;

		} while (min < max);
		return max;
	}

	// 计算适应度
	public double getFitness(boolean[] individual) {
		int length = individual.length;
		// 记录当前的位置,入口点是(1,0)
		int x = 1;
		int y = 0;
		// 根据染色体中基因的指导向前走
		for (int i = 0; i < length; i++) {
			boolean b1 = individual[i];
			boolean b2 = individual[++i];
			// 00向左走
			if (b1 == false && b2 == false) {
				if (x > 0 && labyrinth.map[y][x - 1] == true) {
					x--;
				}
			}
			// 01向右走
			else if (b1 == false && b2 == true) {
				if (x + 1 < width && labyrinth.map[y][x + 1] == true) {
					x++;
				}
			}
			// 10向上走
			else if (b1 == true && b2 == false) {
				if (y > 0 && labyrinth.map[y - 1][x] == true) {
					y--;
				}
			}
			// 11向下走
			else if (b1 == true && b2 == true) {
				if (y + 1 < height && labyrinth.map[y + 1][x] == true) {
					y++;
				}
			}
		}
		int n = Math.abs(x - labyrinth.x_end) + Math.abs(y -labyrinth.y_end) + 1;
//		if(n==1)
//			printPath(individual);
		return 1.0 / n;
	}

	// 运行遗传算法
	public boolean run() {
		// 初始化种群
		initPopulation();
		Random rand = new Random(System.currentTimeMillis());
		boolean success = false;
		while (iter_limit-- > 0) {
			// 打乱种群的顺序
			Collections.shuffle(individuals);
			for (int i = 0; i < population - 1; i += 2) {
				// 交叉
				if (rand.nextDouble() < cross_ratio) {
					cross(individuals.get(i), individuals.get(i + 1));
				}
				// 变异
				if (rand.nextDouble() < muta_ratio) {
					mutation(individuals.get(i));
				}
			}
			// 种群更替
			if (selection() == 1) {
				success = true;
				break;
			}
		}
		return success;
	}

//	public static void main(String[] args) {
//		GA ga = new GA(8, 8);
//		if (!ga.run()) {
//			System.out.println("没有找到走出迷宫的路径.");
//		} else {
//			int gen = ga.best_individual.size();
//			boolean[] individual = ga.best_individual.get(gen - 1).indv;
//			System.out.println(ga.getPath(individual));
//		}
//	}

	// 根据染色体打印走法
	public String getPath(boolean[] individual) {
		int length = individual.length;
		int x = 1;
		int y = 0;
		LinkedList<String> stack=new LinkedList<String>();
		for (int i = 0; i < length; i++) {
			boolean b1 = individual[i];
			boolean b2 = individual[++i];
			if (b1 == false && b2 == false) {
				if (x > 0 && labyrinth.map[y][x - 1] == true) {
					x--;
					if(!stack.isEmpty() && stack.peek()=="右")
						stack.poll();
					else
						stack.push("左");
				}
			} else if (b1 == false && b2 == true) {
				if (x + 1 < width && labyrinth.map[y][x + 1] == true) {
					x++;
					if(!stack.isEmpty() && stack.peek()=="左")
						stack.poll();
					else
						stack.push("右");
				}
			} else if (b1 == true && b2 == false) {
				if (y > 0 && labyrinth.map[y - 1][x] == true) {
					y--;
					if(!stack.isEmpty() && stack.peek()=="下")
						stack.poll();
					else
						stack.push("上");
				}
			} else if (b1 == true && b2 == true) {
				if (y + 1 < height && labyrinth.map[y + 1][x] == true) {
					y++;
					if(!stack.isEmpty() && stack.peek()=="上")
						stack.poll();
					else
						stack.push("下");
				}
			}
		}
		StringBuilder sb=new StringBuilder(length/4);
		Iterator<String> iter=stack.descendingIterator();
		while(iter.hasNext())
			sb.append(iter.next());
		return sb.toString();
	}
}

  

posted @ 2011-10-16 19:17  张朝阳  阅读(4257)  评论(5编辑  收藏  举报