C語言編寫遺傳演算法解決TSP旅行商問題
阿新 • • 發佈:2019-02-08
最近在上計算智慧的課,老師剛剛教了遺傳演算法,佈置了用遺傳演算法解決TSP的問題的作業,於是經過幾小時的奮戰,終於編寫完成。
首先先對TSP問題進行分析。TSP問題,也就是旅行商問題,題目的大題內容是 一位旅行商,要遍遊N座城市(城市數量記為NUM_CITY), 已知每兩座城市之間的位置,要求每座城市必須去且只去過一次,求遍遊該N座城市的最短路程。
利用遺傳演算法解決該問題,步驟如下:
1.初始化城市之間的距離
2.生成並初始化初始種群,種群內每個個體儲存著一條完整的路徑(每個城市標號出現且只出現一次)
3.計算目前種群每個個體的適應值(要求求最短路程,路徑一定是個正數,故而得到每個個體中儲存的路徑的總路程後,取倒數,得到的數越大,適應性越高,總路程越短)
4.找出目前最優個體,讓其直接複製至下一代(即不變異)
5.對其他個體根據發生交叉互換的概率Pc,得到參與交叉互換的個體集合
6.使參與交叉互換的個體隨機發生交叉互換(每個個體只參與一次)交叉互換的片段起始點和終點均隨機產生
7.對除最優個體外的每個個體,根據突變概率Pm發生突變,隨機產生兩個位置點,使這兩個位置點之間的片段進行置倒(即2345變成5432)
8.迴圈執行步驟34567,直到演變代數>GENERATIONS為止(暫定為5000代)
程式碼如下所示,(註釋部分用的printf用於測試查錯,可忽視)
#include <stdio.h> #include <stdlib.h> #include <time.h> #include <math.h> #define RAND(X) (rand()%(X)) #define NUM_CITY (30) #define GENERATIONS (5000) #define MAX_SIZE (50) #define LOWEST_ALIVE (1.0/MAX_SIZE) typedef struct _Group{ int city[NUM_CITY]; int adapt; float pAlive; }Group; int cityDistance[NUM_CITY][NUM_CITY]; Group g_Group[MAX_SIZE]; float Pm = 0.1; float Pc = 0.8; int bestOne; void getFitness(Group &group) // 得到適應值 { int distance = 0; int temp; int x1, x2, y1, y2; int tdistance; for (int i = 1; i < NUM_CITY; i++) { distance += cityDistance[group.city[i - 1]][group.city[i]]; } group.adapt = distance; group.pAlive = 1 / (float)distance; } void Swap(int &a, int &b) // 交換ab值 { int c; c = a; a = b; b = c; } void Init() // 初始化 { srand((unsigned int)time(0)); for (int i = 0; i < NUM_CITY; i++) // 初始化城市距離 { for (int j = i + 1; j < NUM_CITY; j++) { cityDistance[i][j] = RAND(100) + 1; cityDistance[j][i] = cityDistance[i][j]; } } printf("城市距離如下:\n"); for (int i = 0; i < NUM_CITY; i++) { for (int j = 0; j < NUM_CITY; j++) { if (j < i + 1) printf(" "); else printf("%3d ", cityDistance[i][j]); } printf("\n"); } for (int i = 0; i < MAX_SIZE; i++) // 初始化樣本數列 { for (int j = 0; j < NUM_CITY; j++) { g_Group[i].city[j] = j; } } int r; for (int i = 0; i < MAX_SIZE; i++) // 打亂順序 { for (int j = 0; j < NUM_CITY; j++) { r = RAND(NUM_CITY); Swap(g_Group[i].city[j], g_Group[i].city[r]); } } printf("產生初始種群如下:\n"); for (int i = 0; i < MAX_SIZE; i++) { printf("第%d個個體:\n", i + 1); for (int j = 0; j < NUM_CITY; j++) { printf("%3d ", g_Group[i].city[j]); } printf("\n"); } } void GetAliveP() // 存活率 { float totalAlive = 0; // 選擇最優部分 totalAlive = 0; for (int i = 0; i < MAX_SIZE; i++) // 計算個體適應值 { getFitness(g_Group[i]); totalAlive += g_Group->pAlive; } for (int i = 0; i < MAX_SIZE; i++) // 矯正個體存活率 讓總和為1 { g_Group[i].pAlive /= totalAlive; } bestOne = 0; for (int i = 0; i < MAX_SIZE; i++) { if (g_Group[i].pAlive > g_Group[bestOne].pAlive) bestOne = i; } printf("目前最佳個體為:%d, 其距離為%d,其軌跡如下:\n", bestOne+1, g_Group[bestOne].adapt); for (int i = 0; i < NUM_CITY; i++) printf("%d ", g_Group[bestOne].city[i]); printf("\n"); } int isOnIt(int num, int Array[NUM_CITY], int ignorePos, int pos1, int pos2) // num是否在Array[]的pos1到pos2之間 其中跳過ignorePos(該數字的原位置) { for (int i = pos1; i <= pos2; i++) { if (Array[i] == num) return i; } return -1; } void Swap(int sel1,int sel2,int pos1, int pos2) // 交叉互換 { int temp; int maxSize = pos2 - pos1 + 1; //printf("開始初步交叉互換\n"); //printf("%d %d\n", pos1, pos2); for (int i = pos1; i <= pos2; i++) { temp = g_Group[sel1].city[i]; g_Group[sel1].city[i] = g_Group[sel2].city[i]; g_Group[sel2].city[i] = temp; } //for (int j = 0; j < NUM_CITY; j++) // printf("%4d", g_Group[sel1].city[j]); //printf("\n"); //for (int j = 0; j < NUM_CITY; j++) // printf("%4d", g_Group[sel2].city[j]); //printf("\n"); int pos; //printf("開始矯正重複值\n"); int times = 0; for (int i = 0; i < NUM_CITY; i++) // 矯正重複值 { times = 0; if (i >= pos1 && i <= pos2) { i = pos2; continue; } do { times++; pos = isOnIt(g_Group[sel1].city[i], g_Group[sel1].city, i, pos1, pos2); if (pos != -1) {/* for (int j = 0; j < NUM_CITY; j++) printf("%4d", g_Group[sel1].city[j]); printf("\n"); for (int j = 0; j < NUM_CITY; j++) printf("%4d", g_Group[sel2].city[j]); printf("\n"); printf("%d %d %d %d %d\n",pos1,pos2,pos, g_Group[sel1].city[i], g_Group[sel2].city[pos]);*/ g_Group[sel1].city[i] = g_Group[sel2].city[pos]; //printf("pos:%d,pos1:%d,pos2:%d\n", pos, pos1, pos2); } } while (pos != -1); do { pos = isOnIt(g_Group[sel2].city[i], g_Group[sel2].city, i, pos1, pos2); if (pos != -1) { g_Group[sel2].city[i] = g_Group[sel1].city[pos]; //printf("pos:%d,pos1:%d,pos2:%d\n", pos, pos1, pos2); } } while (pos != -1); } // printf("交叉互換過程完畢\n"); } void Mutation(int sel, int pos1,int pos2)//個體突變 { int maxSize = pos2 - pos1 + 1; for (int i = 0; i < maxSize / 2; i++) { Swap(g_Group[sel].city[pos1+i], g_Group[sel].city[pos2-i]); } } void Genetic() // 產生下一代種群 { int maxNum = 0, minNum = 0; for (int i = 0; i < MAX_SIZE; i++) { if (g_Group[i].pAlive > g_Group[maxNum].pAlive) maxNum = i; if (g_Group[i].pAlive < g_Group[maxNum].pAlive) minNum = i; } g_Group[minNum] = g_Group[maxNum]; // 使最大直接替換最小 //printf("開始交叉\n"); // 交叉配對 int selNum; int maxTimes = 0, nowTimes = 0; int canSelected[MAX_SIZE]; // 可以用於交叉的個體 bool isCanSelected[MAX_SIZE]; for (int i = 0; i < MAX_SIZE; i++) { if (i == maxNum) // 不讓最優秀的個體參與配對 continue; else if ((RAND(100)) / 100.0 < Pc) // 根據概率判斷是否參與配對 { canSelected[maxTimes++] = i; } } for (int i = 0; i < maxTimes; i++) { selNum = RAND(maxTimes); Swap(canSelected[i], canSelected[selNum]); } int pos1, pos2; for (int i = 0; i < maxTimes; i+=2) { selNum = i + 1; if (selNum >= maxTimes) break; pos1 = RAND(NUM_CITY); // 選定交叉位置 pos2 = RAND(NUM_CITY - pos1) + pos1; if (pos1 == pos2) { if (pos1 > 0) pos1--; else pos2++; }/* printf("%d與%d開始交叉互換\n", canSelected[i], canSelected[selNum]);*/ Swap(canSelected[i], canSelected[selNum], pos1, pos2); /* printf("第%d個體與第%d個體進行交叉配對,得到新的兩個個體如下:\n", canSelected[i] + 1, canSelected[selNum] + 1); printf("第%d個體:\n", canSelected[i] + 1); for (int j = 0; j < NUM_CITY; j++) printf("%4d", g_Group[canSelected[i]].city[j]); printf("\n第%d個體:\n", canSelected[selNum] + 1); for (int j = 0; j < NUM_CITY; j++) printf("%4d", g_Group[canSelected[selNum]].city[j]); printf("\nselNum:%d, maxTimes:%d\n",selNum,maxTimes);*/ } /* printf("開始突變\n");*/ // 突變 for (int i = 0; i < MAX_SIZE; i++) { if (i == maxNum || i == minNum) { continue; } if (RAND(100) / 100.0 < Pm) // 符合突變概率 { pos1 = RAND(NUM_CITY); // 選擇位置 pos2 = RAND(NUM_CITY - pos1) + pos1; if (pos1 == pos2) { if (pos1 > 0) pos1--; else pos2++; } /*printf("第%d個體突變前:\n", i + 1); for (int j = 0; j < NUM_CITY; j++) printf("%4d", g_Group[i].city[j]); printf("\n");*/ Mutation(i, pos1, pos2); // printf("第%d個體突變:\n", i + 1);/* // for (int j = 0; j < NUM_CITY; j++) // printf("%4d", g_Group[i].city[j]); // printf("\n"); } } } void Train() { int nowGenerations = 0; float totalAlive = 0; do { printf("第%d代種群\n", nowGenerations + 1); GetAliveP();// 計算存活率 Genetic();// 根據遺傳規律得到下一代 nowGenerations++; } while (nowGenerations < GENERATIONS); printf("經過%d次繁衍,得到的優秀個體為:\n", nowGenerations); printf("其距離為%d,其軌跡如下:\n", g_Group[bestOne].adapt); for (int i = 0; i < NUM_CITY; i++) printf("%d ", g_Group[bestOne].city[i]); printf("\n"); printf("其他個體如下:\n"); for (int i = 0; i < MAX_SIZE; i++) { printf("其距離為%d,其軌跡如下:\n", g_Group[i].adapt); for (int j = 0; j < NUM_CITY; j++) { printf("%d ", g_Group[i].city[j]); } printf("\n"); } } int main() { Init(); Train(); return 0; }