TSPLIB簡介與簡易解析器實現

來源:互聯網
上載者:User

標籤:c#   Regex   tsp   

背景知識

TSP即Travelling SalesmanProblem(旅行商人問題)的簡稱。是數學領域中的著名問題之一。有n個城市,一個旅行商人要從其中某一個城市出發,唯一走遍所有的城市,再回到他出發的城市,求最短的路線。這個問題對快遞業等行業也非常具有現實意義,當然現實中的TSP一般是動態,要更為複雜。TSP可以分為兩類,一類是對稱TSP(Symmetric TSP),另一類是非對稱TSP(Asymmetric TSP)。區別就在雩都市a到b和都市b到a的cost是否相等,相等的就是對稱TSP。這裡我們討論的都是對稱TSP。大家應該能想象的到,所有的TSP問題都可以用一個Graph來描述,當然這篇文章主要不是為了討論TSP本身,具體的定義就從略了,大家可以自行尋找。

TSP已經被證明是個NP Hard問題。目前有許多演算法能夠用來解決TSP,比如大家熟悉的動態規劃,以及諸如蟻群演算法的各種進化演算法。為了評價這些演算法的效能,德國Heidelberg University 的Gerhard Reinelt在上世紀90年代在其研究室的首頁上公開了一套TSPLIB(以TSP為主的問題庫),因其在之後被TSP方面的幾篇重要論文所採用,漸漸成為評價TSP演算法公認的BENCHMARK。

連結:http://www.iwr.uni-heidelberg.de/groups/comopt/software/TSPLIB95/

 

TSPLIB檔案格式

TSPLIB是一個包含了TSP及其相關問題的問題庫。其中的檔案都具有.tsp尾碼。

