生活随笔
收集整理的這篇文章主要介紹了
旅行商问题的n种解法
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
問題描述:
旅行商問題(Traveling Salesman Problem,TSP)是旅行商要到若干個城市旅行,各城市之間的費用是已知的,為了節省費用,旅行商決定從所在城市出發,到每個城市旅行一次后返回初始城市,問他應選擇什么樣的路線才能使所走的總費用最短?此問題可描述如下:設G=(V,E)是一個具有邊成本cij的有向圖,cij的定義如下,對于所有的i和j,cij>0,若<i,j>不屬于E,則cij=∞。令|V|=n,并假設n>1。 G的一條周游路線是包含V中每個結點的一個有向環,周游路線的成本是此路線上所有邊的成本和。
問題分析:
旅行商問題要從圖G的所有周游路線中求取最小成本的周游路線,而從初始點出發的周游路線一共有(n-1)!條,即等于除初始結點外的n-1個結點的排列數,因此旅行商問題是一個排列問題。排列問題比子集合的選擇問題通常要難于求解得多,這是因為n個物體有n!種排列,只有 個子集合(n!>O( ))。通過枚舉(n-1)!條周游路線,從中找出一條具有最小成本的周游路線的算法,其計算時間顯然為O(n!)。
枚舉法思想: 程序中采用深度優先策略。(采用隱式和顯式兩種形式)
枚舉算法的特點是算法簡單,但運算量大,當問題的規模變大,循環的階數越大,執行的速度越慢。如果枚舉范圍太大(一般以不超過兩百萬次為限),在時間上就難以承受。在解決旅行商問題時,以頂點1為起點和終點,然后求{2…N}的一個全排列,使路程1→{2…N}的一個全排列→1上所有邊的權(代價)之和最小。所有可能解由(2,3,4,…,N)的不同排列決定。
核心代碼(完整源代碼見源代碼)
??
為便于討論,介紹一些關于解空間樹結構的術語。在下面分析回溯法和分支限界法時都直接或間接用到解空間樹。在解空間樹中的每一個結點確定所求問題的一個問題狀態 (problem state)。由根結點到其它結點的所有路徑則確定了這個問題的狀態空間 (state space)。解狀態(solution states)表示一些問題狀態S,對于這些問題狀態,由根到S的那條路徑確定了這解空間中的一個元組。答案狀態 (answer states)表示一些解狀態S,對于這些解狀態而言,由根到S的這條路徑確定了這問題的一個解(即,它滿足隱式約束條件)。解空間的樹結構稱為狀態空間樹 (state space tree)。
對于旅行商問題,一旦設想出一種狀態空間樹,那么就可以先系統地生成問題狀態,接著確定這些問題狀態中的哪些狀態是解狀態,最后確定哪些解狀態是答案狀態,從而將問題解出。為了生成問題狀態,采用兩種根本不同的方法。如果已生成一個結點而它的所有兒子結點還沒有全部生成,則這個結點叫做活結點 。當前正在生成其兒子結點的活結點叫E-結點 。不再進一步擴展或者其兒子結點已全部生成的生成結點就是死結點 。在生成問題狀態的兩種方法中,都要用一張活結點表。在第一種方法中,當前的E-結點R一旦生成一個新的兒子C,這個兒子結點就變成一個新的E-結點,當完全檢測了子樹C之后,R結點就再次成為E-結點。這相當與問題狀態的深度優先生成。在第二種狀態生成方法中,一個E-結點一直保持到死結點為止。這兩種方法中,將用限界函數去殺死還沒有全部生成其兒子結點的那些活結點。如果旅行商問題要求找出全部解,則要生成所有的答案結點。使用限界函數的深度優先結點生成方法稱為回溯法 。E-結點一直保持到死為止的狀態生成方法稱為分支限界法 。
回溯法思想:
?????? 為了應用回溯法,所要求的解必須能表示成一個n- 元組(x1,…,Xn),其中x1是取自某個有窮集Si。通常,所求解的問題需要求取一個使某一規范函數P(x1,…,Xn)取極大值(或取極小值或滿足該規范函數條件)的向量。
?????? 假定集合Si的大小是mi,于是就有m=m1m2…Mn個n-元組可能滿足函數P。所謂硬性處理是構造這m個n-元組并逐一測試它們是否滿足P,從而找出該問題的所有最優解。而回溯法的基本思想是,不斷地用修改過的函數Pi(x1,…Xi)(即限界函數)去測試正在構造中的n-元組的部分向量(x1,…,Xi),看其是否可能導致最優解。如果判定(x1,…,Xi)不可能導致最優解,那么就可能要測試的后n-i個元素組成的向量一概略去。因此回溯法作的次數比硬性處理作的測試次數(m次)要少得多。用回溯法求解的旅行商問題,即在枚舉法的基礎上多了一個約束條件,約束條件可以分為兩種類型:顯示約束和隱式約束。
核心代碼(完整源代碼見源代碼)
?
分支限界法思想: 本題采用FIFO分支限界法。
如前所述,分支限界法是在生成當前E-結點全部兒子之后再生成其它活結點的兒子,且用限界函數幫助避免生成不包含答案結點子樹的狀態空間的檢索方法。在總的原則下,根據對狀態控件樹中結點檢索的次序的不同又將分支限界設計策路分為數種不同的檢索方法。在求解旅行商問題時,程序中采用FIFO檢索(First In First Out),它的活結點表采用一張先進先出表(即隊列)。可以看出,分支限界法在兩個方面加速了算法的搜索速度,一是選擇要擴展的節點時,總是選擇選擇一個最小成本的結點,盡可能早的進入最有可能成為最優解的分支;二是擴展節點的過程中,舍棄導致不可行解或導致非最優解的子結點。
核心代碼(完整源代碼見源代碼)
?
貪心法思想:
貪心法是一種改進了的分級處理方法。它首先旅行商問題描述,選取一種度量標準。然后按這種度量標準對n個輸入城市排序,并按序一次輸入一個城市。如果這個輸入和當前已構成在這種量度意義下的部分最優解加在一起不能產生一個可行解,則不把這個城市加入到這部分解中。這種能夠得到某種量度意義下的最優解的分級處理方法成為談心方法。
獲得最優路徑的貪心法應一條邊一條邊地構造這棵樹。根據某種量度來選擇將要計入的下一條邊。最簡單的量度標準是選擇使得迄今為止計入的那些邊的成本的和有最小增量的那條邊。
核心代碼(完整源代碼見源代碼)
?
源代碼:
在程序執行目錄下建立data.txt文件,用于存放城市節點信息,格式如下:
5? 5
0? 7? 6? 1? 3??
7? 0? 3? 7? 8?
6? 3? 0? 12 11
1? 7? 12 0? 2
3? 8? 11 2? 0
第一行表示為5行5列,之后為各個節點的權值;
程序執行前先建立如下頭文件,用于存儲和表示節點信息:
?
[cpp] view plaincopy
//-------------------------------------?AdjtwGraph.h文件--------------------------------------------------?? #ifndef?AdjTWGraph_H?? #define?AdjTWGraph_H?? #include?<vector>?? #include?<iostream>?? using?namespace?std;?? const?int?MaxV=100;?? struct?Edge?? ?{?? ????int?dest;?? ????int?weight;?? ????Edge?*?next;?? ?????Edge(){}?? ?????Edge(int?d,int?w):dest(d),weight(w),next(NULL){}?? };?? struct?item?? ?{????int?data;?? ????Edge?*?adj;?? };?? class?AdjTWGraph?? ?{?? private:?? ????item?vertices[MaxV];?? ????int?numV,numE;?? public?:?? ????AdjTWGraph();?? ????~AdjTWGraph();?? ?????int?NumV(){return?numV;}?? ?????int?NumE(){return?numE;}?? ????int?GetValue(const?int?i);?? ????int?GetWeight(const?int?v1,const?int?v2);?? ????void?InsertV(const?int?&?vertex);?? ????void?InsertE(const?int?v1,const?int?v2,int?weight);?? ????friend?ostream&?operator<<(ostream&?os,??AdjTWGraph?&?m)?? ?????{?????for?(int?i?=?0;?i?<?m.numV?;?i++)?????{?? ????????????for?(int?j?=?0;?j?<?m.numV;?j++)?? ????????????????os?<<?right?<<?m.GetWeight(i,j)?<<?"?";?? ????????????os?<<?endl;?? ????????}?? ????????return?os;?? ????}?? ????friend?istream&?operator>>(istream&?is,?AdjTWGraph?&?m)?? ?????{????int?t;?? ????????for?(int?i?=?0;?i?<?m.NumV();?i++)?? ????????????for?(int?j?=?0;?j?<?m.NumV();?j++)?? ?????????????{?? ????????????????is?>>?t;?????m.InsertE(i,j,t);?? ????????????}?? ????????return?is;?? ????}?? };?? AdjTWGraph::AdjTWGraph()?? ?{?? ????for(int?i=0;i<MaxV;i++)?????vertices[i].adj=NULL;?? ????numV=0;numE=0;?? }?? AdjTWGraph::~AdjTWGraph()?? ?{?? ????for(int?i=0;i<numV;i++)?? ?????{?? ????????Edge?*?p=vertices[i].adj,*q;?? ????????while(p!=NULL)?? ?????????{?? ????????????q=p->next;delete?p;p=q;?? ????????}?? ????}?? }?? ?int?AdjTWGraph::GetValue(const?int?i){????return?vertices[i].data;??}?? int?AdjTWGraph::GetWeight(const?int?v1,const?int?v2)?? ?{?? ????Edge?*p=vertices[v1].adj;?? ????while(p!=NULL?&&?p->dest<v2)?p=p->next;?? ?????if(v2!=p->dest)????{????return?0;????}?? ????return?p->weight;?? }?? ?void?AdjTWGraph::InsertV(const?int?&?v)?{?vertices[numV].data=v;?numV++;??}?? void?AdjTWGraph::InsertE(const?int?v1,const?int?v2,int?weight)?? ?{?? ????Edge?*?q=new?Edge(v2,weight);?? ????if(vertices[v1].adj==NULL)?vertices[v1].adj=q;?? ????else?? ?????{?? ????????Edge?*curr=vertices[v1].adj,*pre=NULL;?? ?????????while(curr!=NULL?&&?curr->dest<v2)????{????pre=curr;curr=curr->next;????}?? ?????????if(pre==NULL){????q->next=vertices[v1].adj;vertices[v1].adj=q;????????}?? ?????????else????{????q->next=pre->next;pre->next=q;????}?? ????}?? ????numE++;?? }?? #endif?? ?? //-------------------------------------?tsp.cpp文件--------------------------------------------------?? #include?"AdjtwGraph.h"?? #include?<fstream>?? #include?<vector>?? #include?<algorithm>?? #include?<ctime>?? #include?<queue>?? using?namespace?std;?? ofstream?fout("out.txt");?? int?N;?? AdjTWGraph?g;?? struct?Node?????? ?{???int?currentIndex;?? ????int?level;?? ????Node?*?previous;?? ?????Node(int?L?=?0,?int?V?=?0,?Node?*p?=?NULL):level(L),currentIndex(V),?previous(p)?{}?? };?? class?TspBase?? ?{?? protected:?????? ????vector<int>?currentPath;?? ????vector<int>?bestPath;?? ????int?cv;?? ????int?bestV;?? ????Node?*?root;?? ?? ????int?SumV();????? ????void?EnumImplicit(int?k);?? ????void?BackTrackImplicit(int?k);?? ?? ????void?EnumExplicit(Node?*?r);?? ????void?BackTrackExplicit(Node?*?r);?? ????void?FIFOBB();?? ?? ????bool?Valid(Node?*p,int?v)??//?? ?????????{????bool?flag?=?true;?? ????????????for(Node?*r?=?p;?r->level?>?0?&&?V;?r?=?r->previous)??flag?=?r->currentIndex?!=v;?? ????????????return?flag;?? ????????}?? ????void?StoreX(Node?*?p)?//?? ?????????{for(Node?*r?=?p;?r->level?>0?;?r?=?r->previous?)?? ?{????currentPath[r->level-1]?=?r->currentIndex;????}?? ????????}?? ????void?Print();?? public:?? ?????TspBase(){currentPath.resize(N);????bestPath.resize(N);????}?? ?~TspBase(){currentPath.resize(0);bestPath.resize(0);}?? ?? ????void?TspEnumImplicit();?? ????void?TspBackTrackImplicit();?? ?? ????void?TspEnumExplicit();?????????? ????void?TspBackTrackExplicit();?? ???? ????void?TspBB();?? ?? ????void?TspGreedy();??????? ?????? ????void?DataClear(bool?flag)?? ?????{???currentPath.resize(N);????????bestPath.resize(N);?? ?????????if(flag)????????{?Node?*?p=root,*q;?? ??????????????????????while(p!=NULL)?{q=p->previous;?delete?p;?p=q;}?????? ????????}?? ????}?? };?? void?TspBase::TspEnumImplicit()??//?????????枚舉隱式?? ?{????fout<<"TspEnumImplicit?..."<<endl;?? cv=0;?bestV=10000;?? ????for(int?i=0;i<N;i++)????currentPath[i]=i;?? ????EnumImplicit(1);?? ????Print();?? }?? void?TspBase::EnumImplicit(int?k)?? ?{????if(k?==?N)?? ?????{????if((cv?+?g.GetWeight(currentPath[N-1],0))?<?bestV)?? ?????????{?? ????????????bestV?=?cv?+?g.GetWeight(currentPath[N-1],0);?? ????????????for(int?i?=?0;?i?<?N;?i++)?? ??????????????bestPath[i]?=?currentPath[i];?? ????????}?????????? ????}?? ????else?? ????????for(int?j?=?k;?j?<?N;?j++)?? ?????????{????swap(currentPath[k],currentPath[j]);?? ????????????cv?+=?g.GetWeight(currentPath[k-1],currentPath[k]);?? ????????????EnumImplicit(k+1);?? ????????????cv?-=?g.GetWeight(currentPath[k-1],currentPath[k]);?? ????????????swap(currentPath[k],currentPath[j]);?? ????????}?? }?? void?TspBase::TspEnumExplicit()????//??枚舉顯式?? ?{???fout<<"TspEnumExplicit??..."<<endl;?? cv=0;?????bestV=10000;?? ?????for(int?i=0;i<N;i++)?????currentPath[i]=i;?? ?????root=new?Node(0,-1,NULL);?? ?????EnumExplicit(root);?? ?????Print();?? }?? ?? void?TspBase::EnumExplicit(Node?*?r)?? ?{????if(r->level?==?N)?? ?????{????StoreX(r);????cv?=?SumV();?? ????????if(cv??<?bestV)?????? ?????????{????bestV?=?cv??;?? ????????????for(int?i?=?0;?i?<?N;?i++)?? ??????????????bestPath[i]?=?currentPath[i];?? ????????}?? ????}?? ????else?? ????????for(int?i?=?0;?i?<?N;?i?++)?? ?????????{?if(Valid(r,i))??? ?????????????{??Node?*q?=?new?Node(r->level+1,i,r);????EnumExplicit(q);????}?? ????????}?? }?? void?TspBase::TspBackTrackImplicit()?????//回溯隱式?? ?{????fout<<"TspBackTrackImplicit?..."<<endl;?? cv=0;??bestV=10000;?? ????for(int?i=0;i<N;i++)????currentPath[i]=i;?? ????BackTrackImplicit(1);?? ????Print();?? }?? void?TspBase::BackTrackImplicit(int?k)?? ?{????if(k?==?N)?? ?????{????if((cv?+?g.GetWeight(currentPath[N-1],0))?<?bestV)?? ?????????{?? ????????????bestV?=?cv?+?g.GetWeight(currentPath[N-1],0);?? ????????????for(int?i?=?0;?i?<?N;?i++)?? ??????????????bestPath[i]?=?currentPath[i];?? ????????}?????????? ????}?? ????else?? ????????for(int?j?=?k;?j?<?N;?j++)?? ?????????{?if((cv?+?g.GetWeight(currentPath[k-1],currentPath[j]))?<?bestV)?? ???????????{????swap(currentPath[k],currentPath[j]);?? ???????????????cv?+=?g.GetWeight(currentPath[k-1],currentPath[k]);?? ????????????BackTrackImplicit(k+1);?? ????????????cv?-=?g.GetWeight(currentPath[k-1],currentPath[k]);?? ????????????swap(currentPath[k],currentPath[j]);?? ??????????}?? ????????}?? }?? void?TspBase::TspBackTrackExplicit()??????//?回溯顯式?? ?{????fout<<"TspBackTrackExplicit??..."<<endl;??? cv=0;?????bestV=10000;?? ?????for(int?i=0;i<N;i++)?????currentPath[i]=i;?? ?????root=new?Node(0,-1,NULL);?? ?????BackTrackExplicit(root);?? ?????Print();?? }?? void?TspBase::BackTrackExplicit(Node?*?r)?? ?{????int?w=0;??//初值?? ????if(r->level?==?N)?? ?????{????StoreX(r);?? ????????cv?=?SumV();?? ????????if(cv??<?bestV)?????? ?????????{???bestV?=?cv??;?? ????????????for(int?i?=?0;?i?<?N;?i++)????????bestPath[i]?=?currentPath[i];?? ????????}?? ????}?? ????else?? ????????for(int?i?=?0;?i?<?N;?i?++)?? ?????????{??if(Valid(r,i))??? ?????????????{????Node?*q?=?new?Node(r->level+1,i,r);?? ????????????????w?+=?g.GetWeight(q->currentIndex,i);?? ????????????????if(w?<?bestV)???????BackTrackExplicit(q);?? ????????????????w?-=?g.GetWeight(q->currentIndex,i);?????? ????????????}?? ????????}?? }?? ?? ?? void?TspBase::Print()?//?? ?{???????fout<<"the?shortest?path?is??";?? ???????for(unsigned?i?=?0;?i?<?N;?i++)?? ?????????????fout<<bestPath[i]?+?1<<"--";?? ???????fout<<"1"<<endl;?? ???????fout<<"minimum?distance?is??"<<bestV<<endl;????????? }?? ?? void?TspBase::TspBB()???????//?分支限界法?? ?{????????fout<<"TspBB(FIFOBB)??........"<<endl;?? cv?=?0;????????bestV?=?100000;?? ????????for(unsigned?i?=?0;?i?<?N;?i++)????currentPath[i]?=?i;?? ????????root=new?Node(0,-1,NULL);?? ????????FIFOBB();?? ????????Print();?? }?? void?TspBase::FIFOBB()?? ?{?queue<Node*>?q;???Node?*r;?? ??q.push(root);?? ??int?w=0;??//初值?? ??while(!q.empty())?? ???{??????r?=?q.front();??????q.pop();?? ??????if(r->level?==?N)?? ???????{?StoreX(r);?? ????????cv?=?SumV();?? ????????if(cv??<?bestV)?????? ?????????{???bestV?=?cv??;?? ????????????for(int?i?=?0;?i?<?N;?i++)?????bestPath[i]?=?currentPath[i];?? ????????}?? ??????}?? ??????else?? ????????for(int?i?=?0;?i?<?N;?i?++)?? ?????????{????if(Valid(r,i))??? ?????????????{???Node?*s?=?new?Node(r->level+1,i,r);?? ????????????????w?+=?g.GetWeight(s->currentIndex,i);?? ????????????????if(w?<?bestV)???????q.push(s);?? ????????????????w?-=??g.GetWeight(s->currentIndex,i);?? ????????????}?? ????????}?? ??}?? }?? int?TspBase::SumV()???????????//用于FIFOBB?? ?{????int?s?=?0;?? ????for(int?i?=?0;?i?<?N;?i++)?? ????????s?+=?g.GetWeight(currentPath[i],currentPath[(i?+?1)%N]);?? ????return?s;?? }?? void?TspBase::TspGreedy()??//TSP貪心算法?? ?{?????fout<<"TspGreedy?........"<<endl;?? bestV?=?0;??????? ????vector<int>?NEAR(N);?//??? ????NEAR[0]?=?-1;?? ????for?(int?i?=?1;?i?<?N;?i++)?? ???????NEAR[i]?=?0;?? ????bestPath[0]?=?1;?? ????int?t;?? ????for?(int?s?=?1;?s?<?N;?s++)?? ?????{?? ??????int?j?=?1;?? ??????while?(j?<?N?&&?NEAR[j]?<?0)?/??? ??????????j++;?? ??????int?K?=?j;?? ??????for?(int?k?=?j?+?1;?k?<?N;?k++)?? ?????????if?(NEAR[k]?>=?0?&&??g.GetWeight(k,NEAR[k])?<?g.GetWeight(j,NEAR[j]))?? ???????????????j?=?k;?? ??????bestPath[s]?=?j?+?1;?? ??????bestV?+=g.GetWeight(j,NEAR[j]);?? ??????NEAR[j]?=?-1;?? ??????for?(k?=?K;?k?<?N;?k++)?//調整NEAR值?? ?????????if?(NEAR[k]?>=?0)?? ?????????????NEAR[k]?=?j;?? ??????t?=?j;?? ????}?? ????bestV?+=?g.GetWeight(t,0);?? ????fout<<"the?shortest?path?is??";?? ????for(unsigned?w?=?0;?w?<?N;?w++)?? ???????fout<<bestPath[w]?<<"--";?? ????fout<<"1"<<endl;?? ????fout<<"minimum?distance?is??"<<bestV<<endl;????? }?? ?? int?main(int?argc,?char*?argv[])?? ?{???int?m,n;?? ????ifstream?fin("data.txt");?? ????if(fin.bad())?return?1;?? ????fin?>>?m?>>?n;?? ????N?=?n;?? ????for(int?i=0;i<N;i++)??g.InsertV(i);?? ????fin?>>?g;?? ????TspBase?it;?? ????it.TspEnumImplicit();????it.DataClear(false);?? ?? ????it.TspBackTrackImplicit();????it.DataClear(false);?? ?? ????it.TspEnumExplicit();????it.DataClear(true);?? ?? ????it.TspBackTrackExplicit();????it.DataClear(true);?? ?? ????it.TspBB();????it.DataClear(true);?? ?? ????it.TspGreedy();????it.DataClear(false);?? ????return?0;?? }?? ?
?
執行結果: the shortest path is? 1--3--2--5--4--1 minimum distance is? 20
總結
以上是生活随笔 為你收集整理的旅行商问题的n种解法 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。