Monalisa TSP巡回赛长度/距离总是错误的

时间:2017-09-08 08:38:01

标签: java euclidean-distance traveling-salesman arbitrary-precision

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;
  }
}

0 个答案:

没有答案