Unfortunately, I am having a reference issue. The variable emptyRoutes does change as soon as I have called ELIMINATOR. However, in ELIMINATOR I have made a copy (new ArrayList(emptyRoutes) of the route, so changing the route in eliminator should not change emptyRoutes?
Does someone know what is going wrong?
MAIN CODE:
public Triple<List<ROUTE>, Integer, List<Customer>> initialization()
{
boolean initialization = true;
// Create the empty routes (also used in the for loop)
Customer vertex0 = u.get(0);
Pair<List<ROUTE>, Integer> results = createEmptyRoutes(vertex0, num_routes);
u.remove(0);
List<ROUTE> emptyRoutes = new ArrayList<ROUTE>(results.getFirst());
int profit = results.getSecond();
// Initialize the values of the variables
List<ROUTE> bestroutes = new ArrayList<ROUTE>(4);
List<Customer> best_u = new ArrayList<Customer>(u);
int tpBestSolution = 0;
int tdBestSolution = 1000000000;
int inU = u.size();
int num = (emptyRoutes.get(0).getLocations().size()-2) + (emptyRoutes.get(1).getLocations().size()-2) + (emptyRoutes.get(2).getLocations().size()-2)+ (emptyRoutes.get(3).getLocations().size()-2);
if (inU + num != 50)
{
System.out.println();
}
for (int i = 0; i < (3*N); i++)
{
Collections.shuffle(u, rnd);
ELIMINATOR B = new ELIMINATOR(u, emptyRoutes, profit, initialization, name, rnd);
inU = B.getU().size();
num = (B.getRoutes().get(0).getLocations().size()-2) + (B.getRoutes().get(1).getLocations().size()-2) + (B.getRoutes().get(2).getLocations().size()-2)+ (B.getRoutes().get(3).getLocations().size()-2);
if (inU + num != 50)
{
System.out.println(i);
}
POSTPROCEDURE PP = new POSTPROCEDURE(B.getRoutes(), profitRoutes(B.getRoutes()), B.getU(), name);
// Temporary solution
List<ROUTE> tempRoutes = PP.getBestSolution();
for (int p = 0; p < PP.getBestSolution().size(); p++)
{
ROUTE r = PP.getBestSolution().get(p);
addToPOOL(r);
}
boolean improvement = false;
if (PP.getTP() > tpBestSolution)
{
improvement = true;
}
else if (PP.getTP() == tpBestSolution)
{
if (PP.getTD() < tdBestSolution)
{
improvement = true;
}
}
if (improvement == true)
{
best_u = new ArrayList<Customer>(PP.getU());
bestroutes = new ArrayList<ROUTE>(tempRoutes);
tpBestSolution = new Integer(PP.getTP());
tdBestSolution = new Integer(PP.getTD());
}
}
return new Triple<List<ROUTE>, Integer, List<Customer>>(bestroutes, tpBestSolution, best_u);
}
ELIMINATOR:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
public class ELIMINATOR extends BASIS
{
private List<Customer> u;
private List<ROUTE> routes;
private int totalprofit;
private Random prob;
public ELIMINATOR(List<Customer> u, List<ROUTE> routes, int profit, boolean initialization, String name, Random rnd)
{
super(name);
this.u = new ArrayList<Customer>(u);
this.routes = new ArrayList<ROUTE>(routes);
this.totalprofit = new Integer(profit);
this.prob = rnd;
if (initialization == true)
{
addCustomers();
for (ROUTE route : routes)
{
checkCorrectNess(route, "after adding procedure in eliminator");
}
}
else
{
removeCustomers();
for (ROUTE route : routes)
{
checkCorrectNess(route, "after removing procedure in eliminator");
}
addCustomers();
for (ROUTE route : routes)
{
checkCorrectNess(route, "after removing and adding procedure in eliminator");
}
}
}
public void removeCustomers()
{
double Ph = 0.1;
double Pl = 0.3;
double total_profit = totalprofit;
int num_customers = 0;
// Calculate average profit
double average_profit = total_profit/num_customers;
// For each customer on each route, determine whether he/she will be removed
for(ROUTE r : routes)
{
List<RouteNode> route = r.getLocations();
int routesize = route.size();
int j = 1;
while (j < routesize-1)
{
boolean removed = false;
RouteNode node = route.get(j);
if (node.customer.getProfit() >= average_profit)
{
if (prob.nextDouble() < Ph)
{
removed = true;
RouteNode node_toberemoved = node;
int index_node = route.indexOf(node);
route.remove(index_node);
u.add(node.customer);
route = removal(route, node_toberemoved, index_node);
r.setLocations(route);
r.setDistance(distanceOneRoute(route));
r.setProfit(profitOneRoute(route));
checkCorrectNess(r, "remove customers eliminator");
}
}
else
{
if (prob.nextDouble() < Pl)
{
removed = true;
RouteNode node_toberemoved = node;
int index_node = route.indexOf(node);
route.remove(index_node);
u.add(node.customer);
route = removal(route, node_toberemoved, index_node);
r.setLocations(route);
r.setDistance(distanceOneRoute(route));
r.setProfit(profitOneRoute(route));
checkCorrectNess(r, "remove customers eliminator");
}
}
if (removed == false)
{
j++;
}
else
{
routesize = route.size();
}
}
}
totalprofit = profitRoutes(routes);
}
public void addCustomers()
{
List<Customer> u_copy = new ArrayList<Customer>(u);
List<Customer> u_temp = new ArrayList<Customer>(u);
for (Customer c : u_temp)
{
boolean added = false;
for (ROUTE r : routes)
{
checkCorrectNess(r, "add customers eliminator");
if (added == true)
{
break;
}
Customer customer = c;
u_copy.remove(c);
List<RouteNode> route = r.getLocations();
for (int i = 0; i < route.size()-1; i++)
{
RouteNode possibleNode = new RouteNode();
possibleNode.customer = customer;
List<Integer> distances = calculateDistances(route.get(i), possibleNode, route.get(i+1));
// Calculate shift for customer under consideration
int arrivalTime = route.get(i).timeStartService+ route.get(i).customer.getService() + distances.get(0);
int wait = Math.max(0, customer.getOpeningTW()-arrivalTime);
int serviceDuration = customer.getService();
int shift = distances.get(0) + wait + serviceDuration + distances.get(2) - distances.get(1);
// Determine Start Service
int startServiceTime = Math.max(customer.getOpeningTW(), arrivalTime);
// Obtain waiting time of next customer
int waiting_next = route.get(i+1).wait;
// Obtain MaxShift of next customer
int maxShift = route.get(i+1).maxShift;
if (shift <= (waiting_next + maxShift) & startServiceTime <= customer.getClosingTW() )
{
// Customer can be inserted
added = true;
RouteNode newNode = new RouteNode();
newNode.customer = customer;
newNode.arrivalTime = arrivalTime;
newNode.timeStartService = startServiceTime;
newNode.shift = shift;
newNode.wait = wait;
int pos_insertion = i + 1;
route = ADD(route, newNode, pos_insertion);
r.setLocations(route);
r.setDistance(distanceOneRoute(route));
r.setProfit(profitOneRoute(route));
checkCorrectNess(r, "add customers eliminator");
// exit the last for loop
break;
}
}
}
if (added == false)
{
u_copy.add(c);
}
}
u = new ArrayList<Customer>(u_copy);
totalprofit = profitRoutes(routes);
}
/**
* Returns list of unvisited customers
* #return
*/
public List<Customer> getU()
{
return u;
}
/**
* Returns list of routes
* #return
*/
public List<ROUTE> getRoutes()
{
return routes;
}
}
I think the problem is that copying a List<Route> object, doesn't copy the Route objects1 that the list contains.
Thus, you end up with two lists containing the same Route objects. The effects of changing a Route found in one list affects the same object in the other list.
1 - I refuse to follow your bad Java style decisions!
Related
can someone help me implement the maximum weight independent set for a TREE (not a graph)?
The tree is represented by an adjacency matrix, and we have an array for the weights of the vertices.
BFS output: // 0: distances from start vertex
// 1: BFS-order
// 2: parent-IDs
I tried this code, it doesn't work on all test cases and it says most of the time that the weight is too small.
Can someone help me find the errors?
import java.io.*;
import java.util.*;
public class Lab5
{
/**
* Problem: Find a maximum weight independent set using dynammic programming.
*/
private static int[] problem(Tree t, int[] weights)
{
// Implement me!
//base cases
if (t.noOfVertices==0) {
return new int[] {};
}
if (t.noOfVertices==1) {
return new int[] {weights[0]};
}
//we will implement this using bfs, we will use 0 as the root
int[][] bfs = t.bfs(0);
//finding leaves
int leaf[] = new int [t.noOfVertices];
//now we can implement our algorithm
//M is the maximum weight of the tree if it contains i, and M1 is the maximum weight of the tree if it doesn't contain i
int M[]=new int[t.noOfVertices];
int M1[]=new int[t.noOfVertices];
//treating elements that aren't leaves
int nodeDiscovered[] = new int[t.noOfVertices];
for (int i = 0; i<t.noOfVertices; i++) {
if (t.edges[i].length==1) {
leaf[i]=1;
M[i]=weights[i];
nodeDiscovered[i]=1;
M1[i]=0;
}
else {
leaf[i]=0;
nodeDiscovered[i]=0;
}
}
for (int i = 1; i<t.noOfVertices; i++) {
if (leaf[i]==1) {
int node = bfs[2][i];
if (nodeDiscovered[node]!=0) {
continue;
}
while (node>-1) {
int parent = bfs[2][node];
ArrayList<Integer> sibs = new ArrayList<Integer>();
if (parent!=-1) {
for (int j = 0; j<t.edges[parent].length; j++) {
if (t.edges[parent][j]!=bfs[2][parent]) {
sibs.add(t.edges[parent][j]);
}
}
}
else {
sibs.add(node);
}
for (int sib : sibs) {
if (nodeDiscovered[sib]!=0) {
continue;
}
M[sib]=weights[sib];
for (int k : t.edges[sib]) {
if(bfs[0][sib]==bfs[0][k]-1) {
M[sib]=M[sib]+M1[k];
M1[sib]+=(M[k]>M1[k])?M[k]:M1[k];
}
}
nodeDiscovered[sib]=1;
}
node = bfs[2][node];
}
}
}
//putting the answers in an arraylist
ArrayList<Integer> set = new ArrayList<Integer>();
if (M[0]>M1[0]) {
set.add(0);
}
for (int i = 1; i<t.noOfVertices; i++) {
if (!set.contains(bfs[2][i]) && M[i]>=M1[i] ) {
set.add(i);
}
}
System.out.println(set);
//putting the elements of the arraylist into an array of int
int[] set1 = new int[set.size()];
for (int i = 0; i<set.size(); i++) {
set1[i]=set.get(i);
}
return set1;
}
// ---------------------------------------------------------------------
// Do not change any of the code below!
// Do not change any of the code below!
/**
* Determines if a given set of vertices is an independent set for the given tree.
*/
private static boolean isIndSet(Tree t, int[] set)
{
if (set == null) return false;
boolean[] covered = new boolean[t.noOfVertices];
for (int i = 0; i < set.length; i++)
{
int vId = set[i];
int[] neighs = t.edges[vId];
if (covered[vId]) return false;
covered[vId] = true;
for (int j = 0; j < neighs.length; j++)
{
int nId = neighs[j];
covered[nId] = true;
}
}
return true;
}
private static final int LabNo = 5;
private static final String course = "CS 427";
private static final String quarter = "Fall 2021";
private static final Random rng = new Random(190817);
private static boolean testProblem(int[][] testCase)
{
int[] parents = testCase[0];
int[] weights = testCase[1];
Tree t = Tree.fromParents(parents);
int[] solution = maxIsWeight(t, weights);
int isWeight = solution[0];
int isSize = solution[1];
int[] answer = problem(t, weights.clone());
if (!isIndSet(t, answer))
{
System.out.println("Not an independent set.");
return false;
}
int ansWeight = 0;
for (int i = 0; i < answer.length; i++)
{
ansWeight += weights[answer[i]];
}
if (ansWeight < isWeight)
{
System.out.println("Weight too small.");
return false;
}
if (answer.length < isSize)
{
System.out.println("Set too small.");
return false;
}
return true;
}
private static int[] maxIsWeight(Tree t, int[] weigh)
{
int n = t.noOfVertices;
int[][] dfs = t.dfs(0);
int[] post = dfs[2];
int[] w = new int[n];
for (int i = 0; i < n; i++)
{
w[i] = weigh[i] * n + 1;
}
boolean[] isCandidate = new boolean[n];
for (int i = 0; i < n; i++)
{
int vId = post[i];
if (w[vId] <= 0) continue;
isCandidate[vId] = true;
int[] neighs = t.edges[vId];
for (int j = 0; j < neighs.length; j++)
{
int uId = neighs[j];
w[uId] = Math.max(w[uId] - w[vId], 0);
}
}
int isWeight = 0;
int isSize = 0;
for (int i = n - 1; i >= 0; i--)
{
int vId = post[i];
if (!isCandidate[vId]) continue;
isWeight += weigh[vId];
isSize++;
int[] neighs = t.edges[vId];
for (int j = 0; j < neighs.length; j++)
{
int uId = neighs[j];
isCandidate[uId] = false;
}
}
return new int[] { isWeight, isSize };
}
public static void main(String args[])
{
System.out.println(course + " -- " + quarter + " -- Lab " + LabNo);
int noOfTests = 300;
boolean passedAll = true;
System.out.println("-- -- -- -- --");
System.out.println(noOfTests + " random test cases.");
for (int i = 1; i <= noOfTests; i++)
{
boolean passed = false;
boolean exce = false;
try
{
int[][] testCase = createProblem(i);
passed = testProblem(testCase);
}
catch (Exception ex)
{
passed = false;
exce = true;
ex.printStackTrace();
}
if (!passed)
{
System.out.println("Test " + i + " failed!" + (exce ? " (Exception)" : ""));
passedAll = false;
//break;
}
}
if (passedAll)
{
System.out.println("All test passed.");
}
}
private static int[][] createProblem(int testNo)
{
int size = rng.nextInt(Math.min(testNo, 5000)) + 5;
// -- Generate tree. ---
int[] parents = new int[size];
parents[0] = -1;
for (int i = 1; i < parents.length; i++)
{
parents[i] = rng.nextInt(i);
}
// -- Generate weights. ---
int[] weights = new int[size];
for (int i = 0; i < weights.length; i++)
{
weights[i] = rng.nextInt(256);
}
return new int[][] { parents, weights };
}
}
I attached an image that contains the algorithm that I used.
I made a adjacency matrix for cities and connecting between them. And for example A-B, B-C, C-D. Now I am wonder if I can calculate distance between cities that aren't connected. Is it possible to calculate distance in matrix between non connected nodes and find path?
Cities class
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
public class GraphCities {
private List<String> cities;
private int[][] matrix;
Scanner s = new Scanner(System.in);
public GraphCities() {
this.cities = new LinkedList<String>();
}
public void addCity(String name) {
if (!cities.contains(name)) {
cities.add(name);
} else {
System.out.println("City " + name + " is already added.");
}
}
public void makeGraph() {
System.out
.println("Distance between cities, if they aren't connected insert -1");
matrix = new int[cities.size()][cities.size()];
for (int i = 0; i < matrix.length; i++) {
for (int j = i; j < matrix.length; j++) {
if (i == j) {
matrix[i][j] = 0;
} else {
System.out.println("Distance from "
+ cities.get(i) + " to " + cities.get(j));
int distance = s.nextInt();
matrix[i][j] = distance;
matrix[j][i] = distance;
}
}
}
}
public void show() {
String show = "\t";
for (int i = 0; i < cities.size(); i++) {
show += cities.get(i) + "\t";
}
show += "\n";
for (int i = 0; i < matrix.length; i++) {
show += cities.get(i) + "\t";
for (int j = 0; j < matrix.length; j++) {
if (matrix[i][j] != -1) {
show += matrix[i][j] + "\t";
} else {
show += "-\t";
}
}
show += "\n";
}
System.out.println(show);
}
}
Main method
public class Main {
public static void main(String[] args) {
GraphCities c = new GraphCities();
c.addCity("A");
c.addCity("B");
c.addCity("C");
c.addCity("D");
c.addCity("E");
c.makeGraph();
System.out.println();
c.show();
}
}
This is my output when i run main method and i think everything is ok.
A B C D E
A 0 50 - - -
B 50 0 30 - -
C - 30 0 40 -
D - - 40 0 20
E - - - 20 0
Now I want to calculate distance from A to D, but i haven't any idea how to do it. I will appreciate any help. Thanks!
To find the shortest path in a weighted graph (each part of the route has a different weight) you can use Dijkstra's Shortest Path Algorithm.
The following code is a one-file mcve. It can be copy-pasted into one file (Main.java) , and executed.
(This answer is base on the code posted before editing the question)
Please note the comments:
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class Main {
public static void main(String[] args) {
AdjList aList = new AdjList();
CityNode a = new CityNode("A");
CityNode b = new CityNode("B");
CityNode c = new CityNode("C");
CityNode d = new CityNode("D");
CityNode e = new CityNode("E");
aList.addCity(a);
aList.addCity(b);
aList.addCity(c);
aList.addCity(d);
aList.addCity(e);
aList.connectCities(a, b, 50);
aList.connectCities(b, c, 30);
aList.connectCities(c, d, 40);
aList.connectCities(d, e, 20);
aList.show();
FindPath findPath = new FindPath();
System.out.println(findPath.calculateShortestPath(aList, a, e)); //prints 140 as expected
//add some complexity
CityNode f = new CityNode("F");
aList.addCity(f);
aList.connectCities(b, f, 10);
aList.connectCities(f, d, 10);
System.out.println(findPath.calculateShortestPath(aList, a, e));//prints 90 as expected
}
}
class FindPath{
//map to hold distances of all node from origin. at the end this map should contain
//the shortest distance between origin (from) to all other nodes
Map<CityNode, Integer> distances;
//using Dijkstra algorithm
public int calculateShortestPath(AdjList aList, CityNode from, CityNode to) {
//a container to hold which cities the algorithm has visited
Set<CityNode> settledCities = new HashSet<>();
//a container to hold which cities the algorithm has to visit
Set<CityNode> unsettledCities = new HashSet<>();
unsettledCities.add(from);
//map to hold distances of all node from origin. at the end this map should contain
//the shortest distance between origin (from) to all other nodes
distances = new HashMap<>();
//initialize map with values: 0 distance to origin, infinite distance to all others
//infinite means no connection between nodes
for(CityNode city :aList.getCities()){
int distance = city.equals(from) ? 0 : Integer.MAX_VALUE;
distances.put(city, distance);
}
while (unsettledCities.size() != 0) {
//get the unvisited city with the lowest distance
CityNode currentCity = getLowestDistanceCity(unsettledCities);
//remove from unvisited, add to visited
unsettledCities.remove(currentCity); settledCities.add(currentCity);
Map<CityNode, Integer> connectedCities = currentCity.getConnectedCities();
for( CityNode city : connectedCities.keySet()){
//check if new distance is shorted than the previously found distance
if(distances.get(currentCity) + connectedCities.get(city) < distances.get(city)){
//if so, keep the shortest distance found
distances.put(city, distances.get(currentCity) + connectedCities.get(city));
//if city has not been visited yet, add it to unsettledCities
if(! settledCities.contains(city)) {
unsettledCities.add(city);
}
}
}
}
return distances.get(to);
}
private CityNode getLowestDistanceCity(Set <CityNode> unsettledCities) {
return unsettledCities.stream()
.min((c1,c2)-> Integer.compare(distances.get(c1), distances.get(c2)))
.orElse(null);
}
}
class CityNode {
private static int counter =0;
private final String name;
//assign unique id to each node. safer than to rely on unique name
private final int id = counter ++;
//map to hold all connected cities and distance to each
private final Map<CityNode, Integer> connectedCities;
public CityNode(String name) {
super();
this.name = name;
connectedCities = new HashMap<>();
}
public String getName() {
return name;
}
//not null safe. distance must not be negative
public void connect(CityNode connectTo, int distance) {
if(connectTo.equals(this)) throw new IllegalArgumentException("Node can not connect to istself");
connectedCities.put(connectTo, distance);
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder(name);
sb.append(connectedCities.keySet().isEmpty() ? " not connected" : " conntected to: " );
for ( CityNode city : connectedCities.keySet()) {
sb.append(city.getName()).append("-")
.append(connectedCities.get(city)).append("km ");
}
return sb.toString();
}
public int getId() {
return id;
}
public Map<CityNode, Integer> getConnectedCities(){
return connectedCities;
}
#Override
public boolean equals(Object o) {
if(o == null ||!(o instanceof CityNode)) return false;
CityNode c = (CityNode) o;
return c.getName().equalsIgnoreCase(name) && id == c.getId();
}
#Override
public int hashCode() {
int hash = 31 * 7 + id;
return name == null ? hash : name.hashCode();
}
}
class AdjList {
//use set which prevents duplicate entries
private final Set<CityNode> citiesList;
public AdjList() {
citiesList = new HashSet<>();
}
//adds city if is not already present. returns true if city was added
public boolean addCity(CityNode city) {
return citiesList.add(city);
}
//not null safe
public void connectCities(CityNode city1, CityNode city2, int distance) {
//assuming undirected graph
city1.connect(city2, distance);
city2.connect(city1, distance);
}
public CityNode getCityByName(String name) {
for (CityNode city : citiesList) {
if (city.getName().equalsIgnoreCase(name))
return city;
}
return null;
}
public void show() {
for (CityNode city : citiesList) {
System.out.println(city);
}
}
//get a copy of cities list
public Collection<CityNode> getCities(){
return new ArrayList<>(citiesList);
}
}
If you need a version of getLowestDistanceCity which does not use Stream use :
private CityNode getLowestDistanceCity(Set <CityNode> unsettledCities) {
CityNode lowestDistanceCity = null;
int lowestDistance = Integer.MAX_VALUE;
for (CityNode city: unsettledCities) {
int nodeDistance = distances.get(city);
if (nodeDistance < lowestDistance) {
lowestDistance = nodeDistance;
lowestDistanceCity = city;
}
}
return lowestDistanceCity;
}
Edit: an implementation using adjacency matrix could look like this:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class DijkstraAdjacencyMatrix {
public static void main(String[] args) {
Set<CityNode> cities = new HashSet<>();
CityNode a = new CityNode("A");
CityNode b = new CityNode("B");
CityNode c = new CityNode("C");
CityNode d = new CityNode("D");
CityNode e = new CityNode("E");
cities.addAll(List.of(a,b,c,d,e));
CitiesGraph graph = new CitiesGraph(cities);
graph.connectCities(a, b, 50);
graph.connectCities(b, c, 30);
graph.connectCities(c, d, 40);
graph.connectCities(d, e, 20);
graph.show();
FindPath findPath = new FindPath();
System.out.println(findPath.calculateShortestPath(graph, a, e)); //prints 140 as expected
//to add some complexity we need to construct a new CitiesGraph. It is not reusable
CityNode f = new CityNode("F");
cities.add(f);
graph = new CitiesGraph(cities);
graph.connectCities(a, b, 50);
graph.connectCities(b, c, 30);
graph.connectCities(c, d, 40);
graph.connectCities(d, e, 20);
graph.connectCities(b, f, 10);
graph.connectCities(f, d, 10);
graph.show();
System.out.println(findPath.calculateShortestPath(graph, a, e));//prints 90 as expected
}
}
class FindPath{
//map to hold distances of all node from origin. at the end this map should contain
//the shortest distance between origin (from) to all other nodes
Map<CityNode, Integer> distances;
//using Dijkstra algorithm
public int calculateShortestPath(CitiesGraph graph, CityNode from, CityNode to) {
//a container to hold which cities the algorithm has visited
Set<CityNode> settledCities = new HashSet<>();
//a container to hold which cities the algorithm has to visit
Set<CityNode> unsettledCities = new HashSet<>();
unsettledCities.add(from);
//map to hold distances of all node from origin. at the end this map should contain
//the shortest distance between origin (from) to all other nodes
distances = new HashMap<>();
//initialize map with values: 0 distance to origin, infinite distance to all others
//infinite means no connection between nodes
for(CityNode city :graph.getCities()){
int distance = city.equals(from) ? 0 : Integer.MAX_VALUE;
distances.put(city, distance);
}
while (unsettledCities.size() != 0) {
//get the unvisited city with the lowest distance
CityNode currentCity = getLowestDistanceCity(unsettledCities);
//remove from unvisited, add to visited
unsettledCities.remove(currentCity); settledCities.add(currentCity);
Collection<CityNode> connectedCities = graph.getCitiesConnectedTo(currentCity);
//iterate over connected city to update distance to each
for( CityNode city : connectedCities){
//check if new distance is shorted than the previously found distance
int distanceToCity = graph.getDistanceBetween(city, currentCity);
if(distanceToCity <= 0) {
continue;
}
if(distances.get(currentCity) + distanceToCity < distances.get(city)){
//if so, keep the shortest distance found
distances.put(city,distances.get(currentCity) + distanceToCity);
//if city has not been visited yet, add it to unsettledCities
if(! settledCities.contains(city)) {
unsettledCities.add(city);
}
}
}
}
return distances.get(to);
}
private CityNode getLowestDistanceCity(Set <CityNode> unsettledCities) {
return unsettledCities.stream()
.min((c1,c2)-> Integer.compare(distances.get(c1), distances.get(c2)))
.orElse(null);
}
}
class CityNode {
private static int counter =0;
private final String name;
//assign unique id to each node. safer than to rely on unique name
private final int id = counter ++;
public CityNode(String name) {
this.name = name;
}
public String getName() {
return name;
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder("City ");
sb.append(name).append(" (id=").append(id).append(")");
return sb.toString();
}
public int getId() {
return id;
}
#Override
public boolean equals(Object o) {
if(o == null || !(o instanceof CityNode)) return false;
CityNode c = (CityNode) o;
return c.getName().equalsIgnoreCase(name) && id == c.getId();
}
#Override
public int hashCode() {
int hash = 31 * 7 + id;
return name == null ? hash : name.hashCode();
}
}
class CitiesGraph{
//use set which prevents duplicate entries
private final Set<CityNode> cities;
private final int[][] adjacencyMatrix;
private static final int NOT_CONNECTED = -1;
public CitiesGraph(Set<CityNode> cities) {
this.cities = cities;
adjacencyMatrix = new int[cities.size()][cities.size()];
//initialize matrix
for(int row = 0; row < adjacencyMatrix.length ; row++){
for(int col = 0; col < adjacencyMatrix[row].length ; col++){
adjacencyMatrix[row][col] = row == col ? 0 : NOT_CONNECTED ;
}
}
}
public void connectCities(CityNode city1, CityNode city2, int distance) {
//assuming undirected graph
adjacencyMatrix[city1.getId()][city2.getId()] = distance;
adjacencyMatrix[city2.getId()][city1.getId()] = distance;
}
public int getDistanceBetween(CityNode city1, CityNode city2) {
return adjacencyMatrix[city1.getId()][city2.getId()];
}
public Collection<CityNode> getCitiesConnectedTo(CityNode city) {
Collection<CityNode> connectedCities = new ArrayList<>();
//iterate over row representing city's connections
int column = 0;
for(int distance : adjacencyMatrix[city.getId()]){
if(distance != NOT_CONNECTED && distance > 0) {
connectedCities.add(getCityById(column));
}
column++;
}
return connectedCities;
}
public CityNode getCityById(int id) {
for (CityNode city : cities) {
if (city.getId() == id) return city;
}
return null;
}
public void show() {
for(int[] row : adjacencyMatrix){
System.out.println(Arrays.toString(row));
}
}
//get a copy of cities list
public Collection<CityNode> getCities(){
return new ArrayList<>(cities);
}
}
I have written a simple genetic algorithm program in java. What it is does is maximize the decimal value represented by the bits in the chromosome. Somehow mutation is not working as expected, e.g. causing two genes to mutate when just one is to change. The print statements I have included there show which to mutate, but in addition to that some more chromosomes get mutated. I can't figure out what the problem is :-(
Here are my java classes.
Gene.java
public class Gene {
private int value;
public Gene() {
value = Math.random() < 0.5 ? 0 : 1;
}
public Gene(int value) {
if (value != 0 && value != 1) {
throw new IllegalArgumentException("value must be either 0 or 1");
}
else {
this.value = value;
}
}
public void mutate() {
value = 1 - value;
}
public int value() {
return value;
}
#Override
public String toString() {
return String.valueOf(value);
}
}
Chromosome.java
import java.util.ArrayList;
import java.util.List;
public class Chromosome implements Comparable {
private ArrayList<Gene> genes;
private final int chromosomeLength;
public Chromosome(int length) {
this.genes = new ArrayList<>();
this.chromosomeLength = length > 0 ? length : 16;
for (int i = 0; i < chromosomeLength; i++) {
this.genes.add(i, new Gene());
}
}
public List<Gene> getAllele(int fromIndex, int toIndex) {
return new ArrayList<>(genes.subList(fromIndex, toIndex));
}
public void setAllele(int fromIndex, List<Gene> allele) {
int lastIndex = fromIndex + allele.size();
if (lastIndex > chromosomeLength) {
throw new IndexOutOfBoundsException("the allele exceeds beyond the size of the chromosome");
}
for (int i = fromIndex, j = 0; i < lastIndex; i++, j++) {
genes.set(i, allele.get(j));
}
}
public int getChromosomeLength() {
return chromosomeLength;
}
public void setGeneAt(int index, Gene gene) {
genes.set(index, gene);
}
public Gene getGeneAt(int index) {
return genes.get(index);
}
public int value() {
return Integer.parseInt(this.toString(), 2);
}
#Override
public String toString() {
StringBuilder chromosome = new StringBuilder("");
genes.stream().forEach((Gene g) -> chromosome.append(g));
return chromosome.toString();
}
#Override
public int compareTo(Object anotherChromosome) {
Chromosome c = (Chromosome) anotherChromosome;
return this.value() - c.value();
}
}
GenePool.java
import java.util.ArrayList;
import java.util.Arrays;
public class GenePool {
private final ArrayList<Chromosome> genePool;
private final int genePoolSize;
private final int chromosomeLength;
private final double crossOverRate;
private final double mutationRate;
private int[] crossPoints;
public GenePool(int numOfChromosome, int chromosomeLength, double crossOverRate, double mutationRate) {
this.genePoolSize = numOfChromosome;
this.chromosomeLength = chromosomeLength > 0 ? chromosomeLength : 16;
this.crossOverRate = crossOverRate;
this.mutationRate = mutationRate;
crossPoints = new int[1];
crossPoints[0] = this.chromosomeLength / 2;
genePool = new ArrayList<>();
for (int i = 0; i < numOfChromosome; i++) {
genePool.add(new Chromosome(chromosomeLength));
}
}
public int getGenePoolSize() {
return genePoolSize;
}
public Chromosome getChromosomeAt(int index) {
return genePool.get(index);
}
public void setChromosomeAt(int index, Chromosome c) {
genePool.set(index, c);
}
public int getChromosomeLength() {
return chromosomeLength;
}
public Chromosome[] crossOver(Chromosome c1, Chromosome c2) {
Chromosome[] offsprings = new Chromosome[2];
offsprings[0] = new Chromosome(c1.getChromosomeLength());
offsprings[1] = new Chromosome(c1.getChromosomeLength());
Chromosome[] parentChromosomes = {c1, c2};
int selector = 0;
for (int i = 0, start = 0; i <= crossPoints.length; i++) {
int crossPoint = i == crossPoints.length ? c1.getChromosomeLength() : crossPoints[i];
offsprings[0].setAllele(start, parentChromosomes[selector].getAllele(start, crossPoint));
offsprings[1].setAllele(start, parentChromosomes[1 - selector].getAllele(start, crossPoint));
selector = 1 - selector;
start = crossPoint;
}
return offsprings;
}
public void mutateGenePool() {
int totalGeneCount = genePoolSize * chromosomeLength;
System.out.println("Mutating genes:");
for (int i = 0; i < totalGeneCount; i++) {
double prob = Math.random();
if (prob < mutationRate) {
System.out.printf("Chromosome#: %d\tGene#: %d\n", i / chromosomeLength, i % chromosomeLength);
genePool.get(i / chromosomeLength).getGeneAt(i % chromosomeLength).mutate();
}
}
System.out.println("");
}
public int getLeastFitIndex() {
int index = 0;
int min = genePool.get(index).value();
int currentValue;
for (int i = 1; i < genePoolSize; i++) {
currentValue = genePool.get(i).value();
if (currentValue < min) {
index = i;
min = currentValue;
}
}
return index;
}
public void saveFittest(ArrayList<Chromosome> offsprings) {
// sort in ascending order
offsprings.sort(null);
offsprings.stream().forEach((offspring) -> {
int leastFitIndex = getLeastFitIndex();
if (offspring.value() > genePool.get(leastFitIndex).value()) {
genePool.set(leastFitIndex, offspring);
}
});
}
public void evolve(int noOfGeneration) {
for (int generation = 1; generation <= noOfGeneration; generation++) {
System.out.println("Generation :" + generation);
ArrayList<Integer> selection = new ArrayList<>();
for (int i = 0; i < genePoolSize; i++) {
if (Math.random() <= crossOverRate) {
selection.add(i);
}
}
if (selection.size() % 2 == 1) {
selection.remove(selection.size() - 1);
}
ArrayList<Chromosome> offsprings = new ArrayList<>();
for (int i = 0; i < selection.size(); i += 2) {
int index1 = selection.get(i);
int index2 = selection.get(i + 1);
offsprings.addAll(Arrays.asList(crossOver(genePool.get(index1), genePool.get(index2))));
}
System.out.println("Before saving the offsprings");
displayChromosomes(genePool, "GenePool");
displayChromosomes(offsprings, "Offsprings");
saveFittest(offsprings);
System.out.println("Before mutation:");
displayChromosomes(genePool, "GenePool");
mutateGenePool();
System.out.println("After mutation:");
displayChromosomes(genePool, "GenePool");
System.out.println("\n\n");
}
}
public void displayChromosomes(ArrayList<Chromosome> geneList, String name) {
System.out.println(name);
if (geneList.isEmpty()) {
System.out.println("Empty list");
}
geneList.stream().forEach((c) -> {
System.out.println(c + " -> " + c.value());
});
System.out.println("");
}
}
GADemo.java
public class GADemo {
public static void main(String[] args) {
GenePool gp = new GenePool(6, 8, 0.25, 0.01);
gp.evolve(10);
}
}
After evolving for a number of generations, the chromosomes all tend to become exactly the same, or very similar. And the problem is that that value is not the maximum for that many bits, and sometimes even a small value. For example, for 8 bits the values should (tend to) approach 255, but this doesn't do so in my code. Someone please provide a hint where/how to look for and solve the problem.
Focus on these lines and imagine the references. These are from setAllele()
for (int i = fromIndex, j = 0; i < lastIndex; i++, j++) {
genes.set(i, allele.get(j));
}
You are basically copying the reference from one onto the other. They are the same Gene so whatever mutation you do on those genes, will also affect even other Chromosomes.
You must produce a deep copy here.
Initially each chromosome has an own list of genes. But when you do the crossover operation you set gene objects from one chromosome into the gene list of other chromosome.
When you evolve the system, the number of shared genes will rise and therefore ultimately all chromosomes will share the same genes. No matter how you mutate a gene the chromosomes are not affected.
EDIT:
As Incognito also answered the setAllele method seems to be the culprit where gene sharing starts. You may want to introduce a method in the gene class where you can set its value given another gene.
Does Apache Mahout provide a way to perform n-fold cross-validation, instead of the random hold-out test? If not, what other Java framework do you suggest (with available code samples / good documentation, and that you have personally used if possible)?
My current code (uses random hold-out):
RecommenderEvaluator evaluator = new AverageAbsoluteDifferenceRecommenderEvaluator();
double result = evaluator.evaluate(builder, null, model, 0.9, 1.0);
System.out.println("Evaluation : " + result);
Here is a custom implementation that I did extending the AbstractDifferenceRecommenderEvaluator from Mahout. I just copy and paste the code. Please check if it satisfy your needs. I think I have enough comments in the class.
public abstract class AbstractKFoldRecommenderEvaluator extends AbstractDifferenceRecommenderEvaluator {
private final Random random;
public double noEstimateCounterAverage = 0.0;
public double totalEstimateCount = 0.0;
public double totalEstimateCountAverage = 0.0;
private static final Logger log = LoggerFactory
.getLogger(AbstractKFoldRecommenderEvaluator.class);
public AbstractKFoldRecommenderEvaluator() {
super();
random = RandomUtils.getRandom();
}
public double getNoEstimateCounterAverage(){
return noEstimateCounterAverage;
}
public double getTotalEstimateCount(){
return totalEstimateCount;
}
public double getTotalEstimateCountAverage(){
return totalEstimateCountAverage;
}
/**
* We use the same evaluate function from the RecommenderEvaluator interface
* the trainingPercentage is used as the number of folds, so it can have
* values bigger than 0 to the number of folds.
*/
#Override
public double evaluate(RecommenderBuilder recommenderBuilder,
DataModelBuilder dataModelBuilder, DataModel dataModel,
double trainingPercentage, double evaluationPercentage)
throws TasteException {
Preconditions.checkNotNull(recommenderBuilder);
Preconditions.checkNotNull(dataModel);
Preconditions.checkArgument(trainingPercentage >= 0.0,
"Invalid trainingPercentage: " + trainingPercentage);
Preconditions.checkArgument(evaluationPercentage >= 0.0
&& evaluationPercentage <= 1.0,
"Invalid evaluationPercentage: " + evaluationPercentage);
log.info("Beginning evaluation using {} of {}", trainingPercentage,
dataModel);
int numUsers = dataModel.getNumUsers();
// Get the number of folds
int noFolds = (int) trainingPercentage;
// Initialize buckets for the number of folds
List<FastByIDMap<PreferenceArray>> folds = new ArrayList<FastByIDMap<PreferenceArray>>();
for (int i = 0; i < noFolds; i++) {
folds.add(new FastByIDMap<PreferenceArray>(
1 + (int) (i / noFolds * numUsers)));
}
// Split the dataModel into K folds per user
LongPrimitiveIterator it = dataModel.getUserIDs();
while (it.hasNext()) {
long userID = it.nextLong();
if (random.nextDouble() < evaluationPercentage) {
splitOneUsersPrefs2(noFolds, folds, userID, dataModel);
}
}
double result = Double.NaN;
List<Double> intermediateResults = new ArrayList<>();
List<Integer> unableToRecoomend = new ArrayList<>();
List<Integer> averageEstimateCounterIntermediate = new ArrayList<>();
noEstimateCounterAverage = 0.0;
totalEstimateCount = 0.0;
totalEstimateCountAverage = 0.0;
int totalEstimateCounter = 0;
// Rotate the folds. Each time only one is used for testing and the rest
// k-1 folds are used for training
for (int k = 0; k < noFolds; k++) {
FastByIDMap<PreferenceArray> trainingPrefs = new FastByIDMap<PreferenceArray>(
1 + (int) (evaluationPercentage * numUsers));
FastByIDMap<PreferenceArray> testPrefs = new FastByIDMap<PreferenceArray>(
1 + (int) (evaluationPercentage * numUsers));
for (int i = 0; i < folds.size(); i++) {
// The testing fold
testPrefs = folds.get(k);
// Build the training set from the remaining folds
if (i != k) {
for (Map.Entry<Long, PreferenceArray> entry : folds.get(i)
.entrySet()) {
if (!trainingPrefs.containsKey(entry.getKey())) {
trainingPrefs.put(entry.getKey(), entry.getValue());
} else {
List<Preference> userPreferences = new ArrayList<Preference>();
PreferenceArray existingPrefs = trainingPrefs
.get(entry.getKey());
for (int j = 0; j < existingPrefs.length(); j++) {
userPreferences.add(existingPrefs.get(j));
}
PreferenceArray newPrefs = entry.getValue();
for (int j = 0; j < newPrefs.length(); j++) {
userPreferences.add(newPrefs.get(j));
}
trainingPrefs.remove(entry.getKey());
trainingPrefs.put(entry.getKey(),
new GenericUserPreferenceArray(
userPreferences));
}
}
}
}
DataModel trainingModel = dataModelBuilder == null ? new GenericDataModel(
trainingPrefs) : dataModelBuilder
.buildDataModel(trainingPrefs);
Recommender recommender = recommenderBuilder
.buildRecommender(trainingModel);
Double[] retVal = getEvaluation(testPrefs, recommender);
double intermediate = retVal[0];
int noEstimateCounter = ((Double)retVal[1]).intValue();
totalEstimateCounter += ((Double)retVal[2]).intValue();
averageEstimateCounterIntermediate.add(((Double)retVal[2]).intValue());
log.info("Evaluation result from fold {} : {}", k, intermediate);
log.info("Average Unable to recommend for fold {} in: {} cases out of {}", k, noEstimateCounter, ((Double)retVal[2]).intValue());
intermediateResults.add(intermediate);
unableToRecoomend.add(noEstimateCounter);
}
double sum = 0;
double noEstimateSum = 0;
double totalEstimateSum = 0;
// Sum the results in each fold
for (int i = 0; i < intermediateResults.size(); i++) {
if (!Double.isNaN(intermediateResults.get(i))) {
sum += intermediateResults.get(i);
noEstimateSum+=unableToRecoomend.get(i);
totalEstimateSum+=averageEstimateCounterIntermediate.get(i);
}
}
if (sum > 0) {
// Get an average for the folds
result = sum / intermediateResults.size();
}
double noEstimateCount = 0;
if(noEstimateSum>0){
noEstimateCount = noEstimateSum / unableToRecoomend.size();
}
double avgEstimateCount = 0;
if(totalEstimateSum>0){
avgEstimateCount = totalEstimateSum / averageEstimateCounterIntermediate.size();
}
log.info("Average Evaluation result: {} ", result);
log.info("Average Unable to recommend in: {} cases out of avg. {} cases or total {} ", noEstimateCount, avgEstimateCount, totalEstimateCounter);
noEstimateCounterAverage = noEstimateCount;
totalEstimateCount = totalEstimateCounter;
totalEstimateCountAverage = avgEstimateCount;
return result;
}
/**
* Split the preference values for one user into K folds, randomly
* Generate random number until is not the same as the previously generated on
* in order to make sure that at least two buckets are populated.
*
* #param k
* #param folds
* #param userID
* #param dataModel
* #throws TasteException
*/
private void splitOneUsersPrefs(int k,
List<FastByIDMap<PreferenceArray>> folds, long userID,
DataModel dataModel) throws TasteException {
List<List<Preference>> oneUserPrefs = Lists
.newArrayListWithCapacity(k + 1);
for (int i = 0; i < k; i++) {
oneUserPrefs.add(null);
}
PreferenceArray prefs = dataModel.getPreferencesFromUser(userID);
int size = prefs.length();
int previousBucket = -1;
Double rand = -2.0;
for (int i = 0; i < size; i++) {
Preference newPref = new GenericPreference(userID,
prefs.getItemID(i), prefs.getValue(i));
do {
rand = random.nextDouble() * k * 10;
rand = (double) Math.floor(rand / 10);
// System.out.println("inside Rand "+rand);
} while (rand.intValue() == previousBucket);
// System.out.println("outside rand "+rand);
if (oneUserPrefs.get(rand.intValue()) == null) {
oneUserPrefs.set(rand.intValue(), new ArrayList<Preference>());
}
oneUserPrefs.get(rand.intValue()).add(newPref);
previousBucket = rand.intValue();
}
for (int i = 0; i < k; i++) {
if (oneUserPrefs.get(i) != null) {
folds.get(i).put(userID,
new GenericUserPreferenceArray(oneUserPrefs.get(i)));
}
}
}
/**
* Split the preference values for one user into K folds, by shuffling.
* First Shuffle the Preference array for the user. Then distribute the item-preference pairs
* starting from the first buckets to the k-th bucket, and then start from the beggining.
*
* #param k
* #param folds
* #param userID
* #param dataModel
* #throws TasteException
*/
private void splitOneUsersPrefs2(int k, List<FastByIDMap<PreferenceArray>> folds, long userID, DataModel dataModel) throws TasteException {
List<List<Preference>> oneUserPrefs = Lists.newArrayListWithCapacity(k + 1);
for (int i = 0; i < k; i++) {
oneUserPrefs.add(null);
}
PreferenceArray prefs = dataModel.getPreferencesFromUser(userID);
int size = prefs.length();
List<Preference> userPrefs = new ArrayList<>();
Iterator<Preference> it = prefs.iterator();
while (it.hasNext()) {
userPrefs.add(it.next());
}
// Shuffle the items
Collections.shuffle(userPrefs);
int currentBucket = 0;
for (int i = 0; i < size; i++) {
if (currentBucket == k) {
currentBucket = 0;
}
Preference newPref = new GenericPreference(userID, userPrefs.get(i).getItemID(), userPrefs.get(i).getValue());
if (oneUserPrefs.get(currentBucket) == null) {
oneUserPrefs.set(currentBucket, new ArrayList<Preference>());
}
oneUserPrefs.get(currentBucket).add(newPref);
currentBucket++;
}
for (int i = 0; i < k; i++) {
if (oneUserPrefs.get(i) != null) {
folds.get(i).put(userID, new GenericUserPreferenceArray(oneUserPrefs.get(i)));
}
}
}
private Double[] getEvaluation(FastByIDMap<PreferenceArray> testPrefs, Recommender recommender) throws TasteException {
reset();
Collection<Callable<Void>> estimateCallables = Lists.newArrayList();
AtomicInteger noEstimateCounter = new AtomicInteger();
AtomicInteger totalEstimateCounter = new AtomicInteger();
for (Map.Entry<Long, PreferenceArray> entry : testPrefs.entrySet()) {
estimateCallables.add(new PreferenceEstimateCallable(recommender, entry.getKey(), entry.getValue(), noEstimateCounter, totalEstimateCounter));
}
log.info("Beginning evaluation of {} users", estimateCallables.size());
RunningAverageAndStdDev timing = new FullRunningAverageAndStdDev();
execute(estimateCallables, noEstimateCounter, timing);
Double[] retVal = new Double[3];
retVal[0] = computeFinalEvaluation();
retVal[1] = (double) noEstimateCounter.get();
retVal[2] = (double) totalEstimateCounter.get();
//retVal.put(computeFinalEvaluation(), noEstimateCounter.get());
//return computeFinalEvaluation();
return retVal;
}}
And here is actual implementation class:
public class RMSRecommenderEvaluatorModified extends AbstractKFoldRecommenderEvaluator {
private RunningAverage average;
#Override
protected void reset() {
average = new FullRunningAverage();
}
#Override
protected void processOneEstimate(float estimatedPreference, Preference realPref) {
double diff = realPref.getValue() - estimatedPreference;
average.addDatum(diff * diff);
}
#Override
protected double computeFinalEvaluation() {
return Math.sqrt(average.getAverage());
}
#Override
public String toString() {
return "RMSRecommenderEvaluator";
}}
I'm trying to implement the Clarke Wright algorithm. The problem I seem to be having is that it merges lots of routes regardless of the tests I've put into place. I've been staring at this for a long time, so I thought it may help to cast a fresh pair of eyes over it.
Instead of creating routes that are valid (ie, the van has enough space to carry everything), it wildly creates routes everywhere regardless of whether it's got the capacity or not.
public void CWVRSolution(){
List<SavingsNode> savings = new ArrayList<SavingsNode>();
// ArrayList<List<Customer>> routes = new ArrayList<List<Customer>>();
this.soln = new ArrayList<List<Customer>>();
//ArrayList<List<Customer>> routelist = new ArrayList<List<Customer>>();
for (int i=0; i < prob.customers.size(); i++){
for (int j=i+1; j<prob.customers.size();j++){
if (i != j){
double savingVal = (prob.depot.distance(prob.customers.get(i)) + prob.depot.distance(prob.customers.get(j))) - prob.customers.get(i).distance(prob.customers.get(j));
savings.add(new SavingsNode(prob.customers.get(i),prob.customers.get(j),savingVal));
}
}
}
Collections.sort(savings);
Collections.reverse(savings);
int sncounter = 0;
for (SavingsNode sn: savings ){
// for every route in prob.soln
// if from or to isnt in any of those routes
// do that stuff
if (!soln.contains(sn.i)){
if(!soln.contains(sn.j)){
if ((sn.i.c + sn.j.c) <= 50){
ArrayList<Customer> route = new ArrayList<Customer>();
route.add(sn.i);
route.add(sn.j);
soln.add(route);
}
}
}else if(!soln.contains(sn.j)){
//find a route that ends in from
int d = 0;
int e = 0;
for (List<Customer> c: soln){
for (Customer x : c){
d += x.c;
}
if (c.get(c.size()-1) == sn.i){
if (d + sn.j.c <= 50){
// Customer route = new Customer(sn.i.c, sn.j.c, cust.requirement);
ArrayList<Customer> newRoute = new ArrayList<Customer>();
newRoute.add(sn.i);
// prob.customers.remove(sn.j);
// soln.remove(c);
c.addAll(newRoute);
soln.add(c);
d=0;
//newRoute.clear();
break;
}
}
}
}
if (!soln.contains(sn.i)){
int d = 0;
int e = 0;
for (List<Customer> c:soln){
if (c.get(0) == sn.j){
for (Customer x : c){
d += x.c;
}
if (d+ sn.i.c <= 50){
ArrayList<Customer> newRoute = new ArrayList<Customer>();
newRoute.add(sn.j);
soln.remove(c);
c.addAll(0, newRoute);
soln.add(c);
d=0;
// soln.remove(arg0)
break;
}
}
}
}
List<Customer> merged = null;
int mergescounter = 0;
int xreq= 0;
int yreq = 0;
for (List<Customer> routeX: soln){
if (merged != null){
break;
}
if (routeX.get(routeX.size()-1) == sn.i){
for(Customer c:routeX){
xreq += c.c;
}
for (List<Customer> routeY: soln){
if (routeY.get(0) == sn.j){
for(Customer c:routeY){
yreq += c.c;
}
if ((routeX!=null)&&(routeY!=null)){
if (routeX != routeY){
int capnec = 0;
capnec = yreq+xreq;
if (sn.i.c +sn.j.c + capnec <= 50){
List<Customer> merged1 = new ArrayList<Customer>();
merged1.addAll(routeY);
merged1.addAll(routeX);
// route.add(merged1);
soln.add(merged1);
mergescounter++;
merged = routeX;
capnec=0;
break;
}
}
}
}
}
}
}
if (merged!=null){
soln.remove(merged);
}
}
Customer class:
public class Customer extends Point2D.Double{
// Requirements of the customer (number to be delivered)
public static int c;
public int requirement;
public Customer(int x, int y, int requirement){
this.x = x;
this.y = y;
//parcel requirement
this.c = requirement;
}
SavingsNode class
SavingsNode(Customer i, Customer j, double savingVal){
this.i = i;
this.j= j;
this.savingVal = savingVal;
}
Any help would be appreciated.