Java: EmptyQueueException being thrown before processing method - java

I'm working on a stock exchange program as a project and so far I've gotten about 98% of it done, the only issue I am having is when I am trying to sell more shares than a current day holds. So for example, I buy 20 shares for $30 each on day 1 and 40 shares for $20 each on day 2. I then input saying I want to sell 30 shares for $20 each. What the code is supposed to do is sell all the shares from day one, and then sell 10 shares from day 2. However, what I'm getting is an EmptyQueueException being thrown. I feel that my sellShares method might be having the error when it goes into the final else statement with the while loop. However I cannot wrap my mind around what might be the error. I've been staring the code down for quite some time and I can't seem to figure out a solution to this. Some assistance on this would greatly be appreciated. The following code is from my main class and the CircleArrayQueue class:
import java.util.Scanner;
import java.lang.Integer;
public class StockTran {
String command = "";
int gain = 0;
int totalPrice = 0; // totalPrice variable will keep of gain or loss of shares being sold
int shareTracker = 0; // shareTracker variable will keep track of shares being bought and sold
String[] stockParts = null;
CircleArrayQueue Q;
boolean quit = false;
public StockTran(String inputCommand) {
try {
Q = new CircleArrayQueue(32);
Scanner conReader = new Scanner(System.in);
this.command = inputCommand.toLowerCase();
this.stockParts = command.split("\\s"); // splits the input into three parts
while (quit == false) { // will loop until user says "q" to quit program
if (this.stockParts[0].equals("q")) { // ends transaction and terminates program
System.out.println("Share trading successfully terminated.");
quit = true;
System.exit(0); // exits the program
}
if (this.stockParts == null || this.stockParts.length > 3) {
System.out.println("That is an invalid input. Please try again.");
}
if (stockParts[0].equals("b")) { // checks to see if it is a buying of shares
int shares = Integer.parseInt(stockParts[1]); // stores share amount
int value = Integer.parseInt(stockParts[2]); // stores selling value
buyShares(shares, value); // calls buyShares method and adds share to queue
}
else if (stockParts[0].equals("s")) { // checks to see if it is a selling of shares
int shares = Integer.parseInt(stockParts[1]);
int value = Integer.parseInt(stockParts[2]);
sellShares(shares, value); // calls sellShares method
}
else if (stockParts[0].equals("c")) { // checks to see if it is capital gain
gain = capitalGain(); // calls capitalGain and calculates net gain
System.out.println("Capital gain is " + gain);
}
else {
System.out.println("That is an invalid input. Please try again."); // any other input is invalid
}
System.out.println("Enter your next command, or press 'q' to quit: ");
command = conReader.nextLine().toLowerCase();
stockParts = command.split("\\s");
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void buyShares(int shareAmount, int sharePrice) { // takes in share total and values for each share
shareTracker = shareTracker + shareAmount; // adds to amount of shares bought
Node temp = new Node(shareAmount, sharePrice); // stores values into node
try {
Q.enqueue(temp); // enqueues the node into the CircularQueue
} catch (FullQueueException e) {
e.printStackTrace();
}
}
public void sellShares(int shareAmount, int sharePrice) throws Exception {
Node temp = new Node(); // stores values into node
int tempShare = 0;
try {
temp = Q.front(); // gets the first node from CircleArrayQueue and stores it in temporary node
int share = temp.getShare();
int price = temp.getPrice();
System.out.println(Q.size());
if (shareAmount > shareTracker) { // throws exception if trying to sell more shares than purchased
throw new Exception ("You don't have that many shares to sell.");
}
else if (share > shareAmount) { // checks to see if first node has a larger share amount or less
temp.setShare(share - shareAmount); // will decrease amount sold from the first days share
shareTracker = shareTracker - shareAmount;
totalPrice = shareAmount * (sharePrice - price) + totalPrice; // calculates total profit or loss
}
else if (share == shareAmount) {
Q.dequeue();
shareTracker = shareTracker - shareAmount; // updates shareTracker to show how many shares are remaining
totalPrice = shareAmount * (sharePrice - price) + totalPrice;
}
else {
while (shareAmount != tempShare) { // will loop until it sells total share amount user wanted
Node temp2 = Q.dequeue(); // removes another node from CircleArrayQueue
int newShare = temp2.getShare();
int newPrice = temp2.getPrice();
tempShare = tempShare + newShare; // adds the shares together to check if while loop condition still holds
totalPrice = shareAmount * (sharePrice - newPrice) + totalPrice;
sellShares(shareAmount - tempShare, sharePrice); // recursively calls sellShares on new amount of shares
}
}
} catch (EmptyQueueException e) {
e.printStackTrace();
}
}
public int capitalGain() { // returns the total net gain or loss in share trading
return totalPrice;
}
public static void main(String[] args) {
String inputCommand = "";
Scanner mainReader = new Scanner(System.in);
System.out.println("Enter 'b' to purchase share, 's' to sell share, 'c' for capital gain, or 'q' to quit: ");
inputCommand = mainReader.nextLine();
StockTran tran = new StockTran(inputCommand);
}
}
public class CircleArrayQueue implements Queue {
protected Node Q[]; // initializes an empty array for any element type
private int MAX_CAP = 0; // initializes the value for the maximum array capacity
private int f, r;
public CircleArrayQueue(int maxCap) {
MAX_CAP = maxCap;
Q = new Node[MAX_CAP]; // sets Q to be a specific maximum size specified
f = 0; // sets front value to be 0
r = 0; // sets rear value to be 0;
}
public int size() {
return (MAX_CAP - f + r) % MAX_CAP; // returns the size of the CircularArrayQueue
}
public boolean isEmpty() { // if front and rear are of equal value, Queue is empty
return f == r;
}
public Node front() throws EmptyQueueException { // method to get the front value of the CircularArrayQueue
if (isEmpty()) throw new EmptyQueueException("Queue is empty.");
return Q[f]; // returns object at front of CircularArrayQueue
}
public Node dequeue() throws EmptyQueueException { // method to remove from the front of the CircularArrayQueue
if (isEmpty()) throw new EmptyQueueException("Queue is empty.");
Node temp = Q[f]; // stores front object in local variable
Q[f] = null; // sets the value to be null in the array
f = (f + 1) % MAX_CAP; // sets the new front value to be this
return temp; // returns the object that was originally in the front
}
public void enqueue(Node element) throws FullQueueException { // method to add to the end of the CircualarArrayQueue
if (size() == MAX_CAP - 1) throw new FullQueueException("Queue has reached maximum capacity.");
Q[r] = element; // stores the new element at the rear of array
r = (r + 1) % MAX_CAP; // sets the new rear value to be the location after element insertion
}
}

Your sellShares routine calls front() without checking to see if there is anything in the queue. If the queue is empty(), you get your exception.

Related

Using Queue Data Structure to solve problem

My code involves me to calculate the amount of time required for a person in a certain position in a queue to buy burgers with the index of the burgers array showing an integer of the amount of burgers a person ordered. Each time a person at the head buys a burger they are taken to the tail and have one burger removed and 1 time value added. I have to find the amount of time it takes for the person at what position I inputted to find the time to buy all of their burgers.
Here is my code:
import java.util.Queue;
public class A4Q2 {
public int calcTimeReqToBuyBurgers(int[] burgers, int p)
{
Queue<Integer> q = new LinkedList<Integer>();
q.addAll(burgers);
int time = 0;
int i = 0;
numBurg = burgers[p];
while(numBurg > 0) {
int x = q.remove();
x--;
if(x != 0) {
q.add(x);
}
time +=1;
}
return time;
}
public static void main(String[] args) {
A4Q2 ex = new A4Q2();
}
}
I do not know where to go from here or if I am even doing this right. I know my question might look a little messy but I am still new to this website. Can someone help me?
You need not use queue for this, and also in your solution it seems that you are not reducing numBurg(which makes the queue run infinitely) and also you need to keep track of index of numBurg. Then I guess it would work. Below is my solution
int time = 0;
int p_value = burgers[p];
for(int i=0;i<burgers.length;i++){
if(burgers[p] > p_value) time+=p_value;
else time+=burgers[p];
if(i == p) p_value-=1;
}
return time;
Explanation: you need to reduce burgers[p] value from all the elements in array until you reach p but after that you can stop so the prior elements will have only p-1 reduced in them as we can stop after we reach p. and also if someone needs less burgers they will only participate until they reach 0. This would be O(n) with no extra space solution.
public int calcTimeReqToBuyBurgers(int[] burgers, int p)
{
Queue<Integer> q = new LinkedList<Integer>();
for (int burger : burgers){
q.add(burger);}
int time = 0;
int i = 0;
int numBurg = burgers[p];
while(numBurg > 0) {
int curr = q.remove();
if(curr == 1){time ++;}
if(curr > 1){
q.add(curr);
time ++;
}
if(curr == numBurg){
q.add(curr -- );
numBurg --;
time ++;
}
}
return time;
}
public static void main(String[] args) {
A4Q2 ex = new A4Q2 ();
int [] burgers= new int [] {3,2,4,1};
int time=ex.calcTimeReqToBuyBurgers(burgers,1);
System.out.println("time "+time);
}

Why is my code skipping over certain elements in a txt file?

I'm currently working on an assignment that requires me to build a small database for an imaginary toy company. They sell four different types of toys, with all of their toy's data in a single text file. Different toys have varying attributes to them.
My job is to read every line of the text file, find out what toy it is based on it's serial number, create a new instance of that toy, and load it into an array list of Toys.
Here are some examples of a line in the .txt file for every type of toy.
Animal
2835360879;Cow;Game Assassin;19.52;3;7;Plastic;M
Puzzle
4353818087;Eight queens puzzle;Gamescape;15.69;5;6;C
Figure
1147205649;Ninja Turtles;Gamezoid;46.15;10;6;A
Board Game
7235647474;13 Dead End Drive;Game Assassin;55.18;10;9;1-8;Emeli Davis
The following method is in charge of parsing through the text file, to create new instances of Toy, and to add them into the Array List
public void loadData() {
try {
File dataFile = new File("res/toys.txt");
if (dataFile.createNewFile()) {
System.out.println("Data file created!");
}
else {
Scanner readData = new Scanner(dataFile);
readData.useDelimiter(";"); // sets semicolons as the delimiter
while(readData.hasNext()) {
sn = readData.nextDouble(); // reads the next double (serial number) on each line
category = categoryHandler(sn); // setting and returning value for category based on the serial number
nm = readData.next(); // initializes the name of the toy
brd = readData.next();
prc = readData.nextDouble();
availableCnt = readData.nextInt();
ageApp = readData.nextInt();
// creates a new object, dependant on the category
switch(category) {
case "Figures":
char classification = readData.next().charAt(0);
Figure figures = new Figure(sn, nm, brd, prc, availableCnt, ageApp, classification);
data.add(figures);
break;
case "Animals":
String material = readData.next();
char size = readData.next().charAt(0);
Animal animals = new Animal(sn, nm, brd, prc, availableCnt, ageApp, material, size);
data.add(animals);
break;
case "Puzzles":
char puzzleType = readData.next().charAt(0);
Puzzle puzzles = new Puzzle(sn, nm, brd, prc, availableCnt, ageApp, puzzleType);
data.add(puzzles);
break;
case "Board Games":
String playerCount = readData.next(); // holds the player count as a string
int minPlayers = Integer.parseInt(playerCount.substring(0, 1)); // holds the first integer
int maxPlayers = Integer.parseInt(playerCount.substring(playerCount.length() - 1, playerCount.length())); // holds the second integer
String designers = "";
BoardGame boardGames = new BoardGame(sn, nm, brd, prc, availableCnt, ageApp, minPlayers, maxPlayers, designers);
data.add(boardGames);
break;
default:
System.out.println("Invalid toy type selected!");
}
if (readData.hasNext()) {
readData.nextLine(); // skips to the next line if there's a line to skip to
}
}
readData.close();
}
}
catch (IOException e) {
System.out.println("An error occured.");
e.printStackTrace();
}
}
The following method is in charge of categorizing the toy type.
public String categoryHandler(double serialNumber) {
String serialNumCheck = Double.toString(serialNumber); // converting serial number to string to allow the first digit to be checked
double firstDigit;
// setting up the first digit, checking to see if it's zero.
if (serialNumCheck.length() == 12) {
firstDigit = 0;
}
else {
firstDigit = Double.parseDouble(Double.toString(serialNumber).substring(0, 1));
}
// conditionals
if (firstDigit == 0 || firstDigit == 1) {
category = "Figures";
}
else if (firstDigit == 2 || firstDigit == 3) {
category = "Animals";
}
else if (firstDigit == 4 || firstDigit == 5 || firstDigit == 6) {
category = "Puzzles";
}
else if (firstDigit == 7 || firstDigit == 8 || firstDigit == 9) {
category = "Board Games";
}
// this condition should not be possible to achieve, unless the first digit is negative. Still have it just in case.
else {
System.out.println("Invalid serial number created!");
category = "";
}
return category;
}
After running the loadData() method, my array list only contains 128 toys, instead of the expected 225. Almost all of them are categorized properly, but a few toys are not, at seemingly at random indices.
I suspect it has something to with readData.nextLine() at the end of the loadData() method. The problem is that when I remove the line, the program throws a NullPointerException as there nothing left to scan on the current line, so no Toy can be created.
At this point I'm fairly lost as to what is causing this bug.
Some guidance would be appreciated.

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());

NullPointerException being thrown and program doesn't terminate as expected

I am creating a stock exchange type program and so far I've got the input working so that it takes the command from the user properly. However what it does to the input isn't working as expected. The first thing I am confused on is why it is throwing me a NullPointerException when I run the code.
Basically the program will take an input of 3 things followed by whitespace between them. So for instance I want to buy 30 shares at $20 each, I would type the input like the following:
b 30 20
It would then split it into 3 parts and store it into an array. After that it will compare the first index of the array to see what the program should do, in this example it will buy so it will invoke the buy method and store the share amount and share value into my CircularArrayQueue.
It gets the share value and share amount stored into a Node, but when I try to invoke the enqueue method with my CirularArrayQueue to store the Node into the Queue, it gives me a NullPointerException.
Another issue I've been running into was the termination for the program. The program is supposed to terminate when it sees that the first index value of the input is "q". I've made a while loop stating that it will loop when the boolean quit is false. Then within the while loop I've made an if statement checking to see if the value of stockParts[0] is "q". If so it would change the value of quit to be true so it can end the loop, but for some reason it isn't terminating and it is still looping.
I've been scratching my head on these issues for a couple hours but I cannot seem to find the root of the problem. Could someone please assist me on this? The following is the code from my main class and the CircularArrayQueue class:
import java.util.Scanner;
import java.lang.Integer;
public class StockTran {
String command = "";
String[] stockParts = null;
CircleArrayQueue Q;
boolean quit = false;
public StockTran(String inputCommand) {
try {
Scanner conReader = new Scanner(System.in);
this.command = inputCommand.toLowerCase();
this.stockParts = command.split("\\s"); // splits the input into three parts
buyShares(Integer.parseInt(stockParts[1]), Integer.parseInt(stockParts[2])); //testing purpose only
while (quit == false) {
if (this.stockParts[0] == "q") { // ends transaction and terminates program
System.out.println("Share trading successfully cancelled.");
quit = true;
}
if (this.stockParts == null || this.stockParts.length > 3) {
throw new Exception("Bad input.");
}
if (stockParts[0] == "b") { // checks to see if it is a buying of shares
int shares = Integer.parseInt(stockParts[1]); // stores share amount
int value = Integer.parseInt(stockParts[2]); // stores selling value
buyShares(shares, value); // calls buyShares method and adds share to queue
}
else if (stockParts[0] == "s") { // checks to see if it is a selling of shares
int shares = Integer.parseInt(stockParts[1]); // stores share amount
int value = Integer.parseInt(stockParts[2]); // stores selling value
sellShares(shares, value); // calls sellShares method
}
else if (stockParts[0] == "c") { // checks to see if it is capital gain
capitalGain(); // calls capitalGain and calculates net gain
}
System.out.println("Enter your next command or press 'q' to quit: ");
command = conReader.nextLine().toLowerCase();
stockParts = command.split("\\s");
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void buyShares(int shareAmout, int shareValue) { // takes in share total and values for each share
Node temp = new Node(shareAmout, shareValue); // stores values into node
try {
Q.enqueue(temp); // enqueues the node into the CircularArrayQueue
//System.out.println(Q.toString());
} catch (FullQueueException e) {
e.printStackTrace();
}
}
public void sellShares(int shareAmount, int sharePrice) { // ToDo
}
public int capitalGain() { // ToDo
return 0;
}
public static void main(String[] args) {
String inputCommand = "";
Scanner mainReader = new Scanner(System.in);
System.out.println("Enter 'b' to purchase share, 's' to sell share, 'c' for capital gain, or 'Q' to quit: ");
inputCommand = mainReader.nextLine();
StockTran tran = new StockTran(inputCommand);
}
}
public class CircleArrayQueue implements Queue {
protected Node Q[]; // initializes an empty array for any element type
private int MAX_CAP = 0; // initializes the value for the maximum array capacity
private int f, r;
public CircleArrayQueue(int maxCap) {
MAX_CAP = maxCap;
Q = new Node[MAX_CAP]; // sets Q to be a specific maximum size specified
f = 0; // sets front value to be 0
r = 0; // sets rear value to be 0;
}
public int size() {
return (MAX_CAP - f + r) % MAX_CAP; // returns the size of the CircularArrayQueue
}
public boolean isEmpty() { // if front and rear are of equal value, Queue is empty
return f == r;
}
public Node front() throws EmptyQueueException { // method to get the front value of the CircularArrayQueue
if (isEmpty()) throw new EmptyQueueException("Queue is empty.");
return Q[f]; // returns object at front of CircularArrayQueue
}
public Node dequeue() throws EmptyQueueException { // method to remove from the front of the CircularArrayQueue
if (isEmpty()) throw new EmptyQueueException("Queue is empty.");
Node temp = Q[f]; // stores front object in local variable
Q[f] = null; // sets the value to be null in the array
f = (f + 1) % MAX_CAP; // sets the new front value to be this
return temp; // returns the object that was originally in the front
}
public void enqueue(Node element) throws FullQueueException { // method to add to the end of the CircualarArrayQueue
if (size() == MAX_CAP - 1) throw new FullQueueException("Queue has reached maximum capacity.");
Q[r] = element; // stores the new element at the rear of array
r = (r + 1) % MAX_CAP; // sets the new rear value to be the location after element insertion
}
}
You haven't initialized the reference Q. Since it's a field variable, it's initialized by default to null.
CircleArrayQueue Q;
When you're faced with a problem like this, you have to debug it. One source of information is the stack trace from the exception, which will tell you where the exception was thrown. You may also be able to ask the debugger in your development environment to stop automatically at the point an exception is thrown.
Secondly, when you compare strings in Java, use the equals() method rather than the == operator. The equals() method compares object values. The == operator compares the values of the references that point to the objects. You can have two equal objects with different reference values.
initialize your CircleArrayQueue Q. if you dont initialize it. it takes null as default value.
CircleArrayQueue q= new CircleArrayQueue(size);

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