網(wǎng)站建設(shè) 源美設(shè)計seo合作代理
一、求解問題概述
1.1 TSP問題
TSP問題是指旅行商問題(Traveling Salesman Problem)。在TSP問題中,假設(shè)有一名旅行商要在給定的一組城市之間進行旅行,每個城市只能被訪問一次,并且旅行商必須最終返回出發(fā)城市。問題的目標(biāo)是找到一條路徑,使得旅行商的總旅行距離最短。
TSP問題是一個經(jīng)典的組合優(yōu)化問題,在計算復(fù)雜性理論中被證明是NP-難問題,意味著在一般情況下,找到最優(yōu)解需要耗費大量的計算時間。TSP問題在實際應(yīng)用中具有廣泛的應(yīng)用,如物流規(guī)劃、電路板設(shè)計、基因組測序等領(lǐng)域。為了解決TSP問題,許多算法和啟發(fā)式方法被提出,包括窮舉搜索、動態(tài)規(guī)劃、近似算法(如最近鄰算法和模擬退火算法)、遺傳算法等。這些方法旨在找到一個近似最優(yōu)解或者在可接受的時間內(nèi)找到較好的解決方案。
1.2 目標(biāo)函數(shù)
在該問題中,我們需要定義一個目標(biāo)函數(shù),它是根據(jù)決策變量的值來計算問題的目標(biāo)。目標(biāo)函數(shù)可以是線性的、非線性的、凸的或非凸的,具體取決于問題的性質(zhì)。例如,在一個生產(chǎn)調(diào)度問題中,目標(biāo)函數(shù)可以是最小化總生產(chǎn)時間或最大化利潤。
arg ? min ? x ∑ i = 1 n ∑ j = 1 n c i j x i j \arg\min_{x}\sum_{i=1}^n\sum_{j=1}^n c_{ij}x_{ij} argxmin?i=1∑n?j=1∑n?cij?xij?
其中:
n n n 是城市的數(shù)量。
c i j c_{ij} cij? 是城市i到城市j之間的距離(或時間)。
x i j x_{ij} xij? 是決策變量,表示是否從城市i移動到城市j。當(dāng)路徑經(jīng)過城市i到城市j時, x i j x_{ij} xij? 取值為1,否則為0。
TSP
的目標(biāo)函數(shù)即為所有路徑距離或路徑時間的總和,通過最小化這個目標(biāo)函數(shù),可以找到一條最優(yōu)路徑,使得旅行商經(jīng)過所有城市后的總距離或總時間最小。
二、優(yōu)化方法概述
TSP問題屬于組合優(yōu)化問題,往往這種問題如果用枚舉法來求解的話,都會遇到 n ! n! n! 或者 m n m^n mn 等復(fù)雜度爆炸的情況,都屬于 NP
難問題。
解決這種問題的方法一般采用近似法求解,即:在損失少量求解精度的前提下,節(jié)約大量的時間3開銷。
下面采用一種改進的遺傳算法對 TSP 問題進行求解。
三、程序代碼
該算法參考自計算機學(xué)報論文《一種改進的求解 TSP 問題的演化算法》1
// 解決TSP問題的重構(gòu)算法(主要是tsp0的代碼風(fēng)格太丑陋了,修改成C++風(fēng)格)
#include<iostream>
#include<fstream>
#include<vector>
#include<time.h>
#include<string>
using namespace std;class TSP_M {
private:int numCity; // 城市數(shù)量vector<vector<double>> cityXY; // 城市坐標(biāo)vector<vector<double>> cityDis; // 城市間距離的鄰接矩陣int numColony = 100; // 種群數(shù)量vector<vector<int>> colony; // 種群vector<double> individualAdaptability; // 個體適應(yīng)度int maxGen = 200000; // 最大演化代數(shù)double probabilityMutation = 0.02; // 變異概率vector<int> bestIndividual; // 當(dāng)前最優(yōu)個體double bestAdaptability; // 當(dāng)前最優(yōu)個體的適應(yīng)度public:// 計算種群中每個個體的適應(yīng)度void calculateAdaptability() {// 計算每個個體的適應(yīng)度for (int i = 0; i < numColony; i++) {double sum = 0;for (int j = 0; j < numCity; j++) {sum += cityDis[colony[i][j]][colony[i][(j + 1) % numCity]];}individualAdaptability[i] = sum;}}// 初始化void init(string filePath) {readTspFile(filePath);// 根據(jù)tsp數(shù)據(jù),初始化相關(guān)數(shù)據(jù)calculateData();}// 進行迭代計算void evolution() {for (int curGen = 0; curGen < maxGen; curGen++) { // 迭代maxGen次for (int i = 0; i < numColony; i++) { // 遍歷種群中所有個體vector<int> path = colony[i]; // 用于存放變異后的路徑int posC1 = rand() % numCity; // 隨機生成變異點1(在path中的位置)int posC2 = rand() % numCity; // 隨機生成變異點2(在path中的位置)int C1, C2; // 變異點1和變異點2對應(yīng)的城市編號C1 = path[posC1]; // 獲取變異點1對應(yīng)的城市int j = rand() % numColony; // 用于外變異的另一個 與 i個體 不同的個體int pos_flag = 0; // 用于標(biāo)記變異過的點的數(shù)量double distanceChange = 0; // 用于記錄距離變化while (true){// 以 probabilityMutation (default = 0.02)的概率進行內(nèi)變異if (rand() / 32768.0 < probabilityMutation) {posC2 = rand() % numCity;while (posC1 == posC2) { // 如果兩個變異點相同,則重新生成posC2 = rand() % numCity;}C2 = colony[i][posC2]; // 獲取變異點1對應(yīng)的城市}else { // 進行外變異(交叉)j = rand() % numColony;while (i == j) { // 如果兩個個體相同,則重新生成j = rand() % numColony;}// 獲取個體 j 中 變異點1 對應(yīng)城市的位置int pos = position(colony[j], path[posC1]);C2 = colony[j][(pos + 1) % numCity]; // 獲取變異點2對應(yīng)的城市posC2 = position(path, C2); // 獲取變異點2在個體 i 中的位置(即變異點2對應(yīng)的城市在個體 i 中的位置}// 如果兩個變異點相鄰,continueif ((posC1 + 1) % numCity == posC2 || (posC1 - 1 + numCity) % numCity == posC2)break;//if (abs(posC1 - posC2) == 1 || abs(posC1 - posC2) == numCity - 1) {// continue;//}// 否則進行倒位操作int C1_left = path[posC1]; // 變異點1左邊的城市int C1_right = path[(posC1 + 1) % numCity]; // 變異點1右邊的城市int C2_left = path[posC2]; // 變異點2左邊的城市int C2_right = path[(posC2 + 1) % numCity]; // 變異點2右邊的城市// 計算倒位后的路徑長度distanceChange += cityDis[C1_left][C2_left] + cityDis[C1_right][C2_right]- cityDis[C1_left][C1_right] - cityDis[C2_left][C2_right];invert(path, posC1, posC2); // 倒位操作pos_flag++; // 變異點數(shù)量加一if (pos_flag >= numCity)break;posC1++; // 變異點1的位置加一if (posC1 >= numCity) posC1 = 0; // 如果變異點1的位置超過了numCity,則變異點1的位置為0}// 更新子個體的適應(yīng)度individualAdaptability[numColony + i] = individualAdaptability[i] + distanceChange;distanceChange = 0;// 記錄 產(chǎn)生的 子個體for (int j = 0; j < numCity; j++) {colony[numColony + i][j] = path[j];}}// 一輪迭代之后進行選擇selection();bestIndividual = colony[0]; // 更新最優(yōu)個體bestAdaptability = individualAdaptability[0]; // 更新最優(yōu)個體的適應(yīng)度for (int i = 1; i < numColony; i++) {if (individualAdaptability[i] < bestAdaptability) {bestIndividual = colony[i];bestAdaptability = individualAdaptability[i];}}// cout << "第" << curGen << "代的最優(yōu)個體適應(yīng)度為:" << bestAdaptability << endl;cout << curGen << ":" << bestAdaptability << endl;// 創(chuàng)建 outfile.txt 文件ofstream outfile("outfile.txt", ios::app);// 每 2000 代將最優(yōu)個體的適應(yīng)度寫入文件if ((curGen + 1) % 2000 == 0) {outfile << curGen << ":" << bestAdaptability << endl;}// 關(guān)閉文件outfile.close();}}// 獲取城市在路徑中的位置int position(vector<int>& path, int city) {for (int i = 0; i < numCity; i++) {if (path[i] == city) {return i;}}return -1;}void invert(vector<int>& path, int pos1, int pos2) {// 如果pos1在pos2的左邊,為一段if (pos1 < pos2) {for (int i = pos1 + 1, j = pos2; i < j; i++, j--) {swap(path[i], path[j]);}}// 如果pos1在pos2的右邊,為兩段else {// 右邊的段 <= 左邊的段if (numCity - 1 - pos1 <= pos2 + 1) {int i, j;for (i = pos2 + 1, j = pos1; i <= numCity - 1; i++, j--) {swap(path[i], path[j]);}for (i = 0; i < j; i++, j--) {swap(path[i], path[j]);}}// 右邊的段 > 左邊的段else {int i, j;for (i = pos2 + 1, j = pos1; j >= 0; i++, j--) {swap(path[i], path[j]);}for (j = numCity - 1; i < j; i++, j--) {swap(path[i], path[j]);}}}}// 在父代和子代中進行一個錦標(biāo)賽選擇void selection() {for (int i = 0; i < numColony; i++) {if (individualAdaptability[i] > individualAdaptability[numColony + i]) {individualAdaptability[i] = individualAdaptability[numColony + i];for (int j = 0; j < numCity; j++) {colony[i][j] = colony[numColony + i][j];}}}}// 讀取tsp文件bool readTspFile(string filePath) {fstream input(filePath, ios::in);if (!input) {cout << "文件打開失敗" << endl;return false;}input >> numCity; // 城市數(shù)量cout << numCity << endl;// 初始化cityXYcityXY = vector<vector<double>>(numCity, vector<double>(2));// 讀取城市坐標(biāo)double x, y;for (int i = 0; i < numCity; i++) {int tmp;input >> tmp >> x >> y;cout << tmp << " " << x << " " << y << endl;cityXY[i][0] = x;cityXY[i][1] = y;}// 關(guān)閉文件input.close();return true;}// 根據(jù)tsp數(shù)據(jù)計算城市之間的距離、并隨機初始化種群、同時計算適應(yīng)度void calculateData() {// 初始化cityDiscityDis = vector<vector<double>>(numCity, vector<double>(numCity));// 計算城市間距離for (int i = 0; i < numCity; i++) {for (int j = 0; j < numCity; j++) {cityDis[i][j] = sqrt(pow(cityXY[i][0] - cityXY[j][0], 2) + pow(cityXY[i][1] - cityXY[j][1], 2));}}// 初始化colony (包括父代和子代)colony = vector<vector<int>>(2 * numColony, vector<int>(numCity));// 以時間為種子,隨機生成種群srand((unsigned)time(NULL));// 建立一個用于隨機生成種群的數(shù)組vector<int> tmp(numCity);for (int i = 0; i < numCity; i++) {tmp[i] = i;}// 隨機初始化種群for (int i = 0; i < numColony; i++) {int numNeedToRand = numCity; // 當(dāng)前需要隨機的次數(shù)for (int j = 0; j < numCity; j++) {int randIndex = rand() % numNeedToRand; // 隨機生成下標(biāo)colony[i][j] = tmp[randIndex]; // 將隨機生成的下標(biāo)對應(yīng)的值賦給種群swap(tmp[randIndex], tmp[numNeedToRand - 1]); // 將已經(jīng)隨機過的下標(biāo)與最后一個下標(biāo)交換numNeedToRand--; // 需要隨機的次數(shù)減一}}// 初始化individualAdaptabilityindividualAdaptability = vector<double>(2 * numColony); // 后面的numColony個是用于存放子個體的適應(yīng)度的// 計算種群中每個個體的適應(yīng)度calculateAdaptability();}// 獲取最優(yōu)個體vector<int> getBestIndividual() {int bestIndex = 0;for (int i = 1; i < numColony; i++) {if (individualAdaptability[i] < individualAdaptability[bestIndex]) {bestIndex = i;}}return colony[bestIndex];}
};int main() {TSP_M tsp;// tsp.readTspFile("./pcb442.tsp");tsp.init("./pcb442.tsp");tsp.evolution();vector<int> bestIndividual = tsp.getBestIndividual();// 輸出最優(yōu)個體到文件fstream output("./bestIndividual_Serial.txt", ios::out);for (int i = 0; i < bestIndividual.size(); i++) {output << bestIndividual[i] << " ";}return 0;
}
四、運行結(jié)果
從圖中可以看出算法的所有的路線基本都沒有交叉,性能較為魯棒。
[1]蔡之華,彭錦國,高偉,魏巍,康立山.一種改進的求解TSP問題的演化算法[J].計算機學(xué)報,2005(05):823-828. ??