小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

干貨|十分鐘教你用動態(tài)規(guī)劃算法解Travelling Salesman Problem(TSP)問題...

 開心華之里 2017-08-26

     乍一看標題,大家是不是覺得“動態(tài)規(guī)劃”這四個字組合在一起有點眼熟?似乎哪會兒學過來著……但是吧,細細一琢磨,又忘了它具體是什么、怎么用、用來解決哪些問題了。

    莫方,小編出現(xiàn)就是為了解決大家一切在學(zhuang)習(bi)上的需求的。動態(tài)規(guī)劃忘了是吧,那今天小編就陪你好好回憶一下。

什么是TSP和動態(tài)規(guī)劃


    簡單來說,Travelling Salesman Problem (TSP) 是最基本的路線問題。它尋求的是旅行者由起點出發(fā),通過所有給定的需求點后,再次返回起點所花費的最小路徑成本,也叫旅行商問題、旅行推銷員問題、貨郎擔問題……

    當然,如果你非要把TSP理解成“內(nèi)容服務提供者”(Telematics Service Provider)小編也不會打你……計算機網(wǎng)絡學得不錯啊,四級過了嗎?

    說完TSP問題,咱們再來聊聊什么是動態(tài)規(guī)劃。

    動態(tài)規(guī)劃算法(Dynamic Programming,簡稱DP)通常用于求解具有某種最優(yōu)性質(zhì)的問題,其基本思想是將待求解問題分解成若干個子問題,先求解子問題,然后由這些子問題的解再得到原問題的解。

    看到這里想必你已經(jīng)明白了,動態(tài)規(guī)劃恰是一種求解TSP問題的好方法,具體如何求解,我們可以舉例實操一下。

實例操作

    假設現(xiàn)在有四個城市,它們分別是0,1,2,3,他們之間往來的代價如下圖所示:

    為了方便起見,我們把它化成二維表的形式:

    好了這里要敲黑板劃橫線了!現(xiàn)在,我們要從城市0出發(fā),期間1,2,3每個城市都必須經(jīng)過并且只能經(jīng)過一次,最后回到0,使得路上花費的代價最小。請問你要怎么走?

     事實上,這是一個最基本的TSP問題,且構(gòu)成最優(yōu)子結(jié)構(gòu)性質(zhì),所以可以使用動態(tài)規(guī)劃求解,下面來驗證一下此方法求解的可行性。

    設 s,s1,s2…s為滿足題意的最短回路。假設從s到s1的路徑已經(jīng)確定,則問題轉(zhuǎn)化為從s1到s的最短路徑問題。而很顯然,s1,s2…s一定可以構(gòu)成一條最短路徑,所以構(gòu)成最優(yōu)子結(jié)構(gòu)性質(zhì),可以用動態(tài)規(guī)劃求解。

    明確問題可解,那下一步就是列方程求解了。

簡單推導一下動態(tài)規(guī)劃方程

    用 V’ 表示一個點的集合,假設從頂點 s 出發(fā), d ( i , V’ ) 表示當前到達頂點 i,經(jīng)過 V’ 集合中所有頂點一次的最小花費。

    ① 當 V’ 為僅包含起點的集合,也就是:

d ( s , { s } ) = 0 ;

    ② 其他情況,則對子問題求最優(yōu)解。需在 V’ 這個城市集合中,嘗試每一個城市結(jié)點,并求出最優(yōu)解。

    ③ 最后的求解方式為:

    其中 S 為包含所有點的集合。

    把公式一套,題就解了。是不是很簡單?但是,小編還有更簡單的方法。

    其實,絕大部分TSP問題都比例子中復雜許多,用程序求解是更好的選擇。在這里小編給大家提供一種較為簡單的方法,只要把動態(tài)規(guī)劃算法原理掌握好了,代碼自然就不難理解了。

用代碼前,你需要做哪些準備?

