TSP_旅行商問題-遺傳演算法
阿新 • • 發佈:2018-12-25
TSP_旅行商問題-遺傳演算法
問題描述
對於n組城市座標,尋找最短路徑使其經過所有城市並回到起點。
問題資料集:tsp.eil51問題
1 37 52 2 49 49 3 52 64 4 20 26 5 40 30 6 21 47 7 17 63 8 31 62 9 52 33 10 51 21 11 42 41 12 31 32 13 5 25 14 12 42 15 36 16 16 52 41 17 27 23 18 17 33 19 13 13 20 57 58 21 62 42 22 42 57 23 16 57 24 8 52 25 7 38 26 27 68 27 30 48 28 43 67 29 58 48 30 58 27 31 37 69 32 38 46 33 46 10 34 61 33 35 62 63 36 63 69 37 32 22 38 45 35 39 59 15 40 5 6 41 10 17 42 21 10 43 5 64 44 30 15 45 39 10 46 32 39 47 25 32 48 25 55 49 48 28 50 56 37 51 30 40 最優解:426
遺傳演算法基本流程
1、初始化種群,隨機產生多組可行解;
2、開始迴圈;
2.a、選擇,計算種群中每個個體的適應值,根據適應值選擇出新的種群替換原來的種群,使得適應值越大的個體被保留下來的可能越大;
2.b、交叉,將種群中的個體兩兩相鄰組合,對每對個體都有一定的交叉概率,使得其交叉互換部分,產生新的兩個個體,替換源個體;
2.c、變異,對種群中的每個個體,都有一定的變異概率,使該個體的部分變異;
3、退出迴圈體,新種群中的最優解達到目標;
遺傳演算法在tsp問題上的應用流程
1、初始化種群,隨機產生多組可行解;
2、開始迴圈;
2.a、選擇,計算種群中每個個體的適應值,根據適應值選擇出新的種群替換原來的種群,使得適應值越大的個體被保留下來的可能越大;
2.b、交叉,將種群中的個體兩兩相鄰組合,對每對個體都有一定的交叉概率,使得其交叉互換相同長度的部分城市序列,產生新的兩個個體,合法化後替換源個體;
2.c、變異,對種群中的每個個體即每個序列,都有一定的變異概率,使該個體的部分變異,即隨機選擇序列中的兩個城市,交換其順序;
3、退出迴圈體,迭代次數控制迴圈結束;
【注1】適應值為路徑總長的倒數;
【注2】交叉部分可能會產生不合法的後代,所以需要用函式處理一下,使其合法,即路徑中不存在重複節點;
引數設定
種群數量 pop_size = 300;
變異概率 u = 0.1;
交叉概率 v = 0.9;
迭代次數 k = 1000;
測試結果
演算法程式碼
#include<iostream>
#include<ctime>
#include<cmath>
#include<fstream>
#include<algorithm>
using namespace std;
typedef struct node {
int x;
int y;
}city;
const int num = 100;//city number
const int width = 100;//
const int height = 100;//
const int pop_size = 300;//種群大小
int seq_pop[pop_size][num];//種群
double energy[pop_size];//個體能量
double fitness[pop_size];//適應度
double sum_fitness = 0;
double sum_energy = 0;//總能量
double u = 0.1;//變異概率大於0.001
double v = 0.9;//交叉概率大於0.001
city citys[num];//citys
double dic[num][num];//distance from two citys;
int N;//real citys
int seq = 0;//當前最優序列
double answer;//最優解
int k = 1000;//迭代次數
void init() {//set N&&citys設定N和citys[num]
N = 51;
citys[0].x = 37; citys[0].y = 52;
citys[1].x = 49; citys[1].y = 49;
citys[2].x = 52; citys[2].y = 64;
citys[3].x = 20; citys[3].y = 26;
citys[4].x = 40; citys[4].y = 30;
citys[5].x = 21; citys[5].y = 47;
citys[6].x = 17; citys[6].y = 63;
citys[7].x = 31; citys[7].y = 62;
citys[8].x = 52; citys[8].y = 33;
citys[9].x = 51; citys[9].y = 21;
citys[10].x = 42; citys[10].y = 41;
citys[11].x = 31; citys[11].y = 32;
citys[12].x = 5; citys[12].y = 25;
citys[13].x = 12; citys[13].y = 42;
citys[14].x = 36; citys[14].y = 16;
citys[15].x = 52; citys[15].y = 41;
citys[16].x = 27; citys[16].y = 23;
citys[17].x = 17; citys[17].y = 33;
citys[18].x = 13; citys[18].y = 13;
citys[19].x = 57; citys[19].y = 58;
citys[20].x = 62; citys[20].y = 42;
citys[21].x = 42; citys[21].y = 57;
citys[22].x = 16; citys[22].y = 57;
citys[23].x = 8; citys[23].y = 52;
citys[24].x = 7; citys[24].y = 38;
citys[25].x = 27; citys[25].y = 68;
citys[26].x = 30; citys[26].y = 48;
citys[27].x = 43; citys[27].y = 67;
citys[28].x = 58; citys[28].y = 48;
citys[29].x = 58; citys[29].y = 27;
citys[30].x = 37; citys[30].y = 69;
citys[31].x = 38; citys[31].y = 46;
citys[32].x = 46; citys[32].y = 10;
citys[33].x = 61; citys[33].y = 33;
citys[34].x = 62; citys[34].y = 63;
citys[35].x = 63; citys[35].y = 69;
citys[36].x = 32; citys[36].y = 22;
citys[37].x = 45; citys[37].y = 35;
citys[38].x = 59; citys[38].y = 15;
citys[39].x = 5; citys[39].y = 6;
citys[40].x = 10; citys[40].y = 17;
citys[41].x = 21; citys[41].y = 10;
citys[42].x = 5; citys[42].y = 64;
citys[43].x = 30; citys[43].y = 15;
citys[44].x = 39; citys[44].y = 10;
citys[45].x = 32; citys[45].y = 39;
citys[46].x = 25; citys[46].y = 32;
citys[47].x = 25; citys[47].y = 55;
citys[48].x = 48; citys[48].y = 28;
citys[49].x = 56; citys[49].y = 37;
citys[50].x = 30; citys[50].y = 40;
}
void set_dic() {//set distance
for (int i = 0; i<N; ++i) {
for (int j = 0; j<N; ++j) {
dic[i][j] = sqrt(pow(citys[i].x - citys[j].x, 2) + pow(citys[i].y - citys[j].y, 2));
}
}
}
double dic_two_point(city a, city b) {
return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2));
}
double count_energy(int* conf) {
double temp = 0;
for (int i = 1; i<N; ++i) {
if (conf[i] < 0 || conf[i] >= N)
return 0;
temp += dic_two_point(citys[conf[i]], citys[conf[i - 1]]);
}
temp += dic_two_point(citys[conf[0]], citys[conf[N - 1]]);
return temp;
}
void generate(int* s) {//rand
bool v[num];
memset(v, false, sizeof(v));
for (int i = 0; i<N; ++i) {
s[i] = rand() % N;
while (v[s[i]]) {
s[i] = rand() % N;
}
v[s[i]] = true;
}
}
void set_fitness() {
sum_fitness = 0;
fitness[0] = sum_energy / energy[0];
sum_fitness += fitness[0];
for (int i = 1; i < pop_size; ++i) {
sum_fitness += sum_energy / energy[i];
fitness[i] = sum_fitness;
}
for (int i = 0; i < pop_size; ++i) {
fitness[i] /= sum_fitness;
}
}
int generate_pop() {//初始化種群
double ans = 1e9;
int maxi = -1;
for (int i = 0; i < pop_size; ++i) {
generate(seq_pop[i]);
energy[i] = count_energy(seq_pop[i]);
sum_energy += energy[i];
if (ans > energy[i]) {
maxi = i;
ans = energy[i];
}
}
answer = count_energy(seq_pop[0]);
return maxi;
}
int cho() {//選擇一個
double temp = rand() % 1000 / 1000.0;
for (int i = 0; i < pop_size; ++i) {
if (fitness[i] > temp)
return i;
}
}
int choose() {//選擇
int t_seq[pop_size][num];
int t = 0;
int maxi = -1;
int ans = 1e9;
sum_energy = 0;
for (int i = 0; i < pop_size; ++i) {
t = cho();
for (int j = 0; j < N; ++j)
t_seq[i][j] = seq_pop[t][j];
energy[i] = count_energy(t_seq[i]);
sum_energy += energy[i];
if (ans > energy[i]) {
ans = energy[i];
maxi = i;
}
}
for (int i = 0; i < pop_size; ++i) {
for (int j = 0; j < N; ++j) {
seq_pop[i][j] = t_seq[i][j];
}
}
return maxi;
}
void solve(int seq[]) {//解決序列衝突
bool visit[num];
memset(visit, false, sizeof(visit));
for (int i = 0; i < N; ++i) {
if (seq[i] < 0 || seq[i] >= N)
return;
if (!visit[seq[i]]) {
visit[seq[i]] = true;
}
else {
for (int j = 0; j < N; ++j) {
if (!visit[j]) {
visit[j] = true;
seq[i] = j;
break;
}
}
}
}
}
int recom() {//雜交_單點交叉
int t_seq[2][num];
double ans = 1e9;
int maxi = -1;
for (int i = 0; i < pop_size; i += 2) {
double tt = rand() % 1000 / 1000.0;
if (tt>v)
continue;
int temp = rand() % N;
for (int j = 0; j < N; ++j) {
if (j >= temp && j <= N) {
t_seq[0][j] = seq_pop[i + 1][j];
t_seq[1][j] = seq_pop[i][j];
}
else {
t_seq[0][j] = seq_pop[i][j];
t_seq[1][j] = seq_pop[i + 1][j];
}
}
solve(t_seq[0]);
solve(t_seq[1]);
if (count_energy(t_seq[0]) < energy[i]) {
for (int j = 0; j < N; ++j) {
seq_pop[i][j] = t_seq[0][j];
}
}
if (count_energy(t_seq[1]) < energy[i + 1]) {
for (int j = 0; j < N; ++j) {
seq_pop[i + 1][j] = t_seq[1][j];
}
}
}
sum_energy = 0;
for (int i = 0; i < pop_size; ++i) {
energy[i] = count_energy(seq_pop[i]);
sum_energy += energy[i];
if (ans > energy[i]) {
ans = energy[i];
maxi = i;
}
}
set_fitness();
return maxi;
}
int vari() {//變異
double ans = 1e9;
int maxi = -1;
int temp = 0, t1 = 0, t2 = 0;
for (int i = 0; i < pop_size; ++i) {
temp = rand() % 1000;
if (temp < u * 1000) {
t1 = rand() % N;
t2 = rand() % N;
swap(seq_pop[i][t1], seq_pop[i][t2]);
energy[i] = count_energy(seq_pop[i]);
}
}
sum_energy = 0;
for (int i = 0; i < pop_size; ++i) {
sum_energy += energy[i];
if (ans > energy[i]) {
ans = energy[i];
maxi = i;
}
}
set_fitness();
return maxi;
}
void test() {//初始化引數
ifstream ifile("data.txt");
if (!ifile) {
cout << "open field\n";
return;
}
while (!ifile.eof()) {
int te = 0;
ifile >> te;
ifile >> citys[te - 1].x >> citys[te - 1].y;
N = te;
}
}
void moni() {
int t_k = k;//迭代次數
double ans = 1e9;
int maxi = 0;
int t = -2;
double v = 1e-9;
ans = 1e9;
sum_fitness = 0;
sum_energy = 0;
memset(seq_pop, -1, sizeof(seq_pop));
//test();//讀取資料,初始化引數
t = generate_pop();//初始化種群
if (t >= 0 && ans > energy[t]) {
ans = energy[t];
maxi = t;
}
set_fitness();//設定適應度
while (t_k--) {
if (answer > ans) {
answer = ans;
seq = maxi;
}
t = choose();//選擇
if (t >= 0 && ans > energy[t]) {
ans = energy[t];
maxi = t;
}
t = recom();//雜交
if (t >= 0 && ans > energy[t]) {
ans = energy[t];
maxi = t;
}
t = vari();//變異
if (t >= 0 && ans > energy[t]) {
ans = energy[t];
maxi = t;
}
}
if (answer > ans) {
answer = ans;
seq = maxi;
}
}
void output() {//
cout << "the best road is : \n";
for (int i = 0; i < N; ++i) {
cout << seq_pop[seq][i];
if (i == N - 1)
cout << endl;
else
cout << " -> ";
}
cout << "the length of the road is " << answer << endl;
}
int main() {
srand(time(nullptr));
int t;
while (cin >> t) {//僅作為重啟演算法開關使用,無意義
init();//使用程式內建資料使用init()函式,
//test();//使用檔案讀取資料使用test()函式,
set_dic();
moni();
output();
}
return 0;
}