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);
}
}
Related
**can someone find the error and send the code please i am new to java
i am unable to find the error
the following are error showing
Note: PrimAlgorithmPQBetter.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details. Error: Main method
not found in class PrimAlgorithmPQBetter, please define the main
method as: public static void main(String[] args) or a JavaFX
application class must extend javafx.application.Application
import javafx.util.Pair;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.PriorityQueue;
public class PrimAlgorithmPQBetter {
static class Edge {
int source;
int destination;
int weight;
public Edge(int source, int destination, int weight) {
this.source = source;
this.destination = destination;
this.weight = weight;
}
}
static class ResultSet {
int parent;
int weight;
}
static class Graph {
int vertices;
LinkedList<Edge>[] adjacencylist;
Graph(int vertices) {
this.vertices = vertices;
adjacencylist = new LinkedList[vertices];
//initialize adjacency lists for all the vertices
for (int i = 0; i <vertices ; i++) {
adjacencylist[i] = new LinkedList<>();
}
}
public void addEgde(int source, int destination, int weight) {
Edge edge = new Edge(source, destination, weight);
adjacencylist[source].addFirst(edge);
edge = new Edge(destination, source, weight);
adjacencylist[destination].addFirst(edge); //for undirected graph
}
public void primMST(){
boolean[] mst = new boolean[vertices];
ResultSet[] resultSet = new ResultSet[vertices];
int [] key = new int[vertices]; //keys used to store the key to know whether priority queue update is required
//Initialize all the keys to infinity and
//initialize resultSet for all the vertices
for (int i = 0; i <vertices ; i++) {
key[i] = Integer.MAX_VALUE;
resultSet[i] = new ResultSet();
}
//Initialize priority queue
//override the comparator to do the sorting based keys
PriorityQueue<Pair<Integer, Integer>> pq = new PriorityQueue<>(vertices, new Comparator<Pair<Integer, Integer>>() {
#Override
public int compare(Pair<Integer, Integer> p1, Pair<Integer, Integer> p2) {
//sort using key values
int key1 = p1.getKey();
int key2 = p2.getKey();
return key1-key2;
}
});
//create the pair for for the first index, 0 key 0 index
key[0] = 0;
Pair<Integer, Integer> p0 = new Pair<>(key[0],0);
//add it to pq
pq.offer(p0);
resultSet[0] = new ResultSet();
resultSet[0].parent = -1;
//while priority queue is not empty
while(!pq.isEmpty()){
//extract the min
Pair<Integer, Integer> extractedPair = pq.poll();
//extracted vertex
int extractedVertex = extractedPair.getValue();
mst[extractedVertex] = true;
//iterate through all the adjacent vertices and update the keys
LinkedList<Edge> list = adjacencylist[extractedVertex];
for (int i = 0; i <list.size() ; i++) {
Edge edge = list.get(i);
//only if edge destination is not present in mst
if(mst[edge.destination]==false) {
int destination = edge.destination;
int newKey = edge.weight;
//check if updated key < existing key, if yes, update if
if(key[destination]>newKey) {
//add it to the priority queue
Pair<Integer, Integer> p = new Pair<>(newKey, destination);
pq.offer(p);
//update the resultSet for destination vertex
resultSet[destination].parent = extractedVertex;
resultSet[destination].weight = newKey;
//update the key[]
key[destination] = newKey;
}
}
}
}
//print mst
printMST(resultSet);
}
public void printMST(ResultSet[] resultSet){
int total_min_weight = 0;
System.out.println("Minimum Spanning Tree: ");
for (int i = 1; i <vertices ; i++) {
System.out.println("Edge: " + i + " - " + resultSet[i].parent +
" key: " + resultSet[i].weight);
total_min_weight += resultSet[i].weight;
}
System.out.println("Total minimum key: " + total_min_weight);
}
public static void main(String[] args) {
int vertices = 6;
Graph graph = new Graph(vertices);
graph.addEgde(0, 1, 4);
graph.addEgde(0, 2, 3);
graph.addEgde(1, 2, 1);
graph.addEgde(1, 3, 2);
graph.addEgde(2, 3, 4);
graph.addEgde(3, 4, 2);
graph.addEgde(4, 5, 6);
graph.primMST();
}
}
}
I am making a game in which i have randomly spawned objects. I also have a table that has the data of which objects are close to one another, say within a range of 200px - lets call them neighbors. What i want is to generate and assign coordinates to all the available objects so that this relationship is reflected. I want to view the structure of how they are.
I have made a greedy algorithm. Which works very slowly. and gets stuck sometimes. Does anyone have a better approach to this? - the coordinates can be assigned dynamically through trial and error no problem.
Below is the current code classes.
/**
* biggest problem
* */
public void assignInitialCoords( MyObject[] objects )
{
objects[0].setxCoor(rand.nextInt(1000));
objects[0].setyCoor(rand.nextInt(1000));
for(int i=0; i<objects.length; i++ )
{
ArrayList<MyObject> neighs = objects[i].getNeighbours();
System.out.println("Assigning " + objects[i].getId() + "'s coors");
setNonNeighborCoords(objects, i);
setNeighborCoordinates(objects, i, neighs);
System.out.println(objects[i].getId() + "(" + objects[i].getxCoor() + ", " + objects[i].getyCoor() + ")\n");
}
}
The classes
import java.util.ArrayList;
import java.util.HashMap;
public class MyObject
{
public ArrayList<MyObject> neighbours;
public ArrayList<MyObject> nonNeighbours;
public double fov = 360;
public double sRange = 100, xCoor, yCoor;
boolean isClustered = false;
public String id;
//Cluster[] clusters;
public MyObject()
{
neighbours = new ArrayList<MyObject>();
nonNeighbours = new ArrayList<MyObject>();
}
/**
* Find neighbours for this Object given a relations table
* example: if a MyObject has id A, and neighbor is B, then the key can be either: A_B or B_A
* Both represent the same relation, so we only need to check it once
* */
public void findNeighbours(HashMap<String, Integer> table, MyObject[] objects)
{
for (int i = 0; i < objects.length; i++)
{
String key1 = getId() + "_" + objects[i].getId(), key2 = objects[i].getId() +"_" + getId(), key="";
if(table.get(key1) != null)
{
key = key1;
if(table.get(key) <= getsRange())
getNeighbours().add(objects[i]);
}
if(table.get(key2) != null)
{
key = key2;
if(table.get(key) <= getsRange())
getNeighbours().add(objects[i]);
}
}
}
/**
* Check whether a given Object is the neighbour ArrayList of this object
* */
public boolean isInNeighbours( MyObject n )
{
if(neighbours.equals(null)) { return false; }
for(int i=0; i<getNeighbours().size(); i++)
if(getNeighbours().get(i).getId().equals(n.getId())) { return true; }
return false;
}
/**
* Check whether a given Object is the noneighbour ArrayList of this object
* */
public boolean isInNonNeighbours( MyObject n )
{
if(nonNeighbours.equals(null)) { return false; }
for(int i=0; i<getNonNeighbours().size(); i++)
if(getNonNeighbours().get(i).getId().equals(n.getId())) { return true; }
return false;
}
/**
* Check if given MyObject Can be a neighbour to this Object - for rand coord generation
* */
public boolean canBeANeighbour(MyObject n)
{
return distanceTo(n) <= sRange;
}
// return Euclidean distance between this and p
public double distanceTo(MyObject p) {
double dx = this.xCoor - p.xCoor;
double dy = this.yCoor - p.yCoor;
return Math.sqrt(dx*dx + dy*dy);
}
//Setters And Getters
public ArrayList<MyObject> getNeighbours(){ return neighbours; }
public void setNeighbours(ArrayList<MyObject> neighbours)
{
this.neighbours = neighbours;
}
public double getFov()
{
return fov;
}
public void setFov(double fov)
{
this.fov = fov;
}
public double getsRange()
{
return sRange;
}
public void setsRange(double sRange)
{
this.sRange = sRange;
}
public double getxCoor()
{
return xCoor;
}
public void setxCoor(double xCoor)
{
this.xCoor = xCoor;
}
public double getyCoor()
{
return yCoor;
}
public void setyCoor(double yCoor)
{
this.yCoor = yCoor;
}
public boolean isClustered()
{
return isClustered;
}
public void setClustered(boolean isClustered)
{
this.isClustered = isClustered;
}
public String getId()
{
return id;
}
public void setId(String id)
{
this.id = id;
}
public ArrayList<MyObject> getNonNeighbours()
{
return nonNeighbours;
}
public void setNonNeighbours(ArrayList<MyObject> nonNeighbours)
{
this.nonNeighbours = nonNeighbours;
}
}
//The sample test:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;
public class SampleField
{
Random rand = new Random();
public int range = 100;
HashMap<String, Integer> table = new HashMap<String, Integer>();
String[] nodeIds = {"A", "B", "C", "D", "E", "F"};
public MyObject[] objects = new MyObject[nodeIds.length];
public static void main(String[] args)
{
SampleField test = new SampleField();
for(MyObject n: test.objects)
{
n.findNeighbours(test.table, test.objects);
}
test.populateNonNeighbours(test.objects);
System.out.println(test.table);
test.printRelationsTable( test.objects );
test.assignInitialCoords(test.objects);
System.out.println(test.table);
test.printRelationsTable( test.objects );
}
public SampleField()
{
initialiseNodes();
generateTestTable(objects);
}
/**
* biggest problem
* */
public void assignInitialCoords( MyObject[] objects )
{
objects[0].setxCoor(rand.nextInt(1000));
objects[0].setyCoor(rand.nextInt(1000));
for(int i=0; i<objects.length; i++ )
{
ArrayList<MyObject> neighs = objects[i].getNeighbours();
System.out.println("Assigning " + objects[i].getId() + "'s coors");
setNonNeighborCoords(objects, i);
setNeighborCoordinates(objects, i, neighs);
System.out.println(objects[i].getId() + "(" + objects[i].getxCoor() + ", " + objects[i].getyCoor() + ")\n");
}
}
/**
* if this object has neighbours, try to set their coordinates so that they do not conflict
*
* #param objects
* #param i
* #param neighs
*/
private void setNeighborCoordinates(MyObject[] objects, int i, ArrayList<MyObject> neighs)
{
//it should have at least one neighbour
if(neighs != null )
for( int j=0; j<neighs.size(); j++ )
{
//Initial assignment to the first neighbor
neighs.get(j).setxCoor(rand.nextInt() + objects[i].getsRange() );
neighs.get(j).setyCoor(rand.nextInt() + objects[i].getsRange() );
//If not a neighbor keep generating coordinates until it keeps being a neighbor.
while( !objects[i].canBeANeighbour(neighs.get(j)) )
{
//go deeper? - later
neighs.get(j).setxCoor(rand.nextInt(1000) - shootRange() - 5 );
neighs.get(j).setyCoor(rand.nextInt(1000) - shootRange() - 5 );
}
}
}
/**
* try to set the coordinates of each object here
* #param objects
* #param i
*/
private void setNonNeighborCoords(MyObject[] objects, int i)
{
for(MyObject n : objects[i].getNonNeighbours())
{
n.setxCoor(rand.nextInt() + shootRange() - 5);
n.setyCoor(rand.nextInt() + shootRange() - 5);
//Make sure non neighbors remain non neighbors
while(objects[i].canBeANeighbour(n))
{
n.setxCoor(rand.nextInt() + shootRange() - 5 );
n.setyCoor(rand.nextInt() + shootRange() - 5 );
}
}
}
/* populate nonNeighbours */
public void populateNonNeighbours(MyObject[] objects)
{
for(int i=0; i<objects.length; i++)
{
for(int j=0; j<objects.length; j++ )
{
if( (objects[i].getId() != objects[j].getId()) && !objects[i].isInNeighbours(objects[j]) )
{
objects[i].getNonNeighbours().add(objects[j]);
}
}
}
}
/* Show each object and its neighbors/nonneighbors - just for output */
public void printRelationsTable( MyObject[] objects )
{
for(int i=0; i<objects.length; i++ )
{
System.out.print("MyObject " + objects[i].getId() + "'s neighbours: ");
for(int j=0; j<objects[i].getNeighbours().size(); j++)
{
System.out.print(objects[i].getNeighbours().get(j).getId() + " ");
}
System.out.println();
System.out.print("\t\t" +objects[i].getId()+ "' : ");
for(int j=0; j<objects[i].getNonNeighbours().size(); j++)
{
System.out.print(objects[i].getNonNeighbours().get(j).getId() + " ");
}
System.out.println();
}
}
/* Initialise Objects here - give basic information */
public void initialiseNodes()
{
for(int i=0; i<nodeIds.length; i++)
{
MyObject n = new MyObject();
n.setId(nodeIds[i]);
n.setsRange(shootRange());
objects[i] = n;
}
}
/* Generate a list of neighbors for testing */
public void generateTestTable(MyObject[] objects)
{
for(int i=0; i<objects.length; i++)
{
/* Get two objects' ids and make them neighbors - ids must be unique */
String firstId = objects[rand.nextInt(objects.length)].getId();
String secondId = objects[rand.nextInt(objects.length)].getId();
while( firstId.equals(secondId) || table.containsKey(firstId + "_" + secondId) || table.containsKey(secondId + "_" + firstId) )
{
firstId = objects[rand.nextInt(objects.length)].getId();
secondId = objects[rand.nextInt(objects.length)].getId();
}
table.put(firstId + "_" + secondId, shootRange());
}
}
/* Range within which they are neighbors */
public int shootRange()
{
return range;
}
public void setRange(int range)
{
this.range = range;
}
}
If you only compare distances (and if you are talking about neighbours, it seems so), then you don't need to count them at all. Instead of
range = sqrt(sqr(a.x-b.x)+sqr(a.y-b.y))
if (range >d)...
use
sqrange(a,b) = sqr(a.x-b.x)+sqr(a.y-b.y)
if (range> d_sqr) ...
That means, you don't use ranges, but their squares. That quickens the comparisons about 50 times (for double). So, you can use much more easy structures.
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!
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.
I'm back with another question about my Dijkstra algorithm. I fixed my previous question, but now I want to make a toString() method.
When I try to make it, the variables I use are unreachable from toString(), and I don't understand why.
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
import java.util.ArrayList;
public class Graph
{
ArrayList<Vertex> vertexObjects = new ArrayList<Vertex>();
ArrayList<Edge> edgeObjects = new ArrayList<Edge>();
ArrayList<Vertex> visitedObjects = new ArrayList<Vertex>();
ArrayList<Vertex> unvisitedObjects = new ArrayList<Vertex>();
ArrayList<Edge> tempEdge = new ArrayList<Edge>();
int numVertices = 0;
public void readFile(String textfile)
{
try {
Scanner s = new Scanner(new File(textfile));
String sameVertex = "";
while (s.hasNext()) {
String preVertex = s.next();
String postVertex = s.next();
String distance = s.next();
Edge temp = new Edge(preVertex, postVertex, distance);
edgeObjects.add(temp);
if (!(preVertex.equals(sameVertex)))
{
Vertex herp = new Vertex(preVertex, Double.POSITIVE_INFINITY, false, null);
vertexObjects.add(herp);
sameVertex = preVertex;
numVertices++;
}
}
} catch (FileNotFoundException e) {
System.out.println("I can't find that file!");
}
}
public void dijkstra(String startVertex, String endVertex)
{
// Change the distance of the startVertex to 0 and all others to infinity.
for (int i = (numVertices-1); i >= 0; i--)
{
if (vertexObjects.get(i).vertexName.equals(startVertex))
{
vertexObjects.get(i).distance = 0;
} else {
vertexObjects.get(i).distance = Double.POSITIVE_INFINITY;
}
}
//Set the node with lowest distance value to Current Status
unvisitedObjects = vertexObjects;
double smallDistance = Double.POSITIVE_INFINITY;
while(unvisitedObjects.size() != 0) {
//set current node to vertex with shortest distance
String currentNode = "";
for (int j = (unvisitedObjects.size()-1); j >= 0; j--) {
if (unvisitedObjects.get(j).distance <= smallDistance) {
smallDistance = unvisitedObjects.get(j).distance;
currentNode = unvisitedObjects.get(j).vertexName;
}
}
//remove the smallest distance having node from the unvisited array
//and place into visited array.
for (int g = (unvisitedObjects.size()-1); g >= 0; g--) {
if (unvisitedObjects.get(g).vertexName.equals(currentNode))
{
visitedObjects.add(unvisitedObjects.get(g));
unvisitedObjects.remove(g);
}
}
//for all the nodes that are adjacent to the current node, update their
//distance values if they are larger than the weight plus previous distances.
for (int w = (edgeObjects.size()-1); w >= 0; w--) {
if (edgeObjects.get(w).startVertex == currentNode) {
tempEdge.add(edgeObjects.get(w));
}
for (int t = (tempEdge.size()-1); t >=0; t--) {
for (int p = (vertexObjects.size()-1); p >= 0; p--) {
if (tempEdge.get(t).endVertex == vertexObjects.get(p).vertexName)
{
if ((Double.parseDouble(tempEdge.get(t).edgeWeight) + smallDistance) < vertexObjects.get(p).distance) {
vertexObjects.get(p).distance = (Double.parseDouble(tempEdge.get(t).edgeWeight) + smallDistance);
}
}
}
}
}
}
String smallDString = Double.toString(smallDistance);
}
public Graph(String textfile, String startingVertex, String endingVertex) {
String graphFile = textfile;
String startVertex = startingVertex;
String endVertex = endingVertex;
}
public String toString() {
return ("The shortest path from "+startVertex+" to "+endVertex+" is "+smallDistance+".");
}
}
You can't access them because they are initialized within a function. You need to declare them as global variables.
ArrayList<Vertex> vertexObjects = new ArrayList<Vertex>();
ArrayList<Edge> edgeObjects = new ArrayList<Edge>();
ArrayList<Vertex> visitedObjects = new ArrayList<Vertex>();
ArrayList<Vertex> unvisitedObjects = new ArrayList<Vertex>();
ArrayList<Edge> tempEdge = new ArrayList<Edge>();
int numVertices = 0;
String startVertex, smallDistance, endVertex = "";
that might be the problem.
You need to make them field in your class. Not just local variables. For example in your constructor you can do:
private String graphFile; // declare variable to store value
public Graph(String textfile, String startingVertex, String endingVertex) {
graphFile = textfile; // assign value in constructor
...
You've only declared the variables in the public Graph(...) constructor, not in the scope for the rest of the class.
You need to declare them in the class body (normally near the top) for the methods to have access to them.
This is because you declared and initialized the variables (graphFile,startVertex,endVertex,) inside of your constructor. This means that they are only visible/usable inside the constructor. You need to declare them as class variables, then you can initialize them in the constructor. For example
public class Graph {
String graphFile;
public Graph(String textfile) {
this.graphFile = textfile;
}
}
You can do the same for the others as well.