Java調用CPLEX解決TSP問題(基於constraint generation模型)

constraint generation 約束生成方法(不知道我翻譯得對不對)在網上資料相對較少,中文的資料更少。

因爲傳統的DFJ模型解決TSP問題需要求解指數級別的子集,因此constraint generation顯得十分必要(就目前而言,120個節點的TSP也是分分鐘解決)。

下面是基於constraint generation的代碼。值得注意的是在運行constraint generation方法時,需要不斷去檢查得到的解是否是最優解,而且在添加約束時,不能每次只加一個環的節點(這樣會導致速度變慢很多很多)

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;

import ilog.concert.*;
import ilog.concert.IloCopyManager.Check;
import ilog.cplex.*;

public class Main {
	public static int _cityNum;
	public static double[][] _cityDis;

	public static ArrayList<Integer> _set;
	public static ArrayList<ArrayList<Integer>> _setPool;

	public static ArrayList<Integer> _setCG;// for constraint generation
	public static ArrayList<ArrayList<Integer>> _setCGPool;// for constraint
															// generation

	public static long _time;
	public static double _ansLength;
	public static double[][] _ansCity;

	public static void main(String[] args) {
	
		String filename = "E:\\JAVA\\TSP1\\data\\gr120.tsp";
		// 讀取標準化後的文件
		ReadData(filename);
		// 運行算法
		 CGsolve();// constraint generation
		// 輸出結果
		WriteAns(filename + ".ans");
		// 檢查是否滿足約束
		// checkOpt();
		System.out.println("finish");
	}


	// constraint generation 方法解決
	public static void CGsolve() {
		try {
			_ansCity = new double[_cityNum][_cityNum];

			IloCplex cplex = new IloCplex();
			IloIntVar[][] x = new IloIntVar[_cityNum][_cityNum];// 0-1變量

			// 設定變量取值範圍
			for (int i = 0; i < _cityNum; i++) {
				for (int j = 0; j < _cityNum; j++) {
					if (i != j)
						x[i][j] = cplex.intVar(0, 1);
					else
						x[i][j] = cplex.intVar(0, 0);
				}
			}
			// 目標函數
			IloLinearNumExpr tempObj = cplex.linearNumExpr();
			for (int i = 0; i < _cityNum; i++) {
				tempObj.add(cplex.scalProd(x[i], _cityDis[i]));
			}
			cplex.addMinimize(tempObj);

			// 添加約束 1 和 2
			for (int i = 0; i < _cityNum; i++) {
				IloLinearIntExpr constraint2 = cplex.linearIntExpr();
				for (int j = 0; j < _cityNum; j++) {
					constraint2.addTerm(x[j][i], 1);
				}
				cplex.addEq(cplex.sum(x[i]), 1);
				cplex.addEq(constraint2, 1);
			}
			// constraint generation 運行 判斷最優 添加約束 輸出結果
			int cnt = 0;
			do {
				long start = System.currentTimeMillis();
				boolean success = cplex.solve();
				long end = System.currentTimeMillis();
				_time = end - start;
				cnt++;
				System.out.println("正在執行第-------- " + cnt + " 次");
				if (success) {
					_ansLength = cplex.getObjValue();
					for (int i = 0; i < _cityNum; i++)
						_ansCity[i] = cplex.getValues(x[i]);
				} else {
					System.out.println("cplex.solve() failed.");
					break;
				}
				if (checkOpt())
					break;
				while (!_setCGPool.isEmpty()) {
					_setCG = _setCGPool.get(0);
					_setCGPool.remove(0);

					IloLinearNumExpr expression = cplex.linearNumExpr();
					for (int i = 0; i < _setCG.size(); i++)
						for (int j = i + 1; j < _setCG.size(); j++) {
							expression.addTerm(1, x[_setCG.get(i)][_setCG.get(j)]);
							expression.addTerm(1, x[_setCG.get(j)][_setCG.get(i)]);// imp
						}
					cplex.addLe(expression, _setCG.size() - 1);
				}
			} while (true);

		} catch (IloException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	// 檢查是否滿足條件 繞圈走一遍
	public static boolean checkOpt() {
		System.out.println("check\n--------------");
		_setCGPool = new ArrayList<ArrayList<Integer>>();
		boolean[] flag = new boolean[_cityNum];
		for (int i = 0; i < _cityNum; i++) {
			if (flag[i])
				continue;
			_setCG = new ArrayList<Integer>();
			int st = i;
			int cnt = 0;
			System.out.println("\n");
			while (true) {
				System.out.print(st + " ");
				_setCG.add(st);
				flag[st] = true;
				for (int j = 0; j < _cityNum; j++) {
					if ((int) (_ansCity[st][j] + 0.00001) == 1) {
						st = j;
						break;
					}
				}
				cnt++;
				if (st == i || cnt >= _cityNum)
					break;
			}
			_setCGPool.add(new ArrayList<Integer>(_setCG));
		}
		if (_setCGPool.size() == 1) {
			System.out.println("\nsuccess");
			return true;
		} else {
			System.out.println("\nfail");
			return false;
		}
	}

	// 輸出答案
	public static void WriteAns(String filename) {
		System.out.print(filename + "\n------------------------------\n");
		System.out.printf("rum time(sec)= %-10.2f\n", _time / 1000.0);
		System.out.printf("min= %-10.2f\n", _ansLength);
		for (int i = 0; i < _cityNum; i++) {
			for (int j = 0; j < _cityNum; j++)
				System.out.print((int) (_ansCity[i][j] + 0.00001) + " ");
			System.out.println();
		}
	}

	// 讀取標準化後的數據
	public static void ReadData(String filename) {
		File file = new File(filename);
		BufferedReader reader = null;
		try {
			System.out.println("正在讀取" + filename);
			reader = new BufferedReader(new FileReader(file));
			String tempString = null;
			tempString = reader.readLine();
			_cityNum = Integer.parseInt(tempString.trim());
			_cityDis = new double[_cityNum][_cityNum];
			for (int i = 0; i < _cityNum; i++) {
				tempString = reader.readLine();
				String[] arg = tempString.split(" ");
				for (int j = 0; j < arg.length; j++) {
					_cityDis[i][j] = Double.parseDouble(arg[j]);
					_cityDis[j][i] = _cityDis[i][j];
				}
			}

			reader.close();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (reader != null) {
				try {
					reader.close();
				} catch (IOException e1) {
				}
			}
		}
	}
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章