Why odd characters appear when printing large ArrayLists - java

I am following a tutorial which partially deals with printing the elements of ArrayLists. The program runs exactly as I'd expect when dealing with small lists. However the string formatting ( I believe ) causes some strange results when larger numbers are input.
My code is as follows:
public class Theatre {
private final String theatreName;
public List<Seat> seats = new ArrayList<>();
public Theatre(String theatreName, int numRows, int seatsPerRow) {
this.theatreName = theatreName;
int lastRow = 'A' + (numRows -1);
for (char row = 'A'; row <= lastRow; row++) {
for(int seatNum = 1; seatNum <= seatsPerRow; seatNum++) {
Seat seat = new Seat(row + String.format("%02d", seatNum));
seats.add(seat);
}
}
}
public String getTheatreName() {
return theatreName;
}
public boolean reserveSeat(String seatNumber) {
int low = 0;
int high = seats.size()-1;
while(low <= high) {
System.out.print(".");
int mid = (low + high) /2;
Seat midVal = seats.get(mid);
int cmp = midVal.getSeatNumber().compareTo(seatNumber);
if(cmp <0) {
low = mid + 1;
} else if(cmp > 0) {
high = mid -1;
} else {
return seats.get(mid).reserve();
}
}
System.out.println("There is no seat " + seatNumber);
return false;
}
// for testing
public void getSeats() {
for(Seat seat : seats) {
System.out.println(seat.getSeatNumber());
}
}
public class Seat implements Comparable<Seat > {
private final String seatNumber;
private boolean reserved = false;
public Seat(String seatNumber) {
this.seatNumber = seatNumber;
}
public boolean reserve() {
if(!this.reserved) {
this.reserved = true;
System.out.println("Seat " + seatNumber + " reserved");
return true;
} else {
return false;
}
}
public boolean cancel() {
if(this.reserved) {
this.reserved = false;
System.out.println("Reservation of seat " + seatNumber + " cancelled");
return true;
} else {
return false;
}
}
public String getSeatNumber() {
return seatNumber;
}
#Override
public int compareTo(Seat seat) {
// returns integer greater than 0 if greater than, less than if less than, 0 if equal
return this.seatNumber.compareTo(seat.getSeatNumber());
}
}
With a Main method class:
public static void main(String[] args) {
Theatre theatre = new Theatre("Olympian", 800, 12);
List<Theatre.Seat> seatCopy = new ArrayList<>(theatre.seats); // shallow copy, contains references to all
// elements of both lists, original and copy
printList(seatCopy);
seatCopy.get(1).reserve();
if (theatre.reserveSeat("A02")) {
System.out.println("Please pay for A02");
} else {
System.out.println("seat already reserved");
}
// see that they are clearly two separate array lists
Collections.reverse(seatCopy);
System.out.println("Printing seat copy");
printList(seatCopy);
System.out.println("Printing theatre.seats");
printList(theatre.seats);
System.out.println("Shuffling seatCopy");
Collections.shuffle(seatCopy);
printList(seatCopy);
}
public static void printList(List<Theatre.Seat> list) {
for (Theatre.Seat seat : list) {
System.out.print(" " + seat.getSeatNumber());
}
System.out.println();
System.out.println("===============================");
}
}
The output (I only quote enough to see ) is:
12 ͠11 ͠10 ͠09 ͠08 ͠07 ͠06 ͠05 ͠04 ͠03 ͠02 ͠01 ͟12 ͟
===============================
Printing theatre.seats
A01 A02 A03 A04 A05 A06 A07 A08 A09 A10
===============================
===============================
Shuffling seatCopy
V07 Ý11 11 ű05 Ú02 ̄06 ̓01 ŕ12 ȣ03 Ǔ05 S07
I am aware that I run out of alphabetical characters and that the formatting in this line:
Seat seat = new Seat(row + String.format("%02d", seatNum));
is intended only to deal with seats of the format "X##".
What I want to understand is specificallty why the odd characters appear ( the "~" and "'", etc. ). Obviously, the formatting is inappropriate. But why does it produce specifically this output?
Thank you for your help,
Marc

You said it yourself. You're running out of alphabetical characters. In fact, you're running out of ASCII characters altogether. From this line:
for (char row = 'A'; row <= lastRow; row++)
What you are doing is starting the row letters from 'A' and continuing across the Unicode character set. So, with more than 26 rows, you start getting symbols like ~, and with enough rows, you leave ASCII altogether and start getting weird row letters like Ý.
If you don't want this to happen, you'll need to ditch the for loop and come up with an entirely different (and more complex) way of assigning row labels.

Related

I have some problems with ArrayList (quiz of head first java)

