0

I'm trying to work on a variation of the Traveling Salesman Problem (TSP) and I've hit a speed bump that has left me scratching my head. I have to find the shortest journey to visit all top 1000 ranking universities in the world.

The starting and ending points are the same location. Also a person can only travel to a university that is within 100 ranks of it.

I'm pretty sure I have the code working for 1 random iteration of this problem but I can't think of a way to have my code run for every conceivable permutation of it.

If anyone could offer any insight it'd be much appreciated.

import java.io.BufferedReader;
import java.util.*;
import java.math.*;

public class Travelling {
    public static String[] contents;
    public static College maynooth;
    public static String[] colleges;
    public static double[] longitude=new double[1000];
    public static double[] latitude=new double[1000];
    public static int[] ranks=new int[1000];
    public static LinkedList unis=new LinkedList();
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        FileIO reader = new FileIO();
        contents = reader.load("universities.txt");
        boolean[] visited=new boolean[1000];
        double startingDistance=0.0;
        for(int i=0;i<visited.length;i++){
            visited[i]=false;
        }   
        for(int j=0;j<contents.length;j++){
            String[] temp=contents[j].split(" ");
            for(int i=0;i<temp.length;i++){
                ranks[j]=j+1;
                temp[i]=temp[i].replaceAll("\\s","");
                double longTemp=Double.parseDouble(temp[temp.length-1]);
                longitude[j]=longTemp;
                double latTemp=Double.parseDouble(temp[temp.length-2]);
                latitude[j]=latTemp;

            }

        }
        List<College> allColleges=new ArrayList<College>();
        for(int i=0;i<1000;i++){
            College tempCollege=new College();
            tempCollege.rank=ranks[i];
            tempCollege.lat=latitude[i];
            tempCollege.lon=longitude[i];
            tempCollege.currentIndex=i;
            allColleges.add(tempCollege);
        }
        maynooth=allColleges.get(607);
        distToUni(startingDistance, allColleges.get(607),allColleges);
        unis.display();
    }

    public static Double distanceBetweenTwoLocationsInKm(Double lat1, Double lon1, Double lat2, Double lon2) {
        if (lat1 == null || lat2 == null || lon1 == null || lon2 == null) {
            return null;
        }

        double earthRadius = 6371.0d;
        double diffBetweenLatitudeRadians = Math.toRadians(lat2 - lat1);
        double diffBetweenLongitudeRadians = Math.toRadians(lon2 - lon1);
        double latitudeOneInRadians = Math.toRadians(lat1);
        double latitudeTwoInRadians = Math.toRadians(lat2);
        double a = Math.sin(diffBetweenLatitudeRadians / 2) * Math.sin(diffBetweenLatitudeRadians / 2) + Math.cos(latitudeOneInRadians) * Math.cos(latitudeTwoInRadians) * Math.sin(diffBetweenLongitudeRadians / 2)
                * Math.sin(diffBetweenLongitudeRadians / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        return (earthRadius * c);
    }

    public static void distToUni(double distance,College firstCollege, List<College> currentColleges){   
        Random randGen=new Random();
        currentColleges.remove(firstCollege);
        for(int i=0;i<currentColleges.size();i++){
            currentColleges.get(i).currentIndex=i;
        }

        List<College> upperList=new ArrayList<College>();
        List<College> lowerList=new ArrayList<College>();

        int upperIndex=firstCollege.currentIndex+1;
        int lowerIndex=(currentColleges.get(firstCollege.currentIndex).currentIndex)-1;
        while((upperIndex<currentColleges.size()-1) && (currentColleges.get(upperIndex)!=null) && (((currentColleges.get(upperIndex).rank)-(currentColleges.get(firstCollege.currentIndex).rank))<100)){
            upperList.add(currentColleges.get(upperIndex));
            upperIndex++;
        }
        while((lowerIndex>=0)&&(currentColleges.get(firstCollege.currentIndex)!=null)&&((currentColleges.get(lowerIndex).rank)-(currentColleges.get(firstCollege.currentIndex).rank)>-100)){
            lowerList.add(currentColleges.get(lowerIndex));
            lowerIndex--;
        }

        List<College> combined=new ArrayList<College>();

        Collections.reverse(lowerList);
        combined.addAll(lowerList);
        combined.addAll(upperList);
        int randIndex = 0;
        if(combined.size()==1){
            College lastCollege = new College();
            lastCollege=combined.get(0);
            distance+=distanceBetweenTwoLocationsInKm(maynooth.lat, maynooth.lon, lastCollege.lat, lastCollege.lon);
        }
        if(combined.size() == 0) {
            unis.insertHead(distance);
            return; 
        }

        try {
            randIndex = randGen.nextInt(combined.size());
            double tempDist=distanceBetweenTwoLocationsInKm(firstCollege.lat, firstCollege.lon, combined.get(randIndex).lat, combined.get(randIndex).lon);
            distance=distance+tempDist;
        } catch (IllegalArgumentException ex) {}

        College currentCollege = new College();
        try {
            currentCollege = combined.get(randIndex);
        } catch(IndexOutOfBoundsException ex) {}
        distToUni(distance, currentCollege, currentColleges);

        return;
    }

}
Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197

1 Answers1

0

I can think of several, possible speedups.

a) In the plane world, we often have similar distance issues with distances and Pythagoras: a²+b²=c², therefore c = math.sqrt (aa+bb). But the costs for math.sqrt are pretty high. But to compare two distances, we don't need to know the real distance - if c1 < c2, then c1² < c2² and vice versa. So don't calculate the square root. Similar idea: The earth bending is nearly constant at every position and you're not using any abbreviation from it either, so the distance in km shouldn't be relevant to compare 2 distances. However,

b) The distance never changes. For 1000 Universities, there are 1000*1000 Distances, but since d(a,b) = d(b,a), it's just 500.000. Minus 1000, because d(a,a) is 0 and not interesting.

But you only need the distances for those in the rank range of 100, so for those with lowest ranking the 100 with better ranking, for the highest 100 with lower ranking, for the ones in the middle 200, 100 in each direction. As an upper bound it's 200*200/2=20.000 distances in a sparse Array, where every 0 would mean, that this university is not in rank range.

These need only to be computed once.

But 200*199*199*...*199*199*198*198*197*...3*3*2*2 (1000-200) cases -> ^| <- 200 cases

ist still a very, very large number to iterate (even if some of the about-200s are only 100s, because they're close to bottom or top of ranking).

Imho you should split up your code into smaller functions, to improve reasoning about the code on a higher abstraction level. Maybe then you don't need to comment it, because the names tell enough.

Else your code needs comments, how you solve the problem.

I only know about algorithms which find pretty good, but only by luck the best solution on a planar shape and without the rank restriction. You take 3 points (ABC) by random and combine them. Now you iteratively add one more point D by random and by searching the pair of points, (AB), (BC), (CA), where inserting the D has minimal costs (ADB), (BDC), (CDA). This has to be repeated until all points are added to the chain.

For the last element, you have already 999 vertices and have to check 999 to find the cheapest insertion, but it is restricted by the rank, so it is only about 200 chains to compare.

That's 200+200+200+....+199+198+198...+3+3+2+2+1+1 or below 200.000 comparisions to make, together with the table of distances, this should speed up the massively. You may then run this random selection some few thousand times, to get a very good result - but it's not guaranteed to be the absolute optimum.

user unknown
  • 35,537
  • 11
  • 75
  • 121