遺傳算法基本思想:
1) 一個種群有多個個體,每個個體有染色體和對應的基因
為了繁殖進行:
2) 選擇:在殘酷的世界中,適者生存,優勝略汰。
3) 重組:染色體交叉,基因重組
4) 突變:染色體上的基因小概率的突變 (一般給小數點后兩位)
背包問題:
背包只能容得下一定重量b的物品,物品有m種,每種物品有自己的重量w(i)和價值v(i)(0<i<=m),從這些物品中選擇裝入背包,是背包不超過重量b,但價值又要最大。
運用動態規划,分支限界都可以達到效果,但不佳。
我用遺傳算法解決:
一般人有多條染色體,但對於背包問題,一個解我們將看成一個個體,所以,一個個體只有一個染色體,一個染色體對應多個基因。如:100101010100111 表示裝入背包的可能解。(具體情況具體分析)
遺傳所做准備:
1) 用0表示“不選擇裝入”,1表示“裝入”,形成一條基因鏈;100101010100111則表示“15種物品”裝入或不裝入背包的可能解。 ------- 此處用chrom[]存放基因,代表染色體
2) 一個基因對應一個個體。 ------- 此處用Population類或結構體聲明其含有chrom[]等信息
3) 可能的解有很多,構成一個種群。 ------- 用Population類定義一個數組代表個體構成的種群newPop[]:存放新生代,oldPop[]:存放上一代
4) 適應度:適應度和目標函數是正相關的,所以需要物品價值和重量。
------- fitness,weight包含在Population類中
最大適應度:maxFitness,
最小適應度:minFitness,
總適應度:sumFitness,(幫助求突變和交叉的染色體)
平均適應度:avgFitness
遺傳算法的函數:
基本:
1) InitPop() 初始化個體,使每個個體都有基因組
2) Statistics(*pop) 計算適應度(最大,最小,總的,平均的)
3) Selection(*pop) 通過選擇種群中符合要求的父母去繁殖新代,返回這對父母的位置
4) crossover(*parent1,*parent2,pos) 傳入要改的個體位置,隨機產生交叉位置,用優良父母繁殖優良后代並替代傳入個體位置
5) mutation(i) i為基因組基因的位置,逐個基因看是否要變異
6) generation() 對個體進行判斷,若不符合要求,進行選擇,重組,突變。
背包的適應性函數:(個體是一個解)
1) calWeight(pop) 計算個體的重量
2) calFit( pop ) 計算個體的價值
1 #include <AfxWin.h> 2 #include <stdlib.h> 3 #include <math.h> 4 #include <time.h> 5 #include <conio.h> 6 #include <stdio.h> 7 /** 8 * @author: ckj 9 * @date: 2012/12/27 10 */ 11 /*============= 一些必要的常量 ===============*/ 12 #define POP_SIZE 200 // 種群的規模 13 #define PRO_CROSS 0.618 // 交叉概率 14 #define PRO_MUTATE 0.03 // 變異概率 15 #define CHROM_SIZE 50 // 染色體長度 16 #define GENERATION_NUM 1000 // 繁殖代數 17 /*============================================*/ 18 19 // 個體類 20 struct population{ 21 unsigned int chrom[CHROM_SIZE]; // 基因組 22 double weight; // 背包重量 23 double fitness; // 適應度 24 unsigned int parent1,parent2,cross; // 雙親,交叉節點 25 }; 26 27 // 新生代和上一代的種群 28 struct population oldPop[POP_SIZE], newPop[POP_SIZE]; 29 30 // 物體的重量,收益,背包的容量 31 int weight[CHROM_SIZE], profit[CHROM_SIZE], contain; 32 33 // 種群的總的適應度,最小,最大,平均適應度。 34 double sumFitness, minFitness, maxFitness, avgFitness; 35 36 // 計算適應度時,用的懲罰函數系數 37 double alpha; 38 39 // 一個種群的最大和最小適應度個體 40 int minPop, maxPop; 41 42 /**============================================= 43 * 參數:chr為裝入背包的一個可能解 44 * 用途:計算裝入背包的一個可能解得(個體)重量 45 * 返回值:個體重量 46 ============================================**/ 47 double calWeight( unsigned int *chr ){ 48 double popSumWeight = 0; 49 for(int i = 0; i < CHROM_SIZE; i++){ 50 popSumWeight += (*chr) * weight[i]; 51 chr++; 52 } 53 return popSumWeight; 54 } 55 56 /**=============================== 57 * 參數:chr為裝入背包的一個可能解 58 * 用途:計算裝入背包的總價值 59 * 返回值:返回個體的價值 60 ================================*/ 61 double calFit( unsigned int *chr ){ 62 double popProfit = 0; 63 for(int i = 0; i < CHROM_SIZE; i++){ 64 popProfit += (*chr) * profit[i]; 65 chr++; 66 } 67 return popProfit; 68 } 69 70 /**======================================= 71 * 參數:傳入一個種群 72 * 用途:計算種群的最大適應度和最小適應度 73 =======================================*/ 74 void statistics( struct population *pop ){ 75 double tmpFitness; 76 77 sumFitness = pop[0].fitness; 78 minFitness = pop[0].fitness; 79 minPop = 0; 80 maxFitness = pop[0].fitness; 81 maxPop = 0; 82 83 for( int i =1; i < POP_SIZE; i++ ){ 84 // 計算種群的總適應度 85 sumFitness += pop[i].fitness; 86 tmpFitness = pop[i].fitness; 87 // 選擇最大適應度的個體的位置和適應度的值 88 if( (tmpFitness > maxFitness) && ((int)(tmpFitness*10)%10 == 0) ){ 89 maxFitness = pop[i].fitness; 90 maxPop = i; 91 } 92 // 選擇最小適應度的個體的位置 93 if( (tmpFitness < minFitness) ){ 94 minFitness = pop[i].fitness; 95 minPop = i; 96 } 97 avgFitness = sumFitness / (float)POP_SIZE; 98 } 99 } 100 101 /**==================== 102 * 用途:報告種群信息 * 103 ====================**/ 104 void report( struct population *pop, int gen ){ 105 int popWeight = 0; 106 printf("\nThe generation is %d.\n", gen); // 輸出種群的代數 107 // 輸出種群中最大適應度個體的染色體信息 108 printf("The population chrom is: \n"); 109 for( int j = 0; j < CHROM_SIZE; j++ ){ 110 if( j % 5 == 0 ) {printf(" ");} 111 // 每個基因單位輸出 112 printf("%1d", pop[maxPop].chrom[j]); 113 } 114 printf("\nThe population's max fitness is %d.", (int)pop[maxPop].fitness); 115 printf("\nThe population's max weight is %d.\n", (int)pop[maxPop].weight); 116 } 117 118 /**==================== 119 * 用途:初始化種群 * 120 ====================**/ 121 void initPop(){ 122 int i, j; 123 double tmpWeight; 124 bool isPop = false; 125 // 生成一個祖宗種群 126 for( i = 0; i < POP_SIZE; i++ ){ 127 while(!isPop){ 128 // 如果不滿足條件,則繼續隨機產生直到有相同的個體 129 for( j = 0; j < CHROM_SIZE; j++ ){ 130 oldPop[i].chrom[j] = rand()%2; // 產生0/1的任意一個數 131 oldPop[i].parent1 = 0; 132 oldPop[i].parent2 = 0; 133 oldPop[i].cross = 0; 134 } 135 // 選擇重量小於背包容量的個體,滿足條件的個體 136 137 tmpWeight = calWeight( oldPop[i].chrom ); 138 if( tmpWeight <= contain ){ 139 oldPop[i].fitness = calFit( oldPop[i].chrom ); 140 oldPop[i].weight = tmpWeight; 141 oldPop[i].parent1 = 0; 142 oldPop[i].parent2 = 0; 143 oldPop[i].cross = 0; 144 isPop = true; 145 } 146 } 147 isPop = false; 148 } 149 } 150 /**========================================= 151 * 參數:用於帶入概率參數:"交叉"/"變異" * 152 * 用途:概率選擇試驗 * 153 =========================================**/ 154 int excise( double probability ){ 155 double pp; 156 pp = (double)(rand()%20001/20000.0); 157 if( pp <= probability ) 158 return 1; 159 return 0; 160 } 161 162 /**==================== 163 * 用途:個體的選擇 * 164 ====================**/ 165 int selection(int pop){ 166 double wheelPos, randNumber,partsum = 0; 167 int i = 0; 168 // 輪賭法 169 randNumber=(rand()%2001)/2000.0; 170 wheelPos = randNumber*sumFitness; 171 do { 172 partsum += oldPop[i].fitness; 173 i++; 174 }while( ( partsum < wheelPos) && (i < POP_SIZE) ); 175 return i-1; 176 } 177 178 /**======================================== 179 * 參數:父母染色體,即不被測試選到的染色體 180 * 用途:染色體交叉 181 * 返回值:1 182 *========================================*/ 183 int crossOver(unsigned int *parent1, unsigned int *parent2, int i){ 184 int j; // 基因組的基因位置 185 int crossPos; // 交叉點位置 186 if( excise(PRO_CROSS) ){ 187 crossPos = rand()%(CHROM_SIZE-1); 188 }else{ 189 crossPos = CHROM_SIZE - 1; 190 } 191 // 開始交叉 192 for( j = 0; j <= crossPos; j++ ){ 193 newPop[i].chrom[j] = parent1[j]; 194 } 195 for( j = crossPos+1; j < CHROM_SIZE; j++ ){ 196 newPop[i].chrom[j] = parent2[j]; 197 } 198 newPop[i].cross = crossPos; 199 return 1; 200 } 201 202 /**========================================== 203 * 參數:染色體基因組的某一位置突變 204 * 用途:染色體基因突變 205 * 返回值:若突變,則突變結果,否則,原樣返回 206 *=========================================*/ 207 int mutation(unsigned int alleles){ 208 if( excise(PRO_MUTATE) ){ 209 // 滿足突變概率,則此基因突變 210 alleles == 0? alleles = 1: alleles = 0; 211 } 212 return alleles; 213 } 214 215 /**==================================================== 216 * mate1,mate2是從種群中挑出的能來誕生新一代的個體位置 217 * 用途:種群群體更新 218 *=====================================================*/ 219 void generation(){ 220 unsigned int i, j, mate1, mate2; 221 double tmpWeight = 0; 222 bool notGen; 223 for( i = 0; i < POP_SIZE; i++){ 224 notGen = false; 225 // 需要繁殖 226 while( !notGen ){ 227 // 選擇:選擇有幾率產生優良后代的父母個體位置 228 mate1 = selection(i); 229 mate2 = selection(i+1); 230 // 交叉:產生新一代替換掉不符合生存條件的i 231 crossOver( oldPop[mate1].chrom, oldPop[mate2].chrom, i); 232 for( j = 0; j < CHROM_SIZE; j++ ){ 233 // 變異:新個體一個一個基因位置代入,看是否要突變 234 newPop[i].chrom[j] = mutation(newPop[i].chrom[j]); 235 } 236 // 選擇重量小於背包重量的個體,滿足條件,則把價值給記錄起來 237 tmpWeight = calWeight( newPop[i].chrom ); 238 if( tmpWeight <= contain ){ 239 newPop[i].fitness = calFit( newPop[i].chrom ); 240 newPop[i].weight = tmpWeight; 241 newPop[i].parent1 = mate1; 242 newPop[i].parent2 = mate2; 243 notGen = true; // 不需要再產生子代 244 } 245 } 246 } 247 } 248 249 void main(int argc, char* argv[]){ 250 int gen, oldMaxPop, k; 251 double oldMax; 252 // 輸入每個物品的重量和價值,背包容量 253 printf("Please input the products\' weights:\n"); 254 for(int i = 0; i < CHROM_SIZE; i++ ) 255 scanf("%d", &weight[i]); 256 printf("\nPlease input the products\' porfits:\n"); 257 for(int j = 0; j < CHROM_SIZE; j++ ) 258 scanf("%d", &profit[j]); 259 contain = 1000; 260 gen = 0; // 代表第一代(主要用在輸出顯示上) 261 262 srand( (unsigned)time(NULL) ); // 置隨機種子 263 initPop(); // 初始化種群 264 memcpy(&newPop,&oldPop,POP_SIZE*sizeof(struct population)); 265 statistics( newPop ); // 產生新種群的信息(適應度等) 266 report( newPop, gen ); 267 while( gen < GENERATION_NUM ){ 268 gen += 1; 269 if( gen % 100 == 0 ) 270 srand( (unsigned)time(NULL) ); // 每一百代產生新的隨機數 271 oldMax = maxFitness; // 將最大適應度給上一代 272 oldMaxPop = maxPop; // 擁有最大適應度的個體位置給上一代 273 generation(); 274 statistics( newPop ); 275 276 // 如果新的最大適應度小於上一代最大適應度,則上一代活着 277 if( maxFitness < oldMax ){ 278 for( k = 0; k < CHROM_SIZE; k++ ){ 279 newPop[minPop].chrom[k] = oldPop[oldMaxPop].chrom[k]; 280 } 281 newPop[minPop].fitness = oldPop[oldMaxPop].fitness; 282 newPop[minPop].parent1 = oldPop[oldMaxPop].parent1; 283 newPop[minPop].parent2 = oldPop[oldMaxPop].parent2; 284 newPop[minPop].cross = newPop[minPop].cross; 285 statistics(newPop); 286 }else if( maxFitness > oldMax ){ 287 report(newPop, gen); 288 } 289 //保存新生代種群的信息到老一代種群信息空間 290 memcpy(&oldPop,&newPop,POP_SIZE*sizeof(struct population)); 291 } 292 }