www.math.uwaterloo.ca/tsp/data/ml/tour/monalisa_5757191.tour的游览长度应该是 5757191 但是我的代码的结果是 5759929.877458311 < / strong>即可。假设愿意为任何改进支付1000美元的声明是正确的,那么下面的代码必须是错误的,但我认为没有任何缺陷。起初我认为这是一个数值精度问题,所以我开始使用任意精度算术,我得到了几乎相同的结果。
- double: 5759929.877458459
- BigDecimal(precision: 8 digits): 5759756.8
- BigDecimal(precision: 16 digits): 5759929.877458103
- BigDecimal(precision: 32 digits): 5759929.877458311
- BigDecimal(precision: 64,128,256,512 digits): no change
所有这些都是从规定的5757191中获得的。 我甚至在假设它是TSP路径而不是循环的情况下从开始到结束去除了循环边缘,但它仍然是高的。
数据文件清楚地说明了“EDGE_WEIGHT_TYPE:EUC_2D”所以它不是一个不同的距离度量...(即欧几里德度量(即sqrt(平方和)))
Here is the dataset of the points
采用此格式
nodeId integerCoordinate1 integerCoordinate2
以下是读取thoses文件并仅返回给定游览长度的代码:
import java.util.HashMap;
import java.util.ArrayList;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.io.File;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.PrintWriter;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.MathContext;
public class TSPTest {
//map from NodeId from the input mona-lisa100K.tsp file to it's 2d coordinates
HashMap<Integer,int[]> pointMap;
//the order of NodeId's visited in a tour (first in the order of `mona-lisa100K.tsp' then replaced with the NodeId's in the best known 'monalisa_5757191.tour' file)
ArrayList<Integer> initState = new ArrayList<Integer>();
File file;
File tour;
int dimension;//i.e. 2 in this case
public TSPTest(File file, File tour) throws FileNotFoundException,IOException {
this.file = file;
this.tour = tour;
readFromFile();
loadTour();
}
public static void main(String... args) throws Throwable {
TSPTest tester = new TSPTest(new File("mona-lisa100K.tsp"),new File("monalisa_5757191.tour"));
int[] state = tester.initState();//the same values in 'monalisa_5757191.tour'
System.out.println("state.length: "+state.length);
double distance = tester.evaluate(state);
System.out.println("TOUR DISTANCE="+distance);
return;
}
synchronized void loadTour() throws FileNotFoundException,IOException {
if (tour == null) return;
BufferedReader in = new BufferedReader(new FileReader(tour), 1000000);
String line;
Pattern pLine = Pattern.compile("^\\d+$");
initState.clear();
while ((line = in.readLine()) != null) {
Matcher matcher = pLine.matcher(line);
if (!matcher.find()) {continue;}
initState.add(Integer.valueOf(matcher.group()));
}
in.close();
return;
}
synchronized void readFromFile() throws FileNotFoundException,IOException {
pointMap = new HashMap<Integer, int[]>();
BufferedReader in = new BufferedReader(new FileReader(file), 1000000);
String line;
Pattern pLine = Pattern.compile("\\d+( \\d+(?:\\.\\d*)?|\\.\\d+)+");
Pattern p = Pattern.compile("\\d+(?:\\.\\d*)?|\\.\\d+");
while ((line = in.readLine()) != null) {
Matcher matcher = pLine.matcher(line);
if (!matcher.find()) {continue;}
matcher = p.matcher(line);
if (!matcher.find()) {continue;}
dimension = 0;
while (matcher.find()) {++dimension;}
Integer index;
int[] cords = new int[dimension];
matcher.reset();
matcher.find();
index = Integer.valueOf(matcher.group());
initState.add(index);
for (int i = 0; i < dimension; ++i) {
matcher.find();
cords[i] = Integer.parseInt(matcher.group());
if (Math.abs(Double.parseDouble(matcher.group())-cords[i])>0.00001) {
//I'm assuming all integer coordinates
throw new RuntimeException(line);
}
}
pointMap.put(index, cords);
}
in.close();
return;
}
public int[] initState() {
int[] state = new int[initState.size()];
for (int i = 0; i < state.length; ++i) {
state[i] = initState.get(i);
}
return state;
}
public double evaluate(int[] state) {
int[] p1;
int[] p2;
java.math.MathContext mc = new java.math.MathContext(512,java.math.RoundingMode.HALF_EVEN);
//double distance = 0.;
java.math.BigDecimal distance = BigDecimal.ZERO;
//get first point in TSP tour
p1=pointMap.get(state[0]);
for (int i = 1; i<state.length; ++i) {
p2=pointMap.get(state[i]);//get the next point
//distance += distance(p1,p2,null);
distance = distance.add(distance(p1,p2,mc), mc);
p1=p2;//next point is now the first point
}
//next two lines would make it a distance over a TSP cycle instead of a path
//p2=pointMap.get(state[0]);
//distance += distance(p1,p2);
//return distance;
return distance.doubleValue();
}
//commented out double version of code that is replaced with BigDecimal
//private double distance(int[] p1, int[] p2, MathContext mc2) {
private BigDecimal distance(int[] p1, int[] p2, MathContext mc2) {
/*//old double based code
double distance = 0;
for (int i = 0; i < p1.length; ++i) {
distance += Math.pow(p1[i]-p2[i],2);
}
distance = Math.sqrt(distance);
return distance;
*/
//I thought this might improve precision... (no difference)
//return Math.hypot(p1[0]-p2[0],p1[1]-p2[1]);
//+8 just to be safe :)
MathContext mc = new MathContext(mc2.getPrecision()+8, java.math.RoundingMode.HALF_EVEN);
//euclidean distance
return sqrt(new BigDecimal(p1[0],mc).subtract(new BigDecimal(p2[0],mc)).pow(2,mc).add(new BigDecimal(p1[1],mc).subtract(new BigDecimal(p2[1],mc)).pow(2,mc)), mc);
}
//Babylonian method (aka Hero's method) (Newton's method)
private java.math.BigDecimal sqrt(java.math.BigDecimal A, java.math.MathContext mc2) {
MathContext mc = new MathContext(mc2.getPrecision()+8, java.math.RoundingMode.HALF_EVEN);
BigDecimal TWO = new BigDecimal(2);
BigDecimal x0 = new BigDecimal("0");
BigDecimal x1 = new BigDecimal(Math.sqrt(A.doubleValue()));
BigDecimal xp = null;
while (!x0.equals(x1) && (xp==null || x1.compareTo(xp)!=0)) {//xp is used to detect a convergence loop (x1 swaps with x0 in an infinite loop)
xp = x0;
x0 = x1;
x1 = A.divide(x0,mc);
x1 = x1.add(x0,mc);
x1 = x1.divide(TWO,mc);
}
return x1;
}
}