I'm working on something quite interesting, the TSP in Genetic Algorithms, more specifically looking at Partially Mapped Crossover. For background on the code, it receives two arrays of type int which correspond to the relevant cities so for example, first and second could be 1,2,3,4,5,6,7 and 2,3,4,5,2,4,3. What happens next is I try and cross over the cities without any duplication, however when I'm executing the while loop, it doesn't seem to be able to solve my issue as it gets stuck in an infinite loop.
Essentially, I'm baffled as to why it would get stuck in a loop when eventually it should just cross Over the city and get rid of all the duplicates, but for some reason I'm forever stuck in the while!
Background of code:
SIZE = size of cities in array, parent one and parent two contain random cities of size SIZE.
Any help would be greatly aprechiated!
private int[][] partiallyMappedCrossover(int first, int second){
//Used to return an array of type int
int[][] tempArray = new int[2][SIZE];
//Used to represent the selected individuals
ArrayList<Integer> parentOne = new ArrayList<Integer>();
ArrayList<Integer> parentTwo = new ArrayList<Integer>();
ArrayList<Integer> parentOneExchange = new ArrayList<Integer>();
ArrayList<Integer> parentTwoExchange = new ArrayList<Integer>();
//Used to generate crossOverPoints
ArrayList<Integer> crossOverPoints = new ArrayList<Integer>();
crossOverPoints.add(random.nextInt(SIZE));
crossOverPoints.add(random.nextInt(SIZE));
Collections.sort(crossOverPoints);
//Used for checking the parents contents
int currentCity = 0;
int arrayIndex = 0;
int newCity = 0;
//Assign the contents of the selected parents to my parentArrays
for(int i = 0; i < SIZE; i++){
parentOne.add(population[first][i]);
parentTwo.add(population[second][i]);
}
//used to gather cities from tours and swap between randomly selected crossoverpoints
for(int k = crossOverPoints.get(0) ; k < crossOverPoints.get(1) ; k++){
//declare ints to store the city value
int a = parentOne.get(k);
int b = parentTwo.get(k);
//excahnge cities between the two crossOverPoints
parentOneExchange.add(b);
parentTwoExchange.add(a);
}
for(int i = 0; i < crossOverPoints.get(0); i++){
//get the first city from the parentOne
currentCity = parentOne.get(i);
//Check the cities
if(parentOneExchange.contains(currentCity)){
//If it does contain the city, give one the index from the exchange
arrayIndex = parentOneExchange.indexOf(currentCity);
// get the city where we have a repitition
newCity = parentTwo.get(arrayIndex);
//if the new city is also a duplicated one, do another check
while(parentOneExchange.contains(newCity)){
// get the index of the city to replace the repeated city
arrayIndex = parentOneExchange.indexOf(newCity);
// get the city that is intended to replace the repeated city
newCity = parentTwo.get(arrayIndex);
}
//replace the duplicated city with the new city
parentOne.set(i,newCity);
}
currentCity = parentTwo.get(i);
if(parentTwoExchange.contains(currentCity)){
//If it does contain the city, give one the index from the exchange
arrayIndex = parentTwoExchange.indexOf(currentCity);
// get the city where we have a repitition
newCity = parentOne.get(arrayIndex);
//if the new city is also a duplicated one, do another check
while(parentTwoExchange.contains(newCity)){
// get the index of the city to replace the repeated city
arrayIndex = parentTwoExchange.indexOf(newCity);
// get the city that is intended to replace the repeated city
newCity = parentOne.get(arrayIndex);
}
//replace the duplicated city with the new city
parentTwo.set(i,newCity);
}
}
//loop the second crosschange
for(int i = crossOverPoints.get(1); i < SIZE; i++){
//get the first city from the parentOne
currentCity = parentOne.get(i);
//Check the cities
if(parentOneExchange.contains(currentCity)){
//If it does contain the city, give one the index from the exchange
arrayIndex = parentOneExchange.indexOf(currentCity);
// get the city where we have a repitition
newCity = parentTwo.get(arrayIndex);
//if the new city is also a duplicated one, do another check
while(parentOneExchange.contains(newCity)){
// get the index of the city to replace the repeated city
arrayIndex = parentOneExchange.indexOf(newCity);
// get the city that is intended to replace the repeated city
newCity = parentTwo.get(arrayIndex);
}
//replace the duplicated city with the new city
parentOne.set(i,newCity);
}
currentCity = parentTwo.get(i);
if(parentTwoExchange.contains(currentCity)){
//If it does contain the city, give one the index from the exchange
arrayIndex = parentTwoExchange.indexOf(currentCity);
// get the city where we have a repitition
newCity = parentOne.get(arrayIndex);
//if the new city is also a duplicated one, do another check
while(parentTwoExchange.contains(newCity)){
// get the index of the city to replace the repeated city
arrayIndex = parentTwoExchange.indexOf(newCity);
// get the city that is intended to replace the repeated city
newCity = parentOne.get(arrayIndex);
}
//replace the duplicated city with the new city
parentTwo.set(i,newCity);
}
}
//Assign the new offspring to the temp array for return
for(int i = 0; i<SIZE; i++){
tempArray[0][i] = parentOne.get(i);
tempArray[1][i] = parentTwo.get(i);
}
//return the contents of my tempArray
return tempArray;
}
Reading code to find errors like this is notoriously difficult and laborious. There are lots of easier ways you can find these types of errors. I'll give you four to consider (in my personal rough order of preference):
Split the various operations in your method into separate methods then write unit tests for each of those methods making sure they do exactly what you expect before moving onto the next. Once they are all working then you write a method that uses them all. Debugging a small method is much easier than debugging a large one.
Add assert statements that check that the conditions that you expect to be true actually are true. I'll give an example of that below.
An interactive debugger can find why your loop is not completing. That way you can see exactly what values the variables have at each point in your loop.
Add log statements to record interim values as the method progresses. This allows you to ensure expected conditions are met as the algorithm progresses.
Looking at one of your while loops:
while(parentOneExchange.contains(newCity)){
// get the index of the city to replace the repeated city
arrayIndex = parentOneExchange.indexOf(newCity);
// get the city that is intended to replace the repeated city
newCity = parentTwo.get(arrayIndex);
}
This will infinitely loop any time parentTwo.get returns a city that has been previously encountered. I expect that's what's happening due to a logic error earlier in the code. You could add an assertion to ensure that's not the case:
List<Integer> previous = new ArrayList<>();
while(parentOneExchange.contains(newCity)){
assert !previous.contains(newCity): previous;
previous.add(newCity);
arrayIndex = parentOneExchange.indexOf(newCity);
newCity = parentTwo.get(arrayIndex);
}
When this assertion fails you can see the list of previously visited cities and try to understand why it is looping.
Related
Hi i'm trying to loop through this structure:
id string1 string2 different_string
1 test test asd
1 test test dsa
2 data data qwe
3 info info ewq
3 info info zxc
3 info info qaz
I have rows with the exact same value but one of them is different so I'm trying to compress that data into a single row.
This is my code:
int actual_id = list.get(0).num(); //I pick the first id = 1
ArrayList<ArrayList<String>> listOLists = new ArrayList<ArrayList<String>>();
ArrayList<String> items = new ArrayList<String>();
for(int i = 0; i < list.size(); i++){
if(list.get(i).id == actual_id){
String str = list.get(i).different_string;
items.add(str);
listOLists.add(items);
items.clear();
}else {
actual_id = list.get(i).id;
i--;
}
}
for(int j = 0; j < listOLists.size(); j++) {
System.out.println(listOLists);
}
First I check the id of each row and compare it with the actual value, I'm adding the string to an array and then append it to a list so I can store my data then reset the array to append new data to it and repeat the process, the problem is when I reset the array the loop doesn't seem to add more items to the list, what am I doing wrong?
this is I would like to get something like this:
{1, test, test, {asd, dsa}},{2, data, data, {asd}},{3, info, info, {ewq, zxc,qaz}}
Your code is a little difficult to follow but from what I can tell, your problem lies in this part of the code:
if(list.get(i).id == actual_id){
String str = list.get(i).different_string;
items.add(str);
listOLists.add(items);
items.clear();
}else {
You add items to listOList, but in the next line you clear it. items still refers to the List you just added to listOList so when you clear it, it clears the list in listOList as well. You may want to declare items within the if block so a new List will be created each pass through the loop like this:
if(list.get(i).id == actual_id){
ArrayList<String> items = new ArrayList<String>();
String str = list.get(i).different_string;
items.add(str);
listOLists.add(items);
}else {
This way, a new List is being created every iteration through the loop avoiding the issue. I can't tell if this will solve all you're problems but I hope this gets you headed in the right direction.
First off, sorry for the terrible title I don't know how to describe this well. I have a sql file with a table with two columns (number and price), in java im extracting the data and have stored them into two seperate lists. Some of the numbers in the column are the same eg (100, 100, 101) and correlate to a different price eg (50, 40, 60). I need to find a way to add all the prices together that have the same column value, so an output like (90, 60). The lists are in order so index one of my list named number will go with index one of my list named price. A small example of the lists would be:
ArrayList<String> number = new ArrayList<String>();
ArrayList<Integer> price = new ArrayList<Integer>();
number.add("100");
number.add("100");
number.add("101");
number.add("101");
number.add("101");
number.add("102");
number.add("103");
number.add("103");
price.add(50);
price.add(150);
price.add(20);
price.add(200);
price.add(75);
price.add(40);
price.add(100);
price.add(125);
Any help would be appreciated, Thanks.
You could use a HashMap<String, Integer>.
Assuming that the size of number and price is the same. You can do something like this:
HashMap<String, Integer> sumForNumber = new HashMap<>();
for (int i = 0; i < number.size(); i++) {
String key = number.get(i);
sumForNumber.put(key, sumForNumber.getOrDefault(key, 0) + price.get(i));
}
I hope this code helps you
Map<String, Integer> result = new HashMap<>();
for (int i = 0; i < number.size(); i++) {
String num = number.get(i);
if (result.containsKey(num)) {
Integer sum = result.get(num);
result.put(num, sum + price.get(i));
} else {
result.put(num, price.get(i));
}
}
I have a master arraylist call toBeDeleted which stored timestamp and email. The following are the sample data inside the toBeDeleted arraylist
[1507075234, bunny#outlook.com]
I have one arraylist call logData1 which stored status,email,timestamps and ID. The following are the sample data inside the logData1 arraylist.
[16, bunny#outlook, 1507075234, 0OX9VQB-01-00P-02]
I hope to delete the data inside the logData1 arraylist by verifying the timestamp first with timestamps stated in toBeDeleted1 arraylist, if the timestamp matched, I will check the email for both arraylist. If both of them are matched, I would like to delete away all the data (status,email,timestamp,ID). But I cant make it work
this is my sample output from my source code
[16, bunny#outlook.com, 1507075234, 0OX9VQB-01-00P-02]
The data inside toBeDeleted1 is :[1507075234, bunny#outlook.com]
The time1 is :1507075234
The email1 is :bunny#outlook.com
The time is :1507075234
The emails is :bunny#outlook.com
The data is :bunny#outlook.com
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -3
at java.util.ArrayList.elementData(Unknown Source)
at java.util.ArrayList.get(Unknown Source)
at EmailReporting.main(EmailReporting.java:83)
This is my sample program
System.out.println(logData1);
System.out.println("The data inside toBeDeleted1 is :"+toBeDeleted1);
for(int v = 0;v<toBeDeleted1.size();v++) //look through the logdata1 for removing the record base on timestamp
{
String time1 = toBeDeleted1.get(v);
String email1 = toBeDeleted1.get(v+1);
System.out.println("The time1 is :"+time1);
System.out.println("The email1 is :"+email1);
for(int f = logData1.size();f>logData1.size()-1;f--)
{
// System.out.println(logData1.size());
// System.out.println("The data in logdata1 is "+logData1.get(f-2));
if(time1.equals(logData1.get(f-2)))
{
System.out.println("The time is :"+logData1.get(f-2));
System.out.println("The emails is :"+logData1.get(f-3));
if(email1.equals(logData1.get(f-3)))
{
System.out.println("The data is :"+logData1.get(f-3));
logData1.remove(f-1);
logData1.remove(f-2);
logData1.remove(f-3);
logData1.remove(f-4);
f-=4;
}
}
}
}
The error occurred after this line of code executed
System.out.println("The data is :"+logData1.get(f-3));
You can find elements in the list in order using Collections.indexOfSubList:
List<String> toFind = Arrays.asList(time1, email1);
int emailIndex = Collections.indexOfSubList(logData1, toFind);
A similar lastIndexOfSubList method also exists. That might be more appropriate for your use case.
You can then use this to remove the elements from toFind:
int emailIndex = Collections.lastIndexOfSubList(logData1, toFind);
if (emailIndex >= 1) {
logData1.subList(emailIndex-1, emailIndex+3).clear();
}
Just do this in a loop to keep going until all occurrences have been removed.
Note that just doing this in a loop naively will keep on searching over the tail of the list repeatedly. Instead, you can use subList to "chop" the end of the list, to avoid re-searching it:
List<String> view = logData1;
int emailIndex;
while ((emailIndex = Collections.lastIndexOfSubList(view, toFind)) >= 1) {
logData1.subList(emailIndex-1, emailIndex+3).clear();
view = logData1.subList(0, emailIndex-1);
}
Additionally, note that deleting from the middle of an ArrayList is inefficient, because the elements after the ones you delete have to be shifted down. This is why using subList(...).clear() is better, because it does all of those shifts at once. But if you are removing lots of 4-element batches, you can do better.
Instead of the subList(...).clear(), you can set the bits of elements to be deleted into a BitSet:
List<String> view = logData1;
BitSet bits = new BitSet(logData1.size());
int emailIndex;
while ((emailIndex = Collections.lastIndexOfSubList(view, toFind)) >= 1) {
bits.set(emailIndex-1, emailIndex+3);
view = logData1.subList(0, emailIndex-1);
}
And then shift all the elements down at once, discarding the elements you want to delete:
int dst = 0;
for (int src = 0; src < logData1.size(); ++src) {
if (!bits.get(src)) {
logData1.set(dst++, logData1.get(src));
}
}
And now truncate the list:
logData1.subList(dst, logData1.size());
I have to create a weighted graph from a text file. Below is a example how the text file looks like. The first number is the id of the actual train station. The second number is a possible destination and after the comma is the time in seconds, it takes to travel. The the third number is another possible destination.
060060101832 060063101842,78 060054104822,90
060054104822 060060101832,90 060057104812,90 060058101502,90 060054105611,66
060057104812 060054104822,90 060057102802,72
I want to store the routes in an ArrayList. Each route object should look like this:
Start: 060060101832
Destination: 060063101842
Time: 78
The problem is, I have to store multiple routes for the same starting location. How do I read the lines properly in, using a scanner? My approach was this:
while (routes.hasNext()) {
routes.useDelimiter(",| |\\n");
String start = routes.next();
String dest= routes.next();
String time= routes.next();
Edge edge = new Edge(start, dest, time);
edges.add(edge);
}
Since I cannot go back in the text file, I can't imagine how a right solution should look like.
This is not a complete code nor it was tested. It may or may not work but it will guide you anyways.
// Java 8
Node n;
Edge e;
String[] splittedLine;
String[] splittedEdge;
HashMap<String, Node> stationNumberToNode = new HashMap<>();
// if the file is not too large, you can read the file at once
List<String> lines = Files.readAllLines(new File("path/to/file.txt").getPath());
for(String line : lines){
splittedLine = line.split(" ");
if((n = stationNumberToNode.get(splittedLine[0]) == null){
n = new Node(splittedLine[0]); // assuming your Node has a constructor that takes the station id
stationNumberToNode.put(stationNumberToNode[0], n);
}
for(int i = 1; i < splittedLine.lenght; ++i){
splittedEdge = splittedLine[i].split(",");
e = new Edge(splittedEdge[0], splittedEdge[1]); // assuming your Edgehas a constructor that takes the destination station and the cost
n.addEdge(e);
}
}
Explanation
Node n;
Edge e;
String[] splittedLine;
String[] splittedEdge;
HashMap<String, Node> stationNumberToNode = new HashMap<>();
Ideally you should always declare variables outside loops, so you avoid allocating a new memory on every iteration. Thus, we declare our 5 variables before entering the loop. The HashMap is used here to cover the case that your input is not always grouped and you avoid having to perform a list search everytime.
List<String> lines = Files.readAllLines(new File("path/to/file.txt").getPath());
Read all the lines on the file at once. Alternatively, as requested on the question, you can read the file using Scanner like on this anwer. You have to change the way you iterate over the lines, though.
splittedLine = line.split(" ");
Splits the line on the " ", since your input file is well formated.
if((n = stationNumberToNode.get(splittedLine[0]) == null){
n = new Node(splittedLine[0]); // assuming your Node has a constructor that takes the station id
stationNumberToNode.put(stationNumberToNode[0], n);
}
Checks if the current node is already on the HashMap. If yes, it will be stored in the variable n. Else, it will create a Node with the current id and add it to our HashMap.
for(int i = 1; i < splittedLine.lenght; ++i){
splittedEdge = splittedLine[i].split(",");
e = new Edge(splittedEdge[0], splittedEdge[1]); // assuming your Edgehas a constructor that takes the destination station and the cost
n.addEdge(e);
}
Since everything in the input file is the destination station and its cost (id,cost), we iterate on the splittedLine from index 1 onwards.
For every edge data, we split based on "," (from your input file), whereas splittedEdge[0] will be the destination id and splittedEdge[1] will be the cost to that destination. We create an Edge with that information and add that Edge to the Node object.
I have created a list of 2D arrays containing randomly generated number values for different locations.
public static int Prices[][] = new int[Cities.length][ItemNames.length];
public static List<int[][]> CityPrices = new ArrayList<int[][]>();
public static void NewDay()
{
for(int i = 0; i<Cities.length; ++i)
{
Prices[i] = PriceGenerator.ReturnPricesForCity(i);
//This method returns an array of random integers
}
CityPrices.add(Prices);
}
But then later when I want to retrieve the price history for a specific item for the amount of days passed, it returns the same value for each day
int Prices[] = new int[GlobalVariables.CityPrices.size()];
String sTest = "";
for(int i = 0; i < Prices.length; ++i)
{
Prices[i] = GlobalVariables.CityPrices.get(i)[spinCity.getSelectedItemPosition()][spinItem.getSelectedItemPosition()];
sTest = sTest + Prices[i] + ",";
}
In this case, the values returned by sTest was : 6055,6055,6055,6055,6055, for five consecutive days.
If I would for instance add a day, the values would change to a range of a new number, which in this case was : 7294,7294,7294,7294,7294,7294,
Please show me what I am doing wrong, as I have been trying to figure this one out the past 4 days with no luck.
Every element in your CityPrices list is the same: in each case, you are adding the Prices two-dimensional array. Your loop modifies Prices[i], but it doesn't change Prices, which is still a reference to the same two-dimensional array right the way through.
I think you're imagining it will pass the contents of the array in its current state, but it doesn't: it passes a reference to the array to the .add() method, so any subsequent changes to the array will be reflected in the contents of CityPrices.
If at the end of your loop you try
CityPrices.get(0) == CityPrices.get(1)
you'll see it returns true.
In the assignment: Prices[i] = GlobalVariables.CityPrices.get(i)[spinCity.getSelectedItemPosition()][spinItem.getSelectedItemPosition()]; you are basically referencing an int[][] at the same index for both dimensions.
On top of that, the spinCity.getSelectedItemPosition() invocation might be returning the same index at every iteration of your loop, hence your identical values.
It's hard to assume anything further as you haven't posted the code for spinCity.