“遺傳算法”解決“背包問題”


遺傳算法基本思想:

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 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM