1. 程式人生 > >旅行商問題(Traveling Salesman Problem,TSP)的+Leapms線性規劃模型及c++呼叫

旅行商問題(Traveling Salesman Problem,TSP)的+Leapms線性規劃模型及c++呼叫

知識點

旅行商問題的線性規劃模型
旅行商問題的+Leapms模型及CPLEX求解
C++呼叫+Leapms

旅行商問題

旅行商問題是一個重要的NP-難問題。一個旅行商人目前在城市1,他必須對其餘n-1個城市訪問且僅訪問一次而後回到城市1,請規
劃其最短的迴圈路線。

旅行商問題的建模

設城市i,j之間的距離為D[i][j],又設0-1變數x[i][j]表示從城市i到城市j的道路是否在迴圈路線上。於是旅行商問題的目標可以被寫成:

min sum{i=1,...,n;j=1,...,n;i<>j}(D[i][j] x[i][j])

因每個城市必須被訪問一次且僅被訪問一次,於是對每個城市需要進入一次且僅一次,而且出去一次且僅一次,於是有以下兩個約束:

sum{i=1,...,n;i<>j} x[i][j] = 1 | j=2,...,n
sum{j=1,...,n;i<>j} x[i][j] = 1 | i=2,...,n

僅採用以上約束時,結果會形成多個不聯通的迴圈。為防止這種情況,為每個城市規定一個訪問循序的編號u[i]變數。u[i]=k表示該城市是第k個被訪問的城市。規定u[0]=1,任意u[i]<=n-1。顯然如果x[i][j]=1,即道路 i,j 被選定在迴圈路徑中,則u[j]>=u[i]+1。用以下約束表達這個邏輯:

u[j]>=u[i]+1-n(1-x[i][j])|i=1,...,n;j=2,...,n;i<>j

上式中,如果x[i][j]=1, 則等價於u[j]>=u[i]+1。如果x[i][j]=0,則右端小於等於0,恆小於等於左端,相當於該約束不存在。

旅行商問題的Leapms模型

使用Cd表示城市的地理座標,則問題的Leapms模型為

//The Traveling Salesman Problem
min sum{i=1,...,n;j=1,...,n;i<>j} x[i][j] D[i][j]
subject to
	sum{i=1,...,n;i<>j} x[i][j] = 1 | j=2,...,n
	sum{j=1,...,n;i<>j} x[i][j] = 1 | i=2,...,n
	
	u[1]=0
	u[j]>=u[i]+1-n(1-x[i][j])|i=1,...,n;j=2,...,n;i<>j
	u[i]<=n-1|i=1,...,n
where
	n is an integer
	Cd is a set
	D[i][j] is a number|i=1,...,n;j=1,...,n
	x[i][j] is a variable of binary|i=1,...,n;j=1,...,n;i<>j
	u[i] is a variable of nonnegative number|i=1,...,n
data_relation
	n=_$(Cd)/2
	D[i][j]=sqrt((Cd[2i-1]-Cd[2j-1])^2+(Cd[2i]-Cd[2j])^2) -->
		|i=1,...,n;j=1,...,n
data
	Cd={
		0 0
		1062 182
		1028 503
		206 200
		473 291
		1741 233
	}//六個城市

使用mip或者cplex命令瞬間可以求解上述模型

Welcome to +Leapms ver 1.1(162260) Teaching Version  -- an LP/LMIP modeling and
solving tool.歡迎使用利珀 版本1.1(162260) Teaching Version  -- LP/LMIP 建模和求
解工具.

+Leapms>load
 Current directory is "ROOT".
 .........
        current.leap
        tsp.leap
        tsp_tamplet.leap
 .........
please input the filename:tsp
================================================================
1:  //The Traveling Salesman Problem
2:  min sum{i=1,...,n;j=1,...,n;i<>j} x[i][j] D[i][j]
3:  subject to
4:      sum{i=1,...,n;i<>j} x[i][j] = 1 | j=2,...,n
5:      sum{j=1,...,n;i<>j} x[i][j] = 1 | i=2,...,n
6:
7:      u[1]=0
8:      u[j]>=u[i]+1-n(1-x[i][j])|i=1,...,n;j=2,...,n;i<>j
9:      u[i]<=n-1|i=1,...,n
10:  where
11:     n is an integer
12:     Cd is a set
13:     D[i][j] is a number|i=1,...,n;j=1,...,n
14:     x[i][j] is a variable of binary|i=1,...,n;j=1,...,n;i<>j
15:     u[i] is a variable of nonnegative number|i=1,...,n
16:  data_relation
17:     n=_$(Cd)/2
18:     D[i][j]=sqrt((Cd[2i-1]-Cd[2j-1])^2+(Cd[2i]-Cd[2j])^2) -->
19:             |i=1,...,n;j=1,...,n
20:  data
21:     Cd={
22:             0 0
23:             1062 182
24:             1028 503
25:             206 200
26:             473 291
27:             1741 233
28:             1815 633
29:             1060 916
30:     }//八個城市
31:
================================================================
>>end of the file.
Parsing model:
1D
2R
3V
4O
5C
6S
7End.
..................................
number of variables=64
number of constraints=72
..................................
+Leapms>mip
relexed_solution=3006.07; number_of_nodes_branched=0; memindex=(2,2)
 nbnode=138;  memindex=(26,26) zstar=4880.76; GB->zi=4802.4
 nbnode=337;  memindex=(24,24) zstar=4328.8; GB->zi=4802.4
 nbnode=513;  memindex=(12,12) zstar=4112.39; GB->zi=4549.03
 nbnode=697;  memindex=(16,16) zstar=4541.65; GB->zi=4549.03
The Problem is solved to optimal as an MIP.
找到整數規劃的最優解.非零變數值和最優目標值如下:
  .........
    u2* =1
    u3* =5
    u4* =7
    u5* =6
    u6* =2
    u7* =3
    u8* =4
    x1_2* =1
    x2_6* =1
    x3_5* =1
    x4_1* =1
    x5_4* =1
    x6_7* =1
    x7_8* =1
    x8_3* =1
  .........
    Objective*=4549.03
  .........
+Leapms>

 C++呼叫+Leapms模型

直接的+leapms求解得到的是變數的結果資料,如果要進一步處理,則使用高階語言呼叫則更加方便。

+Leapms提供了c_leap類可以做此工作。主要的函式是:

c_leap::loadleap(char *leapfile) -- 調入名為leapfile的leapms模型

c_leap::mip() -- 求解當前的leapms模型(使用leapms自帶求解器,功能較弱)

c_leap::cplex() -- 求解當前的leapms模型(使用CPLEX求解器)

c_leap::getObj() -- 返回當前最優解的目標值

c_leap::getVar(char *varname) -- 返回變數名為varname的值

c_leap::getVar(char *varname,int nid, int id1,...) -- 返回變數名為varname,腳標個數為nid, 腳標分別為id1,...的變數的值。

把城市座標資料放在loc.txt中,下面的c++程式碼可以根據leapms模型模板(即去掉data段的旅行商問題leapms模型)生成當前模型、求解當前模型,並輸出autocad批處理圖形指令碼。

#include<iostream>
#include<fstream>
#include "leap.h"
using namespace std;

int m;
double x[3000],y[3000];

//例項化leap物件
c_leap lp;			

//讀原始資料生成當前模型
bool genModel(string fmodel,string fdata){
	ifstream iff;
	ofstream off;

	//複製模板到當前模型current.leap中
	iff.open(fmodel);
	off.open("current.leap");
	if(!iff||!off)return false;
	string line;
	while(getline(iff,line)){
		off<<line<<endl;
	}
	iff.close();

	//讀入資料檔案新增到當前模型的資料區
	iff.open(fdata);
	if(!iff)return false;
	

	off<<"data"<<endl<<"\tCd={"<<endl;
	int i=0;
	while(!iff.eof()){
		iff>>x[i]>>y[i];
		off<<"\t\t"<<x[i]<<" "<<y[i]<<endl;
		i++;
	}
	m=i;
	off<<"\t}"<<endl;

	iff.close();
	off.close();

	//結束模型生成過程
	return true;	
}


//輸出圖形
void draw(){

	ofstream ocad;
	ocad.open("tsp.scr");
	if(!ocad){
		cout<<"\t輸出圖形錯誤!"<<endl;
		return;
	}

	for(int i=0;i<m;i++){
		ocad<<"color 7"<<endl;
		ocad<<"point "<<x[i]<<","<<y[i]<<endl;
		for(int j=0;j<m;j++){
			if(i==j)continue;
			if(lp.getVar("x",2,i+1,j+1)>0){
				ocad<<"color 1"<<endl;
				ocad<<"line "<<x[i]<<","<<y[i]<<" "<<x[j]<<","<<y[j]<<" "<<endl;
			}
		}

	}
	ocad.close();	
}

int main(){

	//讀原始資料生成當前模型
	if(!genModel("tsp_tamplet.leap","loc.txt")){
		cout<<"\t錯誤!不能開啟檔案!"<<endl;
		return -1;
	}

	//調入模型
	lp.loadLeap("current.leap");	

	//使用cplex求解模型的整數解
	lp.cplex();	
		
	//輸出旅行商路徑圖形
	draw();

	//結束程式
	return 0;
}

對31個城市TSP問題的求解結果:

城市分佈

巡迴路線