用遺傳算法解決TSP問題


淺談遺傳算法:https://www.cnblogs.com/AKMer/p/9479890.html

Description

\(小m\)在踏上尋找\(小o\)的路程之后不小心碰到了大魔王\(fater\)
大魔王看了看\(小m\)的命運,心生憐憫,便給\(小m\)和自己做一個交換的機會。
這個交換是這樣的:
由於\(小o\)不知在天涯海角,\(小m\)的要找到實在是太難了。所以大魔王願意把\(小m\)\(小o\)同時扔到一個迷宮(\(n\)個點的完全無向圖)里,但是\(小o\)在哪個點上是未知的。
\(小m\)初始在\(1\)號點。\(1\)號點上有迷宮出口,打開出口需要遍歷整張圖拿到全部點上的鑰匙。\(小m\)要將自己日思夜念的\(小o\)從迷宮深處的不知方位的點帶出來就必須遍歷所有的點。然而大魔王並不願意一昧的幫助\(小m\),他說如果\(小m\)重復經過一個點(\(1\)號點除外)或者走過的路程不是最短的環那么他和\(小o\)將永遠無法逃出迷宮。
\(小m\)想見\(小o\),不管三七二十一便答應了大魔王。在進去之前大魔王給了他一張地圖,然后便將\(小m\)扔進了迷宮並且關上了入口。
\(小m\)在黑暗的迷宮里才明白過來,自己雖然是\(OIer\),但是自己的\(Computer\)似乎被大魔王沒收了。於是乎,作為同是\(OIer\)的摯友,他打電話告訴你這件事情並且希望你能幫助他。他將地圖以一個矩陣的形式給了你。由於他實在是太想念\(小o\)了,但他也不好意思太麻煩你,所以他希望你能在\(1s\)內幫他算出最短路徑。只要你告訴他正確的答案,出題人就會告訴他怎么走才是最優的。

Input

輸入一共\(n+1\)
第一行一個數字\(n\)
接下來一個\(n\)階矩陣,\(dis[i][j]\)記錄\(i,j\)之間的距離

Output

輸出僅一行
一個數字表示最短的哈密爾頓環的長度

Sample Input

4
0 1 2 3
1 0 2 3
2 2 0 3
3 3 3 0

Sample Output

9

\(n\leqslant100,dis[i][j]\leqslant100000\)

我自己造的數據:https://files.cnblogs.com/files/AKMer/TSP.zip
由於爆搜跑得慢所以只造了\(n\leqslant12\)的數據

首先題目意思是要你求一個無向完全圖中最短哈密爾頓環的長度。

對於這題用遺傳算法,估價函數可以設成一個最長環長度減去當前染色體所表示的環的長度,那么環長度越短,適應性就會越高。

然后交換操作要進行一些改進。因為直接交換會使得某個染色體中重復出現某個點(會導致\(小m\)\(小o\)不能逃出迷宮!!)。於是乎我們就將串與串之間交換改成在一條染色體上交換兩個位置,和變異一起做了。

就大功告成了。如果是提答題的話遺傳代數和變異次數可以設置多一點,如果是想在傳統題上騙分記得不要設置太高導致\(TLE\)

一開始\(srand(time(0))\)\(Wa\)不止:

此處輸入圖片的描述

此處輸入圖片的描述

此處輸入圖片的描述

難道就只能這樣了嗎?不!
后來我\(srand\)我倆的名字首字母就\(A\)掉了哈哈哈

此處輸入圖片的描述

時間復雜度:\(O(歐洲人)\)
空間復雜度:\(O(n)\)

代碼如下:

#include <ctime>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int inf=10000005;
const int maxn=105;

int dis[maxn][maxn];//dis存距離
int f[maxn],f1[maxn],g[maxn];//f存適應度函數,f1緩存f,g存確定性選擇法下選幾次
int n,low=inf,mx_f,tim,chr_cnt=100;//low存當前種群中最短長度,tim存low持續不變了多少代,chr_cnt存染色體條數,mx_f存當前種群最長環長度

int read() {
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    return x*f;
}//快讀

int random(int limit) {
    return rand()%limit;
}//rand一個[0,limit)范圍的數字

struct Chromosome {
    int gene[maxn];//存環上點的順序

    int calc() {
        int res=0;
        for(int i=2;i<=n;i++)
            res+=dis[gene[i-1]][gene[i]];
        return res+dis[gene[n]][gene[1]];//依次累加環上的邊
    }//算適應度函數

    void Initialization() {
        for(int i=1;i<=n;i++)
            gene[i]=i;
        random_shuffle(gene+1,gene+n+1);
    }//初始化
}chr[maxn],new_chr[maxn],ans;//擦汗r存當前種群,new_chr存下一代種群,ans存方案,最后用於統計答案

void select() {//選擇
    for(int i=1;i<=chr_cnt;i++) {
        f[i]=chr[i].calc();//算最短路
		mx_f=max(mx_f,f[i]);
        if(f[i]<low) {
            low=f[i],tim=0;
            ans=chr[i];//如果更新low就更新全局答案
        }
    }
    tim++;int res=0,cnt=0;//res存總適應度,cnt存下一代染色體條數
    for(int i=1;i<=chr_cnt;i++)
        f[i]=mx_f-f[i]+1,res+=f[i];//更新適應度,累加到res里
    for(int i=1;i<=chr_cnt;i++)
        g[i]=1.0*f[i]/res*chr_cnt,f1[i]=f[i],cnt+=g[i];//算g[i],將f緩存到f1里
    while(cnt<chr_cnt)g[random(chr_cnt)+1]++,cnt++;//沒有選滿就繼續選
    cnt=0;
    for(int i=1;i<=chr_cnt;i++) {
        for(int j=1;j<=g[i];j++) {
            new_chr[++cnt]=chr[i];
            f[cnt]=f1[i];//同步更新適應度
            if(cnt==chr_cnt)break;
        }
        if(cnt==chr_cnt)break;//滿了就不加了
    }//模擬選擇過程
    for(int i=1;i<=chr_cnt;i++)
        chr[i]=new_chr[i];//更新chr
}

void variety() {//變異
    for(int i=1;i<=500*chr_cnt;i++) {//不用交換的話就多變異幾次
        int u=random(chr_cnt)+1,l=random(n)+1,r=random(n)+1;//u是即將變異的染色體,l和r是要交換的位置
        Chromosome tmp=chr[u];swap(tmp.gene[l],tmp.gene[r]);
        int res=tmp.calc();//tmp緩存變異后的染色體
        if(res<chr[u].calc()) {//如果變異后更優了就變異
            chr[u]=tmp;
            f[u]=mx_f-res;//更新染色體和適應度
        }
    }
}

void Genetic() {
    while(1) {
        select();
        if(tim>200)return;//超過200代不變就欽定它正確了
        variety();
    }//遺傳過程
}

int main() {
    srand('o'+'x'+'y'+'m'+'z'+'f');//她叫小o,我是小m
    n=read();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            dis[i][j]=read();//讀入
    for(int i=1;i<=chr_cnt;i++)
        chr[i].Initialization();//初始化祖先種群
    Genetic();//遺傳
    printf("%d\n",ans.calc());//輸出
    return 0;
}


免責聲明!

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



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