理解狀態(tài)壓縮DP

   所謂狀態(tài)壓縮,就是利用二進制以及位運算來實現(xiàn)對于本來應該很大的數(shù)組的操作。而求解動態(tài)規(guī)劃問題,很重要的一環(huán)就是狀態(tài)的表示,一般來說,一個數(shù)組即可保存狀態(tài)。但是有這樣的一些題目,它們具有DP問題的特性,但是狀態(tài)中所包含的信息過多,如果要用數(shù)組來保存狀態(tài)的話需要四維以上的數(shù)組。于是,我們就需要通過狀態(tài)壓縮來保存狀態(tài),而使用狀態(tài)壓縮來保存狀態(tài)的DP就叫做狀態(tài)壓縮DP。

    例題TSP的動態(tài)規(guī)劃方程中,V’ 是一個集合,而對于集合的狀態(tài)表示最簡單的辦法就是利用C++中STL里的set,但是這個時候就要考慮一個問題,在代碼實現(xiàn)的時候,我們不能用一個集合去做一個數(shù)組的下標。自然而然,我們想到可以利用集合的特征值,但這個方法很復雜,而且不容易實現(xiàn)。

    小編在這里給大家普及一下位運算的知識。最簡單的與(and),或 ( or ),非 ( not ), 大家都很熟悉,和邏輯電路是相通的。而對于異或 ( xor ), 則是很有趣的一種位運算,它的運算規(guī)則是相同為 0,不同為 1。例如: 

1 xor 1 = 0,0 xor 0 = 0,

1 xor 0 = 1,0 xor 1 = 1;

    它的運算滿足交換律以及結(jié)合律。除此之外,xor 還有很多神奇的操作,有興趣的同學可以自己去查閱。

    再復雜一點的有左移 ( shl ), 右移 ( shr ),相當于對于二進制數(shù)的位置移動。例如10001(2) shl 1,就是10001(2)左移一位,變成了100010(2),換算成十進制,相當于擴大了 2 倍,同理右移則是縮減兩倍。那么對于任意的一個二進制數(shù),左移 k 位就是乘 2k, 右移就是整除 2k 。

    |||| 小貼士:

在C++中,位運算操作符分別是:

與 &,或 |非 ~,異或 ^

左移 ,右移 >>

   推到動態(tài)規(guī)劃方程時,我們注意到 V’ 是一個數(shù)的集合,而且解決的問題規(guī)模比較小,于是可以用一個二進制數(shù)來存儲這個集合。簡單來說就是——如果城市 k 在集合 V’ 中,那么存儲集合的變量 i 的第 k 位就為 1,否則為 0。由于有 n 個城市,所有的狀態(tài)總數(shù)我們用 M 來表示,那么很明顯:M = 2^n,而 0 到 2^n -1 的所有整數(shù)則構(gòu)成了 V’ 的所有狀態(tài)。這樣,結(jié)合位運算,動歸方程的狀態(tài)表示就很容易了。

準備所需工具

    還有兩樣你需要準備的東西,那就是城市數(shù)據(jù)文件編譯軟件。代碼中使用的城市數(shù)據(jù)文件可以有兩種保存格式:一種是上例提到的矩陣式,也可以是 “城市名 城市X坐標 城市Y坐標” 式。大家可以根據(jù)實際情況自行調(diào)整。

    至于編譯軟件,小編在這里給大家提供的是C++代碼,用你用得最順手的編譯器就可以了。小編在這里強烈推薦DEV-CPP!體積小,編譯方便,代碼還很美觀。

    好了不啰嗦了,上代碼~

 代碼示例(C++)

#include

using namespace std;

// 定義常量

const int INF = 0x3f3f3f3f;

#define sqr(x) ((x)*(x))

// 定義變量

string file_name;

int type; // type == 1 滿秩矩陣格式, type == 2 二維坐標式

int s;

int N;// 城市結(jié)點數(shù)量

int init_point;

double **dp; // 動態(tài)規(guī)劃狀態(tài)數(shù)組dp[i][j],i表示集合V’,j表示當前到達的城市結(jié)點

double **dis; // 兩個城市結(jié)點之間的距離

double ans;

// 定義結(jié)構(gòu)體

struct vertex{

  double x, y; // 城市結(jié)點的坐標

  int id; // 城市結(jié)點的id

  int input(FILE *fp){

    return fscanf(fp, '%d %lf %lf', &id, &x, &y);

  }

}*node;

 

double EUC_2D(const vertex &a, const vertex &b){

  return sqrt(sqr(a.x - b.x) + sqr(a.y - b.y));

}

 

