I made an attempt to convert the Ruby Code given in [http://www.cleveralgorithms.com/nature-inspired/physical/simulated_annealing.html][1] for solving the Travelling Salesman Problem using Simulated Annealing, and both codes ran without any bugs. However, I found that they both perform differently on the same problem instance (berlin52 from the TSPLIB). The Ruby code usually gets best solution between 9000 and 10000, while the Java code gets best solution between 16000 and 17000. Could there be any part of my Java code which wasn't well implemented? I have checked, but couldn't find any! Thanks in advance.
Ruby Code:
def euc_2d(c1, c2)
Math.sqrt((c1[0] - c2[0])**2.0 + (c1[1] - c2[1])**2.0).round
end
def cost(permutation, cities)
distance =0
permutation.each_with_index do |c1, i|
c2 = (i==permutation.size-1) ? permutation[0] : permutation[i+1]
distance += euc_2d(cities[c1], cities[c2])
end
return distance
end
def random_permutation(cities)
perm = Array.new(cities.size){|i| i}
perm.each_index do |i|
r = rand(perm.size-i) + i
perm[r], perm[i] = perm[i], perm[r]
end
return perm
end
def stochastic_two_opt!(perm)
c1, c2 = rand(perm.size), rand(perm.size)
exclude = [c1]
exclude << ((c1==0) ? perm.size-1 : c1-1)
exclude << ((c1==perm.size-1) ? 0 : c1+1)
c2 = rand(perm.size) while exclude.include?(c2)
c1, c2 = c2, c1 if c2 < c1
perm[c1...c2] = perm[c1...c2].reverse
return perm
end
def create_neighbor(current, cities)
candidate = {}
candidate[:vector] = Array.new(current[:vector])
stochastic_two_opt!(candidate[:vector])
candidate[:cost] = cost(candidate[:vector], cities)
return candidate
end
def should_accept?(candidate, current, temp)
return true if candidate[:cost] <= current[:cost]
return Math.exp((current[:cost] - candidate[:cost]) / temp) > rand()
end
def search(cities, max_iter, max_temp, temp_change)
current = {:vector=>random_permutation(cities)}
current[:cost] = cost(current[:vector], cities)
temp, best = max_temp, current
max_iter.times do |iter|
candidate = create_neighbor(current, cities)
temp = temp * temp_change
current = candidate if should_accept?(candidate, current, temp)
best = candidate if candidate[:cost] < best[:cost]
if (iter+1).modulo(10) == 0
puts " > iteration #{(iter+1)}, temp=#{temp}, best=#{best[:cost]}"
end
end
return best
end
if __FILE__ == $0
# problem configuration
berlin52 = [[565,575],[25,185],[345,750],[945,685],[845,655],
[880,660],[25,230],[525,1000],[580,1175],[650,1130],[1605,620],
[1220,580],[1465,200],[1530,5],[845,680],[725,370],[145,665],
[415,635],[510,875],[560,365],[300,465],[520,585],[480,415],
[835,625],[975,580],[1215,245],[1320,315],[1250,400],[660,180],
[410,250],[420,555],[575,665],[1150,1160],[700,580],[685,595],
[685,610],[770,610],[795,645],[720,635],[760,650],[475,960],
[95,260],[875,920],[700,500],[555,815],[830,485],[1170,65],
[830,610],[605,625],[595,360],[1340,725],[1740,245]]
# algorithm configuration
max_iterations = 2000
max_temp = 100000.0
temp_change = 0.98
# execute the algorithm
best = search(berlin52, max_iterations, max_temp, temp_change)
puts "Done. Best Solution: c=#{best[:cost]}, v=#{best[:vector].inspect}"
end
Java Code:
package localsearch;
import java.lang.reflect.Array;
import java.util.*;
public class SimulatedAnnealing {
public static Random rand = new Random();
public static double[][] berlin52 = {{565,575},{25,185},{345,750},{945,685},{845,655},
{880,660},{25,230},{525,1000},{580,1175},{650,1130},{1605,620},
{1220,580},{1465,200},{1530,5},{845,680},{725,370},{145,665},
{415,635},{510,875},{560,365},{300,465},{520,585},{480,415},
{835,625},{975,580},{1215,245},{1320,315},{1250,400},{660,180},
{410,250},{420,555},{575,665},{1150,1160},{700,580},{685,595},
{685,610},{770,610},{795,645},{720,635},{760,650},{475,960},
{95,260},{875,920},{700,500},{555,815},{830,485},{1170,65},
{830,610},{605,625},{595,360},{1340,725},{1740,245}};
public static void main (String[] args){
double[][] cities = berlin52;
// Algorithm configuration
int maxIterations = 2000;
double maxTemperature = 100000.0;
double tempChange = 0.98;
// Execute the algorithm
long startTime = System.currentTimeMillis();
Candidate best = search(berlin52, maxIterations, maxTemperature, tempChange);
long endTime = System.currentTimeMillis();
System.out.println("Done. Best Solution: c = " + best.cost + ", v = " + best.vector);
System.out.println("Time taken: " + (endTime - startTime) / 1000.0);
}
public static double euc2d(double[] c1, double[] c2){
return round(Math.sqrt(Math.pow(c1[0] - c2[0], 2.0) + Math.pow(c1[1] - c2[1], 2.0)), 0);
}
public static double cost(List<Integer> permutation, double[][] cities){
double distance = 0;
for (int i = 0; i < permutation.size(); i++) {
int c1 = i;
int c2 = (i == permutation.size()-1)? permutation.get(0) : permutation.get(i+1);
distance += euc2d(cities[c1], cities[c2]);
}
return round(distance, 4);
}
public static ArrayList<Integer> randomPermutation(double[][] cities){
int n = cities.length;
ArrayList<Integer> perm = new ArrayList<>();
for (int i = 0; i < n; i++)
perm.add(i);
for (int i = 0; i < n; i++) {
int r = (rand.nextInt(n) + i) % n;
Collections.swap(perm, i, r);
}
return perm;
}
public static List<Integer> stochasticTwoOpt(List<Integer> perm){
int c1 = rand.nextInt(perm.size());
while (c1 == 0)
c1 = rand.nextInt(perm.size());
int c2 = rand.nextInt(perm.size());
ArrayList<Integer> exclude = new ArrayList<>(Arrays.asList(c1, 0));
exclude.add((c1==0) ? perm.size()-1 : c1-1);
exclude.add((c1 == (perm.size()-1)) ? 0 : c1+1);
while (exclude.contains(c2))
c2 = rand.nextInt(perm.size());
if (c2 < c1){
int temp = c1;
c1 = c2;
c2 = temp;
}
return twoOpt(perm, c1, c2);
}
public static List<Integer> twoOpt(List<Integer> perm, int i, int j){
ArrayList<Integer> newPerm = new ArrayList<>(perm.subList(0, i));
ArrayList<Integer> reversedPortion = new ArrayList<>(perm.subList(i, j + 1));
Collections.reverse(reversedPortion);
newPerm.addAll(reversedPortion);
newPerm.addAll(perm.subList(j + 1, perm.size()));
return newPerm;
}
public static double round(double d, int numbersAfterDecimalPoint) {
double n = Math.pow(10, numbersAfterDecimalPoint);
double d2 = d * n;
long lon = (long) d2;
lon = ((long) (d2 + 0.5) > lon) ? lon + 1 : lon;
return (lon) / n;
}
public static class Candidate {
public double cost;
public List<Integer> vector;
public Candidate(List<Integer> vector, double cost){
this.vector = vector;
this.cost = cost;
}
public Candidate(){
vector = new ArrayList<>();
cost = 0.0;
}
public Candidate(Candidate candidate){
this.vector = new ArrayList<>(candidate.vector);
this.cost = candidate.cost;
}
}
public static Candidate createNeighbor(Candidate current, double[][] cities){
Candidate candidate = new Candidate();
candidate.vector = new ArrayList<>(current.vector);
candidate.vector = stochasticTwoOpt(candidate.vector);
candidate.cost = cost(candidate.vector, cities);
return candidate;
}
public static boolean shouldAccept (Candidate candidate, Candidate current, double temperature){
if(candidate.cost <= current.cost)
return true;
return Math.exp((current.cost - candidate.cost) / temperature) > rand.nextDouble();
}
public static Candidate search(double[][] cities, int maxIterations, double maxTemperature, double tempChange){
ArrayList<Integer> initPerm = randomPermutation(cities);
Candidate current = new Candidate(initPerm, cost(initPerm, cities));
double temp = maxTemperature;
Candidate best = new Candidate(current);
for (int iter = 0; iter < maxIterations; iter++) {
Candidate candidate = createNeighbor(current, cities);
temp *= tempChange;
if(shouldAccept(candidate, current, temp))
current = new Candidate(candidate);
if (candidate.cost < best.cost)
best = new Candidate(candidate);
if ((iter+1) % 10 == 0)
System.out.println("> iteration " + (iter+1) + ", temp = " + temp + ", best = " + best.cost);
}
return best;
}
}