好像有那么点空闲时间,那就写点东西吧,由于近期一个项目用到了遗传算法,粒子群算法,蚁群算法等启发式智能搜索算法,今天先以本人的观点去阐述遗传算法,仅作自己对知识点的回顾,和给大家一点参考的意见,其他算法以后有时间再作描述。
遗传算法(Genetic Algorithm)是模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型,是一种通过模拟自然进化过程搜索最优解的方法,它最初由美国Michigan大学J.Holland教授于1975年首先提出来的,并出版了颇有影响的专著《Adaptation in Natural and Artificial Systems》,GA这个名称才逐渐为人所知,J.Holland教授所提出的GA通常为简单遗传算法(SGA),这里我就直接摘抄百度百科了。
术语
种群(population)
基因(gene)
染色体(chromosome)
表现型(phenotype)
编码(codeing)
解码(decodeing)
交叉( Crossover )
突变 ( Mutation )
基本思想
正如简介所描述的那样,遗传算法是模拟达尔文的进化论思想,我想"生存竞争,适者生存"正好简要的阐述了这一算法的基本特质。用适应函数去考核每个基因的生存能力,用选择交叉变异去实现进化,搜索出种群的近似最优解,其主要步骤如下:
- 初始化种群
- 适应选择
- 交叉变异
- 迭代
算法描述
为了更好的描述,这里我以一个01背包问题为例子来简单的阐述遗传算法。
给定一个背包C=100,N=5个物品,其重量和价值分别如下,求这个背包能装的最大价值是多少
1 77 92 2 22 22 3 29 87 4 50 46 5 99 90
那么针对上面这个问题,首先我们要考虑编码,也就是把它转换成我们的基因型的形式,这里,我们用一个长度为5的二进制字符串表示相应物品的取舍。其基因型(染色体)就是10100,那么表现型就是选择了1号物品和3号物品。有编码自然就有解码,就是把基因型转换成表现型,编码个人认为是遗传算法中至关重要的一步,怎么根据问题去选择编码方式,直接影响了后面所提到的适应函数的简与复杂。
把问题映射到基因型上我们已经解决了,现在就是怎么去考核一个基因型对当前环境的适应度,换句话说就是更有可能存活遗传下去,那么这里我们必须得设计一个适应函数f,因为是求背包的最大值,又因为不能超过背包所能承受的总重量,所以用h(x)表示第二个不超过总重量的条件,f(y)表示背包的价值,所以得到适应函数f(h(x))。
然后就是怎么去选择的问题,我们用p(x)=f(y)/totall(f(y))来表示某个基因型占总体的概率,现在我们就采用轮盘赌的方法,什么是轮盘赌呢,这里简要提及一下,我们把各个概率放在一个轮盘里,然后转动这个轮盘,最后轮盘停止,指针停在哪里就代表选择哪个概率的事件。
比如在01背包中,我们根据适应函数算出各个基因型的适应度,也算出了每个基因型所占的比例,然后我们根据轮盘赌的方法从中选择出n条基因型出来,然后n条基因型随机两两配对交叉产生两个子代。
交叉
那么什么是交叉呢,和生物学中染色体交叉是一样的概念,就是互换某一段基因,如下图,这里我们采用的是单点交叉。
变异
变异是指,在某一个比较小的概率下,某个基因在遗传时,某个基因座上的基因由0边成1,或者由1变成0。
新产生的基因型,如果原来的种群中没有的话,就加进这个种群,然后再按上面所述去迭代,直到找到我们比较满意的基因型为止,现在我们什么都准备好了,就让它们去交配把,去创建一个种族吧。
下面给出按照上面的例子我临时打的代码.
1 package artiano.ga; 2 3 import java.util.HashMap; 4 import java.util.Iterator; 5 import java.util.LinkedList; 6 import java.util.List; 7 import java.util.Map; 8 import java.util.Set; 9 10 class Chromosome{ 11 String key; 12 double f; 13 double p; 14 public Chromosome(String key,double f,double p){ 15 this.key=key; 16 this.f=f; 17 this.p=p; 18 } 19 public Chromosome(Chromosome o){ 20 this.key=o.key; 21 this.f=o.f; 22 this.p=o.p; 23 } 24 } 25 26 public class GAtest { 27 public Map<String,Chromosome> SAVE=new HashMap<String,Chromosome>(); 28 public List<Chromosome> list; 29 public double[][] B={ 30 {77,92}, 31 {22,22}, 32 {29,87}, 33 {50,46}, 34 {99,90} 35 }; 36 private double M=100; 37 private int N=5; 38 39 public double toPhenotype(String key){ //解码成表现型 40 41 double count=0; 42 for(int i=0;i<key.length();i++){ 43 double tmp=0; 44 if(key.charAt(i)=='1'){ 45 tmp+=B[i][1]; 46 } 47 count+=tmp; 48 } 49 return count; 50 } 51 public void init(int n){ //这里的初始化种族应该是随机的,这里为了简单起见,我随便给了一组 52 SAVE.put("10000", new Chromosome("10000",0,0)); 53 SAVE.put("01100", new Chromosome("01100",0,0)); 54 SAVE.put("00001", new Chromosome("00001",0,0)); 55 SAVE.put("01010", new Chromosome("01010",0,0)); 56 57 } 58 public String lunpandu(){ //轮盘赌 59 double nowP=Math.random(); 60 Set<String> keySet=SAVE.keySet(); 61 Iterator it=keySet.iterator(); 62 double m=0; 63 while(it.hasNext()){ 64 String key=(String)it.next(); 65 Chromosome o=SAVE.get(key); 66 m+=o.p; 67 if(nowP<=m) return key; 68 } 69 return ""; 70 } 71 public Chromosome selected(){ //选择 72 Set<String> keySet=SAVE.keySet(); 73 Iterator it=keySet.iterator(); 74 double count=0; 75 Chromosome max=new Chromosome("-1",0,0); 76 while(it.hasNext()){ 77 String key=(String)it.next(); 78 Chromosome o=SAVE.get(key); 79 count+=o.f=toPhenotype(key); 80 if(o.f>max.f) max=o; 81 } 82 it=keySet.iterator(); 83 while(it.hasNext()){ 84 String key=(String)it.next(); 85 Chromosome o=SAVE.get(key); 86 o.p=o.f/count; 87 88 System.out.println(o.key+" "+o.f+" "+o.p); 89 } 90 list=new LinkedList<Chromosome>(); 91 for(int i=0;i<2;i++){ 92 String seleKey=lunpandu(); 93 list.add(new Chromosome(SAVE.get(seleKey))); 94 System.out.println("--->"+seleKey); 95 } 96 return max; 97 } 98 99 public void crossover(){ //交叉 100 for(int i=0;i<list.size()/2;i++){ 101 Chromosome o1=list.get(i*2); 102 Chromosome o2=list.get(i*2+1); 103 int index=(int)Math.round(1+Math.random() * 2); 104 String o11=o1.key.substring(0, index); 105 String o12=o1.key.substring(index, o1.key.length()); 106 String o21=o2.key.substring(0, index); 107 String o22=o2.key.substring(index, o1.key.length()); 108 System.out.println("|||| "+o11+" | "+o12); 109 System.out.println("|||| "+o21+" | "+o22); 110 o1.key=o11+o22; 111 o2.key=o21+o12; 112 System.out.println("|||| "+o1.key); 113 System.out.println("|||| "+o2.key); 114 long bianyi=Math.round(Math.random() * 10000); 115 if(bianyi<100); 116 117 if(hefa(o1.key) && SAVE.get(o1.key)==null) SAVE.put(o1.key, o1); 118 if(hefa(o2.key) && SAVE.get(o2.key)==null) SAVE.put(o2.key, o2); 119 } 120 } 121 public boolean hefa(String key){ //是否满足h(x) 122 double m=0; 123 for(int i=0;i<N;i++){ 124 if(key.charAt(i)=='1'){ 125 m+=B[i][0]; 126 } 127 } 128 if(m<=M)return true; 129 return false; 130 } 131 public void iteration(int n){ //种群迭代 132 for(int i=0;i<n;i++){ 133 System.out.println("========="+(i+1)); 134 Chromosome max=selected(); 135 if(max.f>=133){ 136 System.out.println(" [----"+max.key+" "+max.f+" "+max.p+"-----]"); 137 break; 138 } 139 crossover(); 140 } 141 142 143 } 144 public static void main(String[] args){ 145 GAtest ts=new GAtest(); 146 ts.init(6); 147 ts.iteration(510); 148 149 } 150 151 }