void io(){ // 數(shù)據(jù)讀入

  printf('input file_name and data type\n');

  cin >> file_name >> type;

  FILE *fp = fopen(file_name.c_str(), 'r');

  fscanf(fp, '%d', &N);

  node = new vertex[N + 5];

  dis = new double*[N + 5];

  if (type == 1){

    for (int i = 0; i < n;="" i="">{

      dis[i] = new double[N];

      for (int j = 0; j < n;="" j="">

      fscanf(fp, '%lf', &dis[i][j]);

    }

  }

  else{

    for (int i = 0; i < n;="" i="">

    node[i].input(fp);

    for (int i = 0; i < n;="" i="">{

      dis[i] = new double[N];

      for (int j = 0; j < n;="" j="">

      dis[i][j] = EUC_2D(node[i], node[j]);// 計算城市之間的距離

    }

  }

  fclose(fp);

  return;

}


void init(){ // 數(shù)據(jù)初始化

  dp = new double*[(1 < n)="" +="">

  for(int i = 0; i < (1="">< n);="">{

    dp[i] = new double[N + 5];

    for(int j = 0; j < n;="">

    dp[i][j] = INF; 

  } // 初始化,除了dp[1][0],其余值都為INF

  ans = INF;

  return;

}

 

double slove(){

  int M = (1 <>

  // M就是第四部分所說的V’狀態(tài)總數(shù),1<>

  dp[1][0] = 0; 

  // 假設固定出發(fā)點為0,從0出發(fā)回到0的花費為0。TSP只要求是一個環(huán)路,所以出發(fā)點可以任選

  for (int i = 1; i < m;="" i="">{ 

  // 枚舉V’的所有狀態(tài)

    for (int j = 1; j < n;="" j="">

    // 選擇下一個加入集合的城市

      if (i & (1 < j))=""> 

      // 城市已經(jīng)存在于V’之中

      if (!(i & 1)) continue; 

      // 出發(fā)城市固定為0號城市

      for (int k = 0; k < n;="" k="">

      // 在V’這個城市集合中嘗試每一個結(jié)點,并求出最優(yōu)解

        if (i & (1 <>{ 

        // 確保k已經(jīng)在集合之中并且是上一步轉(zhuǎn)移過來的結(jié)點

           dp[(1 < j)="" |="" i][j]="min(dp[(1">< j)="" |="" i][j],="" dp[i][k]="" +="" dis[k][j]);="">// 轉(zhuǎn)移方程

          } // 將j點加入到i集合中

      }

    }

  }

  for (int i = 0; i < n;="" i="">

  ans = min(dp[M - 1][i] + dis[i][0], ans);

  // 因為固定了出發(fā)點,所以要加上到城市0的距離。另外要從所有的完成整個環(huán)路的集合V’中選擇,完成最后的轉(zhuǎn)移

  return ans;

}

 

int main(){

  io();

  init();

  string tmp = file_name + '.sol';

  FILE *fp = fopen(tmp.c_str(), 'w');

  fprintf(fp, '%.2lf\n', slove());

  delete[] dp;

  delete[] node;

  delete[] dis;

  fclose(fp);

  return 0;

}

 算例運行 

例1:滿秩矩陣式(type==1)

        就拿前文的例子,其文件存儲的格式應如下:

4

0   3   6   7

5   0   2   3

6   4   0   2

3   7   5   0

運行結(jié)果

11

例2:二維坐標式(type==2)

         若城市數(shù)據(jù)文件如下所示:

     16
     1   38.24   20.42
     2   39.57   26.15
     3   40.56   25.32
     4   36.26   23.12
     5   33.48   10.54
     6   37.56   12.19
     7   38.42   13.11
     8   37.52   20.44
     9   41.23   9.10
   10   41.17   13.05
   11   36.08   -5.21
   12   38.47   15.13
   13   38.15   15.35
   14   37.51   15.17
   15   35.49   14.32
   16   39.36   19.56

運行結(jié)果

73.99


總結(jié)

    動態(tài)規(guī)劃通過迭代方式尋找每一個子問題的最優(yōu)解法,因此該解法可以得出TSP的最優(yōu)解。但算法時間效率較差,因此在問題規(guī)模逐漸變大的過程中計算量會急劇膨脹。所以,本算法只適用于小規(guī)模求精確解的TSP問題,但對于你平常遇到的大多數(shù)TSP問題,這也足夠了。

    所以,你學會了嗎?




    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多