關於這些檔案的使用,有一篇專門的解說論文(https://docs.google.com/file/d/0B4zUGKjaO9uERU1RZDNuRkg3TW8/edit)。但是解說並不是特別詳細,另外很多同學也不知道這篇論文的存在,我在這裡還是稍微說明一下。

一個典型的TSPLIB檔案,如eil51.tsp,具有如下的格式:

NAME : eil51

COMMENT : 51-city problem (Christofides/Eilon)

TYPE : TSP

DIMENSION : 51

EDGE_WEIGHT_TYPE : EUC_2D

NODE_COORD_SECTION

1 37 52

2 49 49

3 52 64

4 20 26

5 40 30

50 56 37

51 30 40

EOF

 

NAME就是該檔案的名字。

COMMENT是對這個問題的附加說明。

TYPE描述了問題的類型,因為TSPLIB中還包含了一些其他類型的問題,但是這裡我們只關注TSP類型。

DIMENSION描述了城市的數量。

EDGE_WEIGHT_TYPE 描述了兩個城市間cost的類型,這裡是我們最為熟悉的2D歐幾裡得距離。

NODE_COORD_SECTION描述了各個城市的2D歐幾裡得座標。每一行按照城市編號,X座標,Y座標的順序。

但是需要注意的是,EDGE_WEIGHT_TYPE並不是只有EUC_2D一種,而是有13種之多。各種類型有對應的距離計算方法,如曼哈頓距離,地理距離等,這裡我就不一一列舉了,論文中有詳細的敘述。這裡我只單獨提一下出現最多的一種類型EXPLICIT,這種類型和其他的區別較大,城市間的距離是顯式給出的,無需再計算。以gr17.tsp為例:

NAME: gr17

TYPE: TSP

COMMENT: 17-city problem (Groetschel)

DIMENSION: 17

EDGE_WEIGHT_TYPE: EXPLICIT

EDGE_WEIGHT_FORMAT: LOWER_DIAG_ROW

EDGE_WEIGHT_SECTION

 0633 0 257 390 0 91 661 228 0 412 227

 169383 0 150 488 112 120 267 0 80 572 196

 77351 63 0 134 530 154 105 309 34 29 0

 259555 372 175 338 264 232 249 0 505 289 262

 476196 360 444 402 495 0 353 282 110 324 61

 208292 250 352 154 0 324 638 437 240 421 329

 297314 95 578 435 0 70 567 191 27 346 83

 4768 189 439 287 254 0 211 466 74 182 243

 105150 108 326 336 184 391 145 0 268 420 53

 239199 123 207 165 383 240 140 448 202 57 0

 246745 472 237 528 364 332 349 202 685 542 157

 289426 483 0 121 518 142 84 297 35 29 36

 236390 238 301 55 96 153 336 0

EOF

需要注意的是,如果EDGE_WEIGHT_TYPE類型為EXPLICIT,那麼就沒有NODE_COORD_SECTION項,而是對應的EDGE_WEIGHT_FORMAT與EDGE_WEIGHT_SECTION,EDGE_WEIGHT_FORMAT指明了資料以何種形式呈現,這裡的LOWER_DIAG_ROW代表著下三角矩陣。也就是說EDGE_WEIGHT_SECTION所列出的資料應當這麼看,

0

633 0

257 390 0

91 661 228 0

城市1到城市2的距離就是633,任何城市到自己本身的距離都為0。另外除了下三角矩陣還有全矩陣,上三角矩陣等。

另外,tsp檔案對空格的數量和最終的EOF並沒有嚴格的要求,因此每個檔案在格式上都可能有些微妙的區別,這就為解析器的實現提出了一些小小的挑戰。好在我們還有Regex可以比較輕鬆的解決這些問題。

 

解析器實現

無論是什麼演算法,真正需要的資訊只有城市規模的維度和各個城市間的距離,如何解析tsp檔案獲得這些資訊,正是我們關心的。下面我給出一個C#實現的簡易解析器,只為了說明問題,代碼本身還有很多可以最佳化重構的地方。

using System;using System.Collections.Generic;using System.Text;using System.Text.RegularExpressions;using System.IO;namespace TspFileParser{    class Program    {        public static class TspParser        {                        public static void ParseTspFile(string fileName, out int[,] distMatrix)            {                //string[] lines = {};                string typePattern = @"\nTYPE\s?:\s?(?<type>\w+)";                string dimPattern = @"DIMENSION\s?:\s?(?<dimension>\d+)";                string wtypePattern = @"EDGE_WEIGHT_TYPE\s?:\s?(?<wtype>\w+)";                string formatPattern = @"EDGE_WEIGHT_FORMAT\s?:\s?(?<ftype>\w+)";                //string pattern = @"(^TYPE\s*:\s*(?<type>\w+))|(DIMENSION\s*:\s*(?<dimension>\d+))|(EDGE_WEIGHT_TYPE\s*:\s*(?<wtype>\w+))";                //map dimension                int dim = 0;                string text = default(string);                                try                 {                   // lines = File.ReadAllLines(fileName);                    text = File.ReadAllText(fileName);                }                 catch(DirectoryNotFoundException /*DirctNot*/)                {                    Console.WriteLine("Directory is incorrect!");                }                catch (FileNotFoundException /*fileNot*/)                {                    Console.WriteLine("File not found!");                }                                Match problemType = Regex.Match(text, typePattern);                if (problemType.Groups["type"].Value != "TSP")                    throw new Exception("Not a tsp file!");//not handled                Match dimension  = Regex.Match(text, dimPattern);                Match weightType = Regex.Match(text, wtypePattern);                string[] textSplit = text.Split(new Char[] {' ', '\n'});                dim = Convert.ToInt32(dimension.Groups["dimension"].Value);                distMatrix = new int[dim, dim];                switch (weightType.Groups["wtype"].Value)                {                    case"EXPLICIT":                        {                            Match formatType = Regex.Match(text, formatPattern);                            switch(formatType.Groups["ftypes"].Value)                            {                                //symmetrical full matrix                                case"FULL_MATRIX":                                    {                                        int startPos = 0;                                        for (int i = 0; i < textSplit.Length; ++i)                                        {                                            if (textSplit[i] == "EDGE_WEIGHT_SECTION")                                            {                                                startPos = i;                                            }                                        }                                        for (int j = 0, n = 0; j < dim; ++j)                                        {                                            for (int i = 0; i < dim; ++i)                                            {                                                ++n;                                                distMatrix[i, j] = text[startPos + n];                                            }                                        }                                        break;                                    }                                    //lower digram matrix                                case"LOWER_DIAG_ROW":                                    {                                        int startPos = 0;                                        for (int i = 0; i < textSplit.Length; ++i)                                        {                                            if (textSplit[i] == "EDGE_WEIGHT_SECTION")                                            {                                                startPos = i;                                            }                                        }                                        for (int j = 0, n=0; j < dim; ++j)                                        {                                            for (int i = 0; i <=j; ++i)                                            {                                                ++n;                                                distMatrix[i, j] = text[startPos + n];                                                                                            }                                        }                                        break;                                    }                                   default: break;                            }                            break;                        }                    case "EUC_2D":                        {                                                                                    int startPos = 0;                            //array to store the cordinate of every city                            Tuple<int, int>[] cityCord = new Tuple<int, int>[dim];                            //find the start index of cordinate region                            for(int i=0; i<textSplit.Length; ++i)                            {                                if( textSplit[i] == "NODE_COORD_SECTION")                                {                                    startPos = i;                                }                            }                            //extract cordinates                            for (int n=0, i = startPos; n<dim; ++n )                            {                                //plus 2 to jump over the index before cordinate                                i = i + 2;                                int x = Convert.ToInt32(textSplit[i]);                                ++i;                                int y = Convert.ToInt32(textSplit[i]);                                cityCord[n] = new Tuple<int, int>(x, y);                            }                            //compute distance                            for (int i = 0, startj = 0; i < dim; ++i)                            {                                for (int j = startj; j < dim; ++j)                                {                                    distMatrix[i, j] = (int)Math.Sqrt((cityCord[i].Item1 - cityCord[j].Item1) * (cityCord[i].Item1 - cityCord[j].Item1)                                                                    + (cityCord[i].Item2 - cityCord[j].Item2) * (cityCord[i].Item2 - cityCord[j].Item2));                                    distMatrix[j, i] = distMatrix[i, j];                                }                                ++startj;                            }                            break;                        }                    default: break;                }                                }                             }        static void Main(string[] args)        {            //TspParser parser = new TspParser();            Console.WriteLine("Please input the path of Tsp file");            int[,] distance;            TspParser.ParseTspFile(Console.ReadLine(),out distance);            Console.ReadKey();        }    }}

這裡我們只有一個靜態類和一個靜態方法。靜態方法ParseTspFile有兩個參數,fileName是tsp檔案的路徑與名稱,out int[,] distMatrix則是用來儲存城市間距離的二維數組。先將整個檔案讀入為一個string,然後用4種pattern分別捕獲欄位TYPE, DIMENSION, EDGE_WEIGHT_TYPE, EDGE_WEIGHT_FORMAT之後的內容。Regex本身並不複雜,相信大家應該能看明白,需要注意的地方是空格和換行的匹配。捕獲到的TYPE類型用於判斷檔案是否是個tsp類型檔案,DIMENSION用於確定數組的大小,EDGE_WEIGHT_TYPE用於判斷距離類型,這裡我簡單的用了個switch語句。不同的類型需要不同的處理,這裡我只實現了EUC_2D與EXPLICIT類型,其餘的大家可以參考論文自行實現。如果是EXPLICIT類型,則還需要判斷EDGE_WEIGHT_FORMAT,這裡我實現了對稱全矩陣和下三角矩陣兩種情形,其餘的大家也可自行實現。Main函數用於測試。最終得到的distMatrix就可以為各種演算法所用了。比起常規的判斷,Regex強大的表達力使得我們關心的資訊的抽取變得十分容易。

TSPLIB簡介與簡易解析器實現

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.