I've just started learning java since last week. I'm using book called 'head first java' and i'm struggling with solving problems about ArrayList. Error says "The method setLocationCells(ArrayList) in the type DotCom is not applicable for the
arguments (int[])" and I haven't found the solution :( help me..!
enter image description here
This looks like a Locate & Conquer type game similar to the game named Battleship with the exception that this game is a single player game played with a single hidden ship in a single horizontal row of columnar characters. Rather simplistic but kind of fun to play I suppose. The hard part is to locate the hidden ship but once you've located it, conquering (sinking) it becomes relatively easy. I'm sure this isn't the games' intent since it is after all named "The Dot Com Game" but the analogy could be possibly helpful.
There are several issues with your code but there are two major ones that just can not be there for the game to work:
Issue #1: The call to the DotCom.setLocationCells() method:
The initial problem is located within the DotComGame class on code line 13 (as the Exception indicates) where the call is made to the DotCom.setLocationCells() method. As already mentioned in comments the wrong parameter type is passed to this method. You can not pass an int[] Array to the setLocationCell() method when this method contains a parameter signature that stipulates it requires an ArrayList object. The best solution in my opinion would be to satisfy the setLocationCells() method parameter requirement...supply an ArrayList to this method.
The reason I say this is because all methods within the DotCom class work with an established ArrayList and one of the tasks of one of these methods (the checkYourself() method) actually removes elements from the ArrayList which is easy to do from a collection but very cumbersome to do the same from an Array.
To fix this problem you will need to change the data type for the locations variable located within the DotComGame class. Instead of using:
int[] locations = {randomNum, randomNum + 1, randomNum + 2};
you should have:
ArrayList<Integer> locations = new ArrayList<>(
Arrays.asList(random, randomNum + 1, randomNum + 2));
or you could do it this way:
ArrayList<Integer> locations = new ArrayList<>();
locations.add(randomNum);
locations.add(randomNum + 1);
locations.add(randomNum + 2);
There are other ways but these will do for now. Now, when the call to the setLocationCells() method is made you ahouldn't get an exception this issue should now be resolved.
Issue #2: The call to the DotCom.checkYourself() method:
Again, this particular issue is located within the DotComGame class on code line 18 where the call is made to the DotCom.checkYourself() method. Yet another parameter data type mismatch. You are trying to pass a variable of type String (named guess) to this method whereas its signature stipulates that it requires an integer (int) value. That again is a no go.
To fix this problem you will need to convert the string numerical value held by the guess variable to an Integer (int) value. So instead of having this:
while(isAlive) {
String guess = helper.getUserInput("Enter a Number: ");
String result = theDotCom.checkYourself(guess);
// ... The rest of your while loop code ...
}
you should have something like:
while(isAlive) {
String guess = helper.getUserInput("Enter a Number: ");
/* Validate. Ensure guess holds a string representation
of a Integer numerical value. */
if (!guess.matches("\\d+")) {
System.err.println("Invalid Value (" + guess
+ ") Supplied! Try again...");
continue;
}
int guessNum = Integer.parseInt(guess);
String result = theDotCom.checkYourself(guessNum);
numOfGuesses++;
if (result.equals("kill")) {
isAlive = false;
System.out.println(numOfGuesses + " guesses!");
}
else if (result.equals("hit")) {
// Do Something If You Like
System.out.println("HIT!");
}
else {
System.out.println("Missed!");
}
}
Below is a game named Simple Battleship which I based off of your code images (please don't use images for code anymore - I hate using online OCR's ;)
BattleshipGame.java - The application start class:
import java.awt.Toolkit;
public class BattleshipGame {
public static int gameLineLength = 10;
public static void main(String[] args) {
GameHelper helper = new GameHelper();
Battleship theDotCom = new Battleship();
int score = 0; // For keeping an overall score
// Display About the game...
System.out.println("Simple Battleship Game");
System.out.println("======================");
System.out.println("In this game you will be displayed a line of dashes.");
System.out.println("Each dash has the potential to hide a section of a");
System.out.println("hidden Battleship. The size of this ship is randomly");
System.out.println("chosen by the game engine and can be from 1 to 5 sections");
System.out.println("(characters) in length. The score for each battle is based");
System.out.println("on the length of the game line that will be displayed to");
System.out.println("you (default is a minimum of 10 charaters). You now have");
System.out.println("the option to supply the game line length you want to play");
System.out.println("with. If you want to use the default then just hit ENTER:");
System.out.println();
// Get the desire game line length
String length = helper.getUserInput("Desired Game Line Length: --> ", "Integer", true, 10, 10000);
if (!length.isEmpty()) {
gameLineLength = Integer.parseInt(length);
}
System.out.println();
// Loop to allow for continuous play...
boolean alwaysReplay = true;
while (alwaysReplay) {
int numOfGuesses = 0;
/* Create a random ship size to hide within the line.
It could be a size from 1 to 5 characters in length. */
int shipSize = new java.util.Random().nextInt((5 - 1) + 1) + 1;
int randomNum = (int) (Math.random() * (gameLineLength - (shipSize - 1)));
int[] locations = new int[shipSize];
for (int i = 0; i < locations.length; i++) {
locations[i] = randomNum + i;
}
System.out.println("Destroy the " + shipSize + " character ship hidden in the");
System.out.println("displayed line below:");
System.out.println();
String gameLine = String.join("", java.util.Collections.nCopies(gameLineLength, "-"));
theDotCom.setLocationCells(locations);
// Play current round...
boolean isAlive = true;
while (isAlive == true) {
System.out.println(gameLine);
String guess = helper.getUserInput("Enter a number from 1 to " + gameLineLength
+ " (0 to quit): --> ", "Integer", 1, gameLineLength);
int idx = Integer.parseInt(guess);
if (idx == 0) {
System.out.println("Quiting with an overall score of: " + score + " ... Bye-Bye");
alwaysReplay = false;
break;
}
idx = idx - 1;
String result = theDotCom.checkYourself(idx);
numOfGuesses++;
System.out.println(result);
if (result.equalsIgnoreCase("kill")) {
Toolkit.getDefaultToolkit().beep();
isAlive = false;
/* Tally the score dependent upon the gameLineLength... */
if (gameLineLength <= 10) { score += 5; }
else if (gameLineLength > 10 && gameLineLength <= 20) { score += 10; }
else if (gameLineLength > 20 && gameLineLength <= 30) { score += 15; }
else if (gameLineLength > 30 && gameLineLength <= 40) { score += 20; }
else { score += 25; }
gameLine = gameLine.substring(0, idx) + "x" + gameLine.substring(idx + 1);
System.out.println(gameLine);
System.out.println(numOfGuesses + " guesses were made to sink the hidden ship.");
System.out.println("Your overall score is: " + (score < 0 ? 0 : score));
}
else if (result.equalsIgnoreCase("hit")) {
gameLine = gameLine.substring(0, idx) + "x" + gameLine.substring(idx + 1);
}
if (result.equalsIgnoreCase("miss")) {
score -= 1;
}
System.out.println();
}
// Play Again? [but only if 'alwaysReplay' holds true]
if (alwaysReplay) {
String res = helper.getAnything("<< Press ENTER to play again >>\n"
+ "<< or enter 'q' to quit >>");
if (res.equalsIgnoreCase("q")) {
System.out.println("Quiting with an overall score of: " + score + " ... Bye-Bye");
break;
}
System.out.println();
}
}
}
}
GameHelper.java - The GameHelper class:
import java.util.Scanner;
public class GameHelper {
private final Scanner in = new Scanner(System.in);
public String getUserInput(String prompt, String responseType, int... minMAX) {
int min = 0, max = 0;
if (minMAX.length == 2) {
min = minMAX[0];
max = minMAX[1];
}
if (minMAX.length > 0 && min < 1 || max < 1) {
throw new IllegalArgumentException("\n\ngetUserInput() Method Error! "
+ "The optional parameters 'min' and or 'max' can not be 0!\n\n");
}
String response = "";
while (response.isEmpty()) {
if (prompt.trim().endsWith("-->")) {
System.out.print(prompt);
}
else {
System.out.println(prompt);
}
response = in.nextLine().trim();
if (responseType.matches("(?i)\\b(int|integer|float|double)\\b")) {
if (!response.matches("-?\\d+(\\.\\d+)?") ||
(responseType.toLowerCase().startsWith("int") && response.contains("."))) {
System.err.println("Invalid Entry (" + response + ")! Try again...");
response = "";
continue;
}
}
// Check entry range value if the entry is to be an Integer
if (responseType.toLowerCase().startsWith("int")) {
int i = Integer.parseInt(response);
if (i != 0 && (i < min || i > max)) {
System.err.println("Invalid Entry (" + response + ")! Try again...");
response = "";
}
}
}
return response;
}
public String getUserInput(String prompt, String responseType, boolean allowNothing, int... minMAX) {
int min = 0, max = 0;
if (minMAX.length == 2) {
min = minMAX[0];
max = minMAX[1];
}
if (minMAX.length > 0 && min < 1 || max < 1) {
throw new IllegalArgumentException("\n\ngetUserInput() Method Error! "
+ "The optional parameters 'min' and or 'max' can not be 0!\n\n");
}
String response = "";
while (response.isEmpty()) {
if (prompt.trim().endsWith("-->")) {
System.out.print(prompt);
}
else {
System.out.println(prompt);
}
response = in.nextLine().trim();
if (response.isEmpty() && allowNothing) {
return "";
}
if (responseType.matches("(?i)\\b(int|integer|float|double)\\b")) {
if (!response.matches("-?\\d+(\\.\\d+)?") ||
(responseType.toLowerCase().startsWith("int") && response.contains("."))) {
System.err.println("Invalid Entry (" + response + ")! Try again...");
response = "";
continue;
}
}
// Check entry range value if the entry is to be an Integer
if (responseType.toLowerCase().startsWith("int")) {
int i = Integer.parseInt(response);
if (i != 0 && (i < min || i > max)) {
System.err.println("Invalid Entry (" + response + ")! Try again...");
response = "";
}
}
}
return response;
}
public String getAnything(String prompt) {
if (prompt.trim().endsWith("-->")) {
System.out.print(prompt);
}
else {
System.out.println(prompt);
}
return in.nextLine().trim();
}
}
Battleship.java - The Battleship class:
import java.util.ArrayList;
public class Battleship {
private ArrayList<Integer> locationCells;
public void setLocationCells(java.util.ArrayList<Integer> loc) {
locationCells = loc;
}
// Overload Method (Java8+)
public void setLocationCells(int[] loc) {
locationCells = java.util.stream.IntStream.of(loc)
.boxed()
.collect(java.util.stream.Collectors
.toCollection(java.util.ArrayList::new));
}
/*
// Overload Method (Before Java8)
public void setLocationCells(int[] loc) {
// Clear the ArrayList in case it was previously loaded.
locationCells.clear();
// Fill the ArrayList with integer elements from the loc int[] Array
for (int i = 0; i < loc.length; i++) {
locationCells.add(loc[i]);
}
}
*/
/**
* Completely removes one supplied Integer value from all elements
* within the supplied Integer Array if it exist.<br><br>
*
* <b>Example Usage:</b><pre>
*
* {#code int[] a = {103, 104, 100, 10023, 10, 140, 2065};
* a = removeFromArray(a, 104);
* System.out.println(Arrays.toString(a);
*
* // Output will be: [103, 100, 10023, 10, 140, 2065]}</pre>
*
* #param srcArray (Integer Array) The Integer Array to remove elemental
* Integers from.<br>
*
* #param intToDelete (int) The Integer to remove from elements within the
* supplied Integer Array.<br>
*
* #return A Integer Array with the desired elemental Integers removed.
*/
public static int[] removeFromArray(int[] srcArray, int intToDelete) {
int[] arr = {};
int cnt = 0;
boolean deleteIt = false;
for (int i = 0; i < srcArray.length; i++) {
if (srcArray[i] != intToDelete) {
arr[cnt] = srcArray[i];
cnt++;
}
}
return arr;
}
public String checkYourself(int userInput) {
String result = "MISS";
int index = locationCells.indexOf(userInput);
if (index >= 0) {
locationCells.remove(index);
if (locationCells.isEmpty()) {
result = "KILL";
}
else {
result = "HIT";
}
}
return result;
}
}

Turning a Queue into a Priority Queue

In the CarWash program that I have right now there is currently a normal queue that I would like to change into a priority queue. My goal is to take one of the basic server characteristics and use that for priority but I am lost on how to do that. In previous attempts I have tried to change the normal queue into a priority queue and have ran into issues on how I am supposed to base it off a server characteristic.
public class CarWash {
public static void main(String[ ] args) {
Scanner kb = new Scanner (System.in);
System.out.println("Enter wash time: ");
int WASHTIME = kb.nextInt();
System.out.println("Enter arrival probability: ");
double ARRIVALPROB = kb.nextDouble();
System.out.println("enter time for simulation: ");
int TOTALTIME = kb.nextInt();
carWashSimulate(WASHTIME, ARRIVALPROB, TOTALTIME);
}
public static void carWashSimulate(int washTime, double arrivalProb, int totalTime) { //simulates the car wash
Queue<Integer> arrivalTimes = new LinkedList<Integer>( );
int next;
ClientGenerator arrival = new ClientGenerator(arrivalProb);
Server machine = new Server(washTime);
ExpressServer newM = new ExpressServer(washTime);
Averager waitTimes = new Averager( );
Averager lostCustomer = new Averager();
int currentSecond;
// Write the parameters to System.out.
System.out.println("Seconds to wash one car: " + washTime);
System.out.print("Probability of customer arrival during a second: ");
System.out.println(arrivalProb);
System.out.println("Total simulation seconds: " + totalTime);
// Check the precondition:
if (washTime <= 0 || arrivalProb < 0 || arrivalProb > 1 || totalTime < 0)
throw new IllegalArgumentException("Values out of range");
for (currentSecond = 0; currentSecond < totalTime; currentSecond++) {
// Simulate the passage of one second of time
// Check whether a new customer has arrived.
if (arrival.query( )){
System.out.println("Customer arrived at " + currentSecond);
if(arrivalTimes.size() <= 8){
arrivalTimes.add(currentSecond);
}
else{
System.out.println("They left, line was too long");
lostCustomer.addNumber(1);
}
// Check whether we can start washing another car.
if ((!machine.isBusy( )) && (!arrivalTimes.isEmpty( )))
{
next = arrivalTimes.remove( );
waitTimes.addNumber(currentSecond - next);
machine.start( );
System.out.println("Server started at " + currentSecond + " serving customer " + next);
}
// Subtract one second from the remaining time in the current wash cycle.
machine.reduceRemainingTime( );
} // end of for loop
// Write the summary information about the simulation.
System.out.println("Customers served: " + waitTimes.howManyNumbers( ));
if (waitTimes.howManyNumbers( ) > 0)
System.out.println("Average wait for customers served: " + waitTimes.average( ) + " sec");
System.out.println("The number of customers lost was " + lostCustomer);
}
}
}
Client Generator Class:
public class ClientGenerator {
private double probability;
// The approximate probability of query( ) returning true.
public ClientGenerator(double p) {
if ((p < 0) || (1 < p))
throw new IllegalArgumentException("Illegal p: " + p);
probability = p;
}
public void adjust(double a) {
if(a > 0 && a+probability < 1) {
probability = probability + a;
} else if (a < 0 && probability + a > 0) {
probability = probability + a;
}
}
public double getProbability() {
return probability;
}
public boolean query( ) {
return (Math.random( ) < probability);
}
}
Server Class:
public class Server {
private int secondsForService; // Seconds for a single wash
private int timeLeft; // Seconds until this Server is no longer busy
public Server(int s) {
secondsForService = s;
timeLeft =0;
}
public boolean isBusy( ) {
return (timeLeft > 0);
}
public void reduceRemainingTime( ) {
if (timeLeft > 0) timeLeft--;
}
public void start( ) {
if (timeLeft > 0)
throw new IllegalStateException("Server is already busy.");
timeLeft = secondsForService;
}
}
Averager class:
public class Averager
{
private int count; // How many numbers have been given to this averager
private double sum; // Sum of all the numbers given to this averager
public Averager( )
{
count =0;
sum = 0;
}
public void addNumber(double value)
{
if (count == Integer.MAX_VALUE)
throw new IllegalStateException("Too many numbers");
count++;
sum += value;
}
public double average( )
{
if (count == 0)
return Double.NaN;
else
return sum/count;
}
public int howManyNumbers( )
{
return count;
}
}
The question seems to be about how to configure the priority rules employed by a java.util.PriorityQueue. That's relatively straightforward. Depending on which constructor you use to instantiate one, PriorityQueue relies either on the natural order of its elements (see Comparable) or on the order defined by a specified Comparator. Whenever such a queue contains any elements, its head is the least with respect to the operative ordering, or among the least if there are multiple elements such that no other element is less.
In comments you clarified
my goal is to implement some way of randomly assigning a value that represents the type of car, which will then prioritize the luxury car before the other cars.
Note well that PriorityQueue uses the properties of the enqueued objects to establish their relative order. Right now you are enqueuing integer arrival times, which don't confer an ability to distinguish between classes of car. If you want to carry more information about each vehicle that arrives then you would probably want to create a new class for that, maybe something like this:
class ClientArrival {
enum Category { NORMAL, LUXURY }
Category category;
int arrivalTime;
// ...
}
You would then be able to create one or more implementations of Comparator<ClientArrival> to use to define the priority rule for a PriorityQueue<ClientArrival>. For example,
class LuxuryFirstComparator implements Comparator<ClientArrival> {
int compare(ClientArrival o1, ClientArrival o2) {
if (o1.getCategory() == o2.getCategory()) {
// ... order based on arrival time ...
} else if (o1.getCategory() == ClientArrival.Category.LUXURY) {
return -1;
} else {
return 1;
}
}
}
One might set up a PriorityQueue<ClientArrival> using that to determine priority via
Queue<ClientArrival> arrivals = new PriorityQueue<>(new LuxuryFirstComparator());

Sort Algorithm Java

Right now i'm struggeling with a basic algorithm, that shall sort a linked list. I have two additional linked lists (in the beginning empty), in which i can copy the Integer Objects of the first linkedlist.
My problem is, that all of my tries simply doesn't work. In the copied example at the bottom, it goes through both of the while loops, but i don't know how to loop everything, until everything is sorted in the third linked list (zug3.zug3). Also i shall compare the actual smallest value of zug1 to the smallest of zug2 and then continue sorting in the list where the value is smaller. That is not possible at the start of sorting, because if i wanna getSmallest() of an empty List, it will get a null pointer exception.
I'm tryin this now since three days with different, for-loops, while-loops, if-else sentences but i don't find out, how to make it work accurate.
Please help!
Example of the Programm:
public class Abstellgleis {
LinkedList<Integer> zug1 = new LinkedList<Integer>();
void initialize() {
for (int i = 0; i <15;i++) {
Random zahl = new Random();
int integer = zahl.nextInt(15);
zug1.add(integer);
}
}
public void wagenAnkoppeln(int i) {
zug1.addFirst(i);
}
int wagenAbkoppeln() {
int waggonNummer = zug1.getFirst();
zug1.removeFirst();
return waggonNummer;
}
int getSmallest() {
int smallest = zug1.size();
for( int i =1; i <zug1.size()-1; i++)
{
if(zug1.get(i) < smallest )
{
//int smallest = integers.get(Oedipus);
smallest = zug1.get(i);
}
}
return smallest;
}
}
public class Rangiergleis {
LinkedList<Integer> zug2 = new LinkedList<Integer>();
void waggonAnkoppeln(int i) {
zug2.addFirst(i);
}
int waggonAbkoppeln() {
int waggonNummer = zug2.getFirst();
zug2.removeFirst();
return waggonNummer;
}
int getSmallest() {
int smallest = 100;
for (int i = 0; i < zug2.size() - 1; i++) {
if (zug2.get(i) < smallest) {
smallest=zug2.get(i);
}
}
return smallest;
}
}
public class Zuggleis {
LinkedList<Integer> zug3 = new LinkedList<Integer>();
void waggonAnkoppeln(int i) {
zug3.addLast(i);
}
}
public class Steuerung {
public static void main(String[] args) {
Abstellgleis zug1 = new Abstellgleis();
zug1.initialize();
Rangiergleis zug2 = new Rangiergleis();
Zuggleis zug3 = new Zuggleis();
System.out.println("Abstellgleis:" + zug1.zug1);
System.out.println("Rangiergleis: " + zug2.zug2);
System.out.println("Abstellgleis: " + zug3.zug3);
while (!zug1.zug1.isEmpty()) {
if (zug1.zug1.getFirst() != zug1.getSmallest()) {
zug2.waggonAnkoppeln(zug1.zug1.getFirst());
System.out.println("Vom Abstellgleis wurde Wagen " +
zug1.zug1.getFirst() + " aufs Rangiergleis bewegt");
zug1.zug1.removeFirst();
}
else if (zug1.zug1.getFirst() == zug1.getSmallest()) {
zug3.waggonAnkoppeln(zug1.zug1.getFirst());
System.out.println(zug1.zug1.getFirst() + "wurde aufs Zuggleis bewegt");
zug1.zug1.removeFirst();
}
System.out.println("Abstellgleis:" + zug1.zug1);
System.out.println("Rangiergleis: " + zug2.zug2);
System.out.println("Zuggleis: " + zug3.zug3);
}
while (!zug2.zug2.isEmpty()) {
if (zug2.zug2.getFirst() != zug2.getSmallest()) {
zug1.wagenAnkoppeln(zug2.zug2.getFirst());
System.out.println("Vom Rangiergleis wurde Wagen " +
zug2.zug2.getFirst() + " aufs Abstellgleis bewegt");
zug2.zug2.removeFirst();
}
else if (zug2.zug2.getFirst() == zug2.getSmallest()) {
zug3.waggonAnkoppeln(zug2.zug2.getFirst());
System.out.println(zug2.zug2.getFirst() + " wurde vom Rangiergleis aufs Zuggleis bewegt");
zug2.zug2.removeFirst();
}
System.out.println("Abstellgleis:" + zug1.zug1);
System.out.println("Rangiergleis: " + zug2.zug2);
System.out.println("Zuggleis: " + zug3.zug3);
}
if (zug1.zug1.isEmpty()) {
while (!zug2.zug2.isEmpty())
if (zug2.zug2.getFirst() != zug2.getSmallest()) {
zug1.wagenAnkoppeln(zug2.zug2.getFirst());
System.out.println("Vom Abstellgleis wurde Wagen " +
zug2.zug2.getFirst() + " aufs Rangiergleis bewegt");
zug2.zug2.removeFirst();
}
else if (zug2.zug2.getFirst() == zug2.getSmallest()) {
zug3.waggonAnkoppeln(zug2.zug2.getFirst());
System.out.println(zug2.zug2.getFirst() + "wurde aufs Zuggleis bewegt");
zug2.zug2.removeFirst();
}
System.out.println("Abstellgleis:" + zug1.zug1);
System.out.println("Rangiergleis: " + zug2.zug2);
System.out.println("Zuggleis: " + zug3.zug3);
}
}
}
Your getSmallest methods in Abstellgleis and Rangiergleis don’t look right. In the first you start by setting smallest to zug1.size(). First time when the size is 15 this is probably fine, but as the Zug grows shorter, there may come a point when the size is smaller than the smallest element, and then your method will give the wrong result. In Rangiergleis you are initializing to 100, that’s sounder. In both methods you are missing the last element. For example in Abstellgleis.getSmallest():
for( int i =1; i <zug1.size()-1; i++)
This is in fact missing both the first and the last element. Elements are indexed 0 through zug1.size() - 1, so it should be one of the two following:
for (int i = 0; i < zug1.size(); i++) {
for (int i = 0; i <= zug1.size() - 1; i++) {
The former would be conventional. If you are sure there is at least one wagon in the train, you may of course initialize smallest to zug1.get(0) and the have the loop run from 1 (this could have been what you intended).
In Rangiergleis.getSmallest() your loop runs from 0 as it should, but is missing the last element in the same way as in Abstellgleis.

Is there a Java library that will create a number range from a list of numbers?

I am creating a table of contents, and what I have is a Map of product numbers to pages. So an entry might look like this:
ABC123 => [59, 58, 57, 19, 36, 15, 33, 34, 13, 39, 11, 37, 38, 21, 20, 40, 63, 60, 45, 46, 22, 23, 24, 26, 3, 2, 10, 1, 7, 6, 5, 4, 8]
What I want to get from this is:
1-8,10,11,13,15,19-24,26,33,34,36-38,40,45,46,57-60
I can code this of course, but I figured that someone else has already solved this problem. My Googling has yielded naught.
I appreciate any help you can offer, as always!
You could collect the numbers into a sorted set and then iterate over the numbers.
Quick and dirty example:
SortedSet<Integer> numbers = new TreeSet<Integer>();
numbers.add( 1 );
numbers.add( 2 );
numbers.add( 3 );
numbers.add( 6 );
numbers.add( 7 );
numbers.add( 10 );
Integer start = null;
Integer end = null;
for( Integer num : numbers ) {
//initialize
if( start == null || end == null ) {
start = num;
end = num;
}
//next number in range
else if( end.equals( num - 1 ) ) {
end = num;
}
//there's a gap
else {
//range length 1
if( start.equals( end )) {
System.out.print(start + ",");
}
//range length 2
else if ( start.equals( end - 1 )) {
System.out.print(start + "," + end + ",");
}
//range lenth 2+
else {
System.out.print(start + "-" + end + ",");
}
start = num;
end = num;
}
}
if( start.equals( end )) {
System.out.print(start);
}
else if ( start.equals( end - 1 )) {
System.out.print(start + "," + end );
}
else {
System.out.print(start + "-" + end);
}
Yields: 1-3,6,7,10
Apache Commons has the IntRange type that you can use. Unfortunately I didn't find a good corresponding set of utilities to create them. Here's the basic approach you could use:
//create a list of 1-integer ranges
List<IntRange> ranges = new LinkedList<IntRange>();
for ( int pageNum : pageNums ) {
ranges.add(new IntRange(pageNum));
}
//sort the ranges
Collections.sort(ranges, new Comparator<IntRange>() {
public int compare(IntRange a, IntRange b) {
return Integer.valueOf(a.getMinimumInteger()).compareTo(b.getMinimumInteger());
}
});
List<IntRange> output = new ArrayList<IntRange>();
if ( ranges.isEmpty() ) {
return output;
}
//collapse consecutive ranges
IntRange range = ranges.remove(0);
while ( !ranges.isEmpty() ) {
IntRange nextRange = ranges.remove(0);
if ( range.getMaximumInteger() == nextRange.getMinimumInteger() - 1 ) {
range = new IntRange(range.getMinimumInteger(), nextRange.getMaximumInteger());
} else {
output.add(range);
range = nextRange;
}
}
output.add(range);
Alternatively you could skip the first step and create the ranges directly from the sorted list of page numbers.
Edit: A better description:
I had to deal with something similar to support a sorted collection of finite ranges, I used a mix of Google's Guava Range class and binary search to insert the element at the corresponding range or create a new singleton Range (A range with 1 element), eventually with more inserts the ranges have chances of expanding (Or shrinking/splitting in case of removal), removal is pretty fast because locating the corresponding range where the element is uses a binary search:
import com.google.common.collect.DiscreteDomains;
import com.google.common.collect.Lists;
import com.google.common.collect.Range;
import com.google.common.collect.Ranges;
import java.util.Collection;
import java.util.List;
public class IntRangeCollection
{
private int factor=10;
private List<Range<Integer>> rangeList=null;
public IntRangeCollection()
{
rangeList=Lists.newArrayListWithExpectedSize(1000);
}
public IntRangeCollection(final int size)
{
rangeList=Lists.newArrayListWithExpectedSize(size);
}
public IntRangeCollection(final int size, final int factor)
{
rangeList=Lists.newArrayListWithExpectedSize(size);
this.factor=factor;
}
protected IntRangeCollection(final List<Range<Integer>> rangeList)
{
this.rangeList=rangeList;
}
public static IntRangeCollection buildIntRangesCollectionFromArrays(final List<Integer[]> arrays)
{
final List<Range<Integer>> rangeList=Lists.newArrayListWithCapacity(arrays.size());
for(Integer[] range : arrays){
rangeList.add(range.length == 1 ? Ranges.singleton(range[0]) : Ranges.closed(range[0], range[1]));
}
return new IntRangeCollection(rangeList);
}
public boolean addElements(final Collection<Integer> elements)
{
boolean modified=false;
for(Integer element : elements){
modified=addElement(element) || modified;
}
return modified;
}
public boolean removeElements(final Collection<Integer> elements)
{
boolean modified=false;
for(Integer element : elements){
modified=removeElement(element) || modified;
}
return modified;
}
public boolean addElement(final Integer element)
{
final Range<Integer> elementRange=Ranges.singleton(element);
if(rangeList.isEmpty()){
rangeList.add(elementRange);
} else{
int
start=0, mid=0,
end=rangeList.size() - 1;
Range<Integer> midRange=null;
while(start<=end){
mid=(start + end) / 2;
midRange=rangeList.get(mid);
if(midRange.contains(element)){
return false;
} else if(testLinkable(midRange, element)){
rangeList.set(mid, midRange.span(elementRange));
if(mid>0){
final Range<Integer> a=rangeList.get(mid - 1);
if(testLinkable(a, midRange)){
rangeList.set(mid - 1, a.span(midRange));
rangeList.remove(mid);
mid--;
}
}
if(mid<rangeList.size() - 1){
final Range<Integer> b=rangeList.get(mid + 1);
if(testLinkable(midRange, b)){
rangeList.set(mid, midRange.span(b));
rangeList.remove(mid + 1);
}
}
return true;
} else if(midRange.lowerEndpoint().compareTo(element)<0){
start=mid + 1;
} else{
end=mid - 1;
}
}
//noinspection ConstantConditions
rangeList.add(midRange.lowerEndpoint().compareTo(element)<0 ? mid + 1 : mid, elementRange);
}
return true;
}
public boolean removeElement(final Integer element)
{
final Range<Integer> elementRange=Ranges.singleton(element);
if(rangeList.isEmpty()){
rangeList.add(elementRange);
} else{
int
start=0, mid,
end=rangeList.size() - 1;
while(start<=end){
mid=(start + end) / 2;
final Range<Integer> midRange=rangeList.get(mid);
if(midRange.contains(element)){
final Integer
lower=midRange.lowerEndpoint(),
upper=midRange.upperEndpoint();
if(lower.equals(upper)){
rangeList.remove(mid);
} else if(lower.equals(element)){
rangeList.set(mid, Ranges.closed(element + 1, upper));
} else if(upper.equals(element)){
rangeList.set(mid, Ranges.closed(lower, element - 1));
} else{
rangeList.set(mid, Ranges.closed(element + 1, upper));
rangeList.add(mid, Ranges.closed(lower, element - 1));
}
return true;
} else if(midRange.lowerEndpoint().compareTo(element)<0){
start=mid + 1;
} else{
end=mid - 1;
}
}
}
return false;
}
public List<Integer> getElementsAsList()
{
final List<Integer> result=Lists.newArrayListWithExpectedSize(rangeList.size() * factor);
for(Range<Integer> range : rangeList){
result.addAll(range.asSet(DiscreteDomains.integers()));
}
return result;
}
public List<Integer[]> getRangesAsArray()
{
final List<Integer[]> result=Lists.newArrayListWithCapacity(rangeList.size());
for(Range<Integer> range : rangeList){
final Integer
lower=range.lowerEndpoint(),
upper=range.upperEndpoint();
result.add(lower.equals(upper) ? new Integer[]{lower} : new Integer[]{lower,upper});
}
return result;
}
public int getRangesCount()
{
return rangeList.size();
}
private boolean testLinkable(final Range<Integer> range, final Integer element)
{
return Ranges.closed(range.lowerEndpoint() - 1, range.upperEndpoint() + 1).contains(element);
}
private boolean testLinkable(final Range<Integer> a, final Range<Integer> b)
{
return Ranges.closed(a.lowerEndpoint() - 1, a.upperEndpoint() + 1).isConnected(b);
}
#Override
public String toString()
{
return "IntRangeCollection{" +
"rangeList=" + rangeList +
'}';
}
public static void main(String[] args)
{
final int MAX_NUMBER=1000;
final long startMillis=System.currentTimeMillis();
final IntRangeCollection ranges=new IntRangeCollection();
for(int i=0; i<MAX_NUMBER; i++){
//noinspection UnsecureRandomNumberGeneration
ranges.addElement((int) (Math.random() * MAX_NUMBER));
}
System.out.println(MAX_NUMBER + " contained in " + ranges.rangeList.size() + " ranges done in " + (System.currentTimeMillis() - startMillis) + "ms");
System.out.println(ranges);
for(int i=0; i<MAX_NUMBER / 4; i++){
//noinspection UnsecureRandomNumberGeneration
ranges.removeElement((int) (Math.random() * MAX_NUMBER));
}
System.out.println(MAX_NUMBER + " contained in " + ranges.rangeList.size() + " ranges done in " + (System.currentTimeMillis() - startMillis) + "ms");
System.out.println(ranges);
}
}
You can use Arrays.sort() and find neighbouring duplicates/ranges. However I suspect TreeSet may be simpler to use.
This is a good example, it shows a simple way to accomplish this.

Making change recursively: How do I modify my algorithm to print all combinations?

I have an algorithm that recursively makes change in the following manner:
public static int makeChange(int amount, int currentCoin) {
//if amount = zero, we are at the bottom of a successful recursion
if (amount == 0){
//return 1 to add this successful solution
return 1;
//check to see if we went too far
}else if(amount < 0){
//don't count this try if we went too far
return 0;
//if we have exhausted our list of coin values
}else if(currentCoin < 0){
return 0;
}else{
int firstWay = makeChange(amount, currentCoin-1);
int secondWay = makeChange(amount - availableCoins[currentCoin], currentCoin);
return firstWay + secondWay;
}
}
However, I'd like to add the capability to store or print each combination as they successfully return. I'm having a bit of a hard time wrapping my head around how to do this. The original algorithm was pretty easy, but now I am frustrated. Any suggestions?
CB
Without getting into the specifics of your code, one pattern is to carry a mutable container for your results in the arguments
public static int makeChange(int amount, int currentCoin, List<Integer>results) {
// ....
if (valid_result) {
results.add(result);
makeChange(...);
}
// ....
}
And call the function like this
List<Integer> results = new LinkedList<Integer>();
makeChange(amount, currentCoin, results);
// after makeChange has executed your results are saved in the variable "results"
I don't understand logic or purpose of above code but this is how you can have each combination stored and then printed.
public class MakeChange {
private static int[] availableCoins = {
1, 2, 5, 10, 20, 25, 50, 100 };
public static void main(String[] args) {
Collection<CombinationResult> results = makeChange(5, 7);
for (CombinationResult r : results) {
System.out.println(
"firstWay=" + r.getFirstWay() + " : secondWay="
+ r.getSecondWay() + " --- Sum=" + r.getSum());
}
}
public static class CombinationResult {
int firstWay;
int secondWay;
CombinationResult(int firstWay, int secondWay) {
this.firstWay = firstWay;
this.secondWay = secondWay;
}
public int getFirstWay() {
return this.firstWay;
}
public int getSecondWay() {
return this.secondWay;
}
public int getSum() {
return this.firstWay + this.secondWay;
}
public boolean equals(Object o) {
boolean flag = false;
if (o instanceof CombinationResult) {
CombinationResult r = (CombinationResult) o;
flag = this.firstWay == r.firstWay
&& this.secondWay == r.secondWay;
}
return flag;
}
public int hashCode() {
return this.firstWay + this.secondWay;
}
}
public static Collection<CombinationResult> makeChange(
int amount, int currentCoin) {
Collection<CombinationResult> results =
new ArrayList<CombinationResult>();
makeChange(amount, currentCoin, results);
return results;
}
public static int makeChange(int amount, int currentCoin,
Collection<CombinationResult> results) {
// if amount = zero, we are at the bottom of a successful recursion
if (amount == 0) {
// return 1 to add this successful solution
return 1;
// check to see if we went too far
} else if (amount < 0) {
// don't count this try if we went too far
return 0;
// if we have exhausted our list of coin values
} else if (currentCoin < 0) {
return 0;
} else {
int firstWay = makeChange(
amount, currentCoin - 1, results);
int secondWay = makeChange(
amount - availableCoins[currentCoin],
currentCoin, results);
CombinationResult resultEntry = new CombinationResult(
firstWay, secondWay);
results.add(resultEntry);
return firstWay + secondWay;
}
}
}
I used the following:
/**
* This is a recursive method that calculates and displays the combinations of the coins included in
* coinAmounts that sum to amountToBeChanged.
*
* #param coinsUsed is a list of each coin used so far in the total. If this branch is successful, we will add another coin on it.
* #param largestCoinUsed is used in the recursion to indicate at which coin we should start trying to add additional ones.
* #param amountSoFar is used in the recursion to indicate what sum we are currently at.
* #param amountToChange is the original amount that we are making change for.
* #return the number of successful attempts that this branch has calculated.
*/private static int change(List<Integer> coinsUsed, Integer currentCoin, Integer amountSoFar, Integer amountToChange)
{
//if last added coin took us to the correct sum, we have a winner!
if (amountSoFar == amountToChange)
{
//output
System.out.print("Change for "+amountToChange+" = ");
//run through the list of coins that we have and display each.
for(Integer count: coinsUsed){
System.out.print(count + " ");
}
System.out.println();
//pass this back to be tallied
return 1;
}
/*
* Check to see if we overshot the amountToBeChanged
*/
if (amountSoFar > amountToChange)
{
//this branch was unsuccessful
return 0;
}
//this holds the sum of the branches that we send below it
int successes=0;
// Pass through each coin to be used
for (Integer coin:coinAmounts)
{
//we only want to work on currentCoin and the coins after it
if (coin >= currentCoin)
{
//copy the list so we can branch from it
List<Integer> copyOfCoinsUsed = new ArrayList<Integer>(coinsUsed);
//add on one of our current coins
copyOfCoinsUsed.add(coin);
//branch and then collect successful attempts
successes += change(copyOfCoinsUsed, coin, amountSoFar + coin, amountToChange);
}
}
//pass back the current
return successes;
}

Categories

Resources