So I wrote a recursive algorithm for the problem of figuring out the least number of 'coins' of a particular set of denominations possible to arrive at a given sum. The algorithm seems to work, but because it's recursive, and calculates every possible option before choosing one or the other, I'm having a difficult time coming up with a way to print out the denominations used as well. So essentially I can calculate the least number of coins possible to use, but not which coins they are. Here's the code and the little main method I'm using to drive it. Any suggestions of streamlining the algorithm itself would also be welcome.
public class DynamicCoinChange {
public static void main(String[] args) {
int[] denoms = {1, 6, 10, 25};
int numCoins = dynamicCoinChange(denoms, 18, 3);
System.out.println(numCoins);
}
public static int dynamicCoinChange(int[] denoms, int amt, int start) {
if (amt == 0 || start < 0) {
return 0;
} else if (amt == 1) {
return 1;
} else if (denoms[start] > amt ||
dynamicCoinChange(denoms, amt, start-1) <
(1 + dynamicCoinChange(denoms, amt-denoms[start], start)) &&
!(dynamicCoinChange(denoms, amt, start-1) == 0)) {
return dynamicCoinChange(denoms, amt, start-1);
} else {
return 1 + dynamicCoinChange(denoms,amt-denoms[start], start);
}
}
}
We need to know two pieces of information:
when a coin is chosen for the optimal solution
which coin was chosen for the optimal solution
According to your algorithm, we know that we select a coin whenever you return 1. We also know that the coin chosen at that the index of the coin chosen is start. Thus, we have information to solve this problem.
Since performance isn't a problem here, we will simply pass a coins parameter that is filled as the coins are selected.
public static int dynamicCoinChange(int[] denoms, int amt, int start, ArrayList<Integer> coins) {
if (amt == 0 || start < 0) {
return 0;
} else if (amt == 1) {
coins.add(1);
return 1;
} else if (denoms[start] > amt
// Note that these calls are not guaranteed to be in our solution
// Thus, we make a copy to prevent the calls from modifying our solution
|| dynamicCoinChange(denoms, amt, start-1, new ArrayList<Integer>(coins)) <
(1 + dynamicCoinChange(denoms, amt-denoms[start], start, new ArrayList<Integer>(coins)))
&& !(dynamicCoinChange(denoms, amt, start-1, new ArrayList<Integer>(coins)) == 0)) {
return dynamicCoinChange(denoms, amt, start-1, coins);
} else {
coins.add(denoms[start]);
return 1 + dynamicCoinChange(denoms,amt-denoms[start], start, coins);
}
}
Since this requires us to change our method signature, we must also modify our driver:
public static void main(String[] args) {
int[] denoms = {1, 6, 10, 25};
ArrayList<Integer> coins = new ArrayList<Integer>();
int numCoins = dynamicCoinChange(denoms, 7, 3, coins);
for (Integer coin : coins)
System.out.println(coin);
System.out.println(numCoins);
}
At the end of the recursive calls, coins should contain the list of coins chosen in chronological order.
The Minimum Change problem need not be programmed recursively. It can be programmed in a simpler iterative fashion.
int[] denoms = { 1, 6, 10, 25 };
int amt = 18;
double[] min = new double[ amt + 1 ];
for( int i = 1; i < min.length; i++ ) { // We're keeping min[ 0 ] as 0/
min[ i ] = Double.POSITIVE_INFINITY;
}
for( int i = 1; i <= amt; i++ ) {
for( int j = 0; j <= N - 1; j++ ) {
if( denoms[ j ] <= i && min[ i - denoms[ j ] ] + 1 < min[ i ] )
min[ i ] = min[ i - denoms[ j ] ] + 1;
}
}
Here, your solution would be in entry min[amt].
Related
I have an assignment to submit and I need help I will appreciate it a lot!!
I need to write a method that gets an integer matrix grid with numbers from 0 and above. 1 value in the matrix is different and contains -1. the function also gets the starting x and y in the matrix.
I need to write a recursive function that finds the shortest path from the starting x, y to the -1 value.
you can "jump" to the left' right' up and down. the condition to "jump" from one index to another is if the absolute value between the subtraction of the the 2 pairing is either 0, 1 or 2
The shortest path is 4
I cannot use while and for loops, and global variables
The function signature should be: public static int shortestPath(int[][] drm, int i, int j)
Thanks a lot!
Here is my try:
package assignment1;
import java.util.*;
public class run {
static Scanner reader = new Scanner(System.in);
public static int shortestPath(int[][] drm, int i, int j) {
if (drm.length == 0 || i >= drm.length || j >= drm.length || i < 0 || j < 0)
return 0;
if (i > 0 && j > 0 && i < drm.length - 1 && j < drm[i].length - 1) {
if (drm[i][j - 1] == -1) {
System.out.print("Got it! the target is on the left");
return 2;
}
if (drm[i][j + 1] == -1) {
System.out.print("Got it! the target is on the right");
return 2;
}
if (drm[i - 1][j] == -1) {
System.out.print("Got it! the target is up");
return 2;
}
if (drm[i + 1][j] == -1) {
System.out.print("Got it! the target is down");
return 2;
}
}
int temp = drm[i][j];
int left = Integer.MAX_VALUE, right = Integer.MAX_VALUE, up = Integer.MAX_VALUE, down = Integer.MAX_VALUE;
if (isValidJump(drm, i, j, i + 1, j)) {
System.out.print("down ");
drm[i][j] = Integer.MIN_VALUE;
down = shortestPath(drm, i + 1, j) + 1;
}
if (isValidJump(drm, i, j, i, j + 1)) {
System.out.print("right ");
drm[i][j] = Integer.MIN_VALUE;
right = shortestPath(drm, i, j + 1) + 1;
}
if (isValidJump(drm, i, j, i, j - 1)) {
System.out.print("left ");
drm[i][j] = Integer.MIN_VALUE;
left = shortestPath(drm, i, j - 1) + 1;
}
if (isValidJump(drm, i, j, i - 1, j)) {
System.out.print("up ");
drm[i][j] = Integer.MIN_VALUE;
up = shortestPath(drm, i - 1, j) + 1;
}
drm[i][j] = temp;
return Math.min(Math.min(Math.min(up, down), left), right);
}
public static boolean isValidJump(int[][] drm, int i, int j, int m, int n) {
if (m < drm.length && m >= 0 && n < drm.length && n >= 0 && drm[m][n] != Integer.MIN_VALUE) {
int jump = drm[m][n] - drm[i][j];
if (jump == 0 || jump == 1 || jump == -1 || jump == -2) {
return true;
}
}
return false;
}
public static void main(String[] args) {
int[][] drm = { { 2, 0, 1, 2, 3 }, { 2, 3, 5, 5, 4 }, { 8, -1, 6, 8, 7 }, { 3, 4, 7, 2, 4 },
{ 2, 4, 3, 1, 2 } };
System.out.println(shortestPath(drm, 0, 0));
}
}
It supposed to return 4 (shortest path)
Given this is for a class I advise you to notify your professor that you received assistance from this post on stack overflow. Neglecting to do this would be considered academic dishonestly at most universities.
Second thing is as David suggested this is a good opportunity for you to learn how to use a debugger. This is a skill that will be incredibly valuable in your academic career and in a engineering role.
Your Code
Now looking at your code it does give the solution "4" for the case you presented which is correct. Problem is if you change the inputs the output may not give the correct answer.
This is because your code as written gives the FIRST path it finds and not the SHORTEST path.
Your logic as far as the recursion is sound and based on this code it looks like you understand the basics of recursion. Your problem is a minor logical flaw with how your are masking your data when you call your function recursively.
You should have everything you need to solve this. If you are still having problems please try to use a debugger and examine the area where you make your recursive calls.
Solution
I advise you to try to figure this out yourself before looking at the spoilers below.
In the code where you make your recursive calls you mask by setting drm[i][j] = Integer.MIN_VALUE. The problem is after each of your recursive calls return you do not set it back to the previous value with drm[i][j] = temp before doing the tests for your next recursive call.
What is happening is when you next call isValidJump() it will always return false because drm[i][j] will always be Integer.MIN_VALUE after your have made your first recursive call on this iteration.
How to fix:
Put drm[i][j] = temp immediately after each recursive call to shortestPath().
I have a brute force solution with backtracking to solve the coin change problem. Currently i get minimum number of coins that can be used. But how could i also return the coins that were actually used? So for example if the amount is 100 then i would like to return [25, 25, 25, 25].
My current code is below:
public class Solution {
public static void main(String[] args) {
Solution s = new Solution();
int coinChange = s.coinChange(0, new int[] { 1, 25, 50 }, 100);
System.out.println(coinChange);
}
public int coinChange(int idx, int[] coins, int amount) {
if (amount == 0){
return 0;
}
if (idx < coins.length && amount > 0) {
int maxVal = amount / coins[idx];
int minCost = Integer.MAX_VALUE;
for (int x = 0; x <= maxVal; x++) {
if (amount >= x * coins[idx]) {
int res = coinChange(idx + 1, coins, amount - x * coins[idx]);
if (res != -1)
minCost = Math.min(minCost, res + x);
}
}
return (minCost == Integer.MAX_VALUE) ? -1 : minCost;
}
return -1;
}
}
First of all, I suggest using accurate variable names. That will make it a lot easier for everyone, yourself included, to understand how the algorithm works. Your current array "coins" is not a list of coins, it is a list of available denominations, so it should named "denominations" or "denominations_available" or something like that. The variable you call "amount" is the amount remaining, so it should be named either "remaining" or amount_remaining".
To store the list of coins used so far, you can use a stack, or just a list that is treated like a stack. For example, you could use an ArrayList of Integers. Every time you call coinChange, add the denomination of the chosen coin (denominations[idx]) to your list before you make the call. Every time you return -1 (failure), remove the last item on the list (if there is one) before you return. When the success condition is reached (amount_remaining==0), the coin list will contain the coins used.
Correction: since coinChange is called multiple times in a loop, the stack must be popped after every call and the best minimum is pushed back again after it is determined. So, it should go like this:
int best_coin = 0;
for (int x = 0; x <= maxVal; x++) {
if (amount >= x * coins[idx]) {
<<<<<< PUSH GOES HERE
int res = coinChange(idx + 1, coins, amount - x * coins[idx]);
<<<<<< POP GOES HERE
if (res == -1){
// failed to find valid combination of coins
} else {
if( minCost < res + x ){
// do nothing
} else { // update minimum
minCost = res + x;
best_coin = coins[idx];
}
}
}
<<<<<< PUSH BEST COIN
return (minCost == Integer.MAX_VALUE) ? -1 : minCost;
Why do i get a stack overflow error the thing is im trying to solve this recursively as a start,before i start using dynamic programming .In the method coins,"a" is the array that holds the coins that will form the total i want,sum is the total i want (17 for example),and i represents he index of the array that i am at
import java.util.*;
public class dp2 {//RECURSIVE WAY THEN OPTIMIZE TO DP
public static int coins (int [] a, int sum,int i){
if (sum==0)
return 1;
else if (i==a.length){
return 0;
}
else if (i>a.length&&sum<a[i]){
return coins(a,sum,i++);
}
else {
return coins(a,sum-a[i],i++)+coins(a,sum-a[i],i);
}
}
public static void main (String [] args ){
Scanner sc = new Scanner (System.in);
while (sc.hasNext()){
int x = sc.nextInt();
int y=x;
int [] a ={1,5,10,25,50};
int w = coins(a,y,0);
System.out.println("There are " +w+ " ways to produce "+x + " coins change.");
}
}
}
I added one statement to your code, at line 4:
System.out.println(sum + ", " + i);
Give the input 27, the output was:
27, 0
26, 0
25, 0
.... decreasing by one each time...
3, 0
2, 0
1, 0
0, 0
-4, 1
-9, 1
-14, 1
And then it never stops because you don't check for sum < 0.
You should be able to figure it out from there... println: the lightest-weight debugger in the world :-)
You have a problem with infinity loop.
First check, what do you want to return, because actually you can return only 1 or 0. So, where is other condition? Example: i == a.length - 1 or sum < 0 ? I think, you want to return sum.
And next, if you put example 17, so where is mechanism to choose witch coins from array you can select?
And next, please change return coins(a,sum-a[i],++i)+coins(a,sum-a[i],i);, because in your code i is always 0
So, maybe is good exaple code for You:
class dp2 {//RECURSIVE WAY THEN OPTIMIZE TO DP
public static int coins (int [] a, int sum,int i){
if (sum==0)
return 1;
else if (i==a.length){
return 0;
}else if(i + 1 == a.length || sum < 0 || sum - a[i] < 0){ //Your break condition ?
return sum;
}
else if (i>a.length&&sum<a[i]){
return coins(a,sum,i++);
}
else {
return coins(a,sum-a[i],++i)+coins(a,sum-a[i],i);
}
}
public static void main (String [] args ){
Scanner sc = new Scanner (System.in);
while (sc.hasNext()){
int x = sc.nextInt();
int y=x;
int [] a ={1,5,10,25,50};
int w = coins(a,y,0);
System.out.println("There are " +w+ " ways to produce "+x + " coins change.");
}
}
}
I am stuck on the coin denomination problem.
I am trying to find the lowest number of coins used to make up $5.70 (or 570 cents). For example, if the coin array is {100,5,2,5,1} (100 x 10c coins, 5 x 20c, 2 x 50c, 5 x $1, and 1 x $2 coin), then the result should be {0,1,1,3,1}
At the moment the coin array will consist of the same denominations ( $2, $1, 50c, 20c, 10c)
public static int[] makeChange(int change, int[] coins) {
// while you have coins of that denomination left and the total
// remaining amount exceeds that denomination, take a coin of that
// denomination (i.e add it to your result array, subtract it from the
// number of available coins, and update the total remainder). –
for(int i= 0; i< coins.length; i++){
while (coins[i] > 0) {
if (coins[i] > 0 & change - 200 >= 0) {
coins[4] = coins[4]--;
change = change - 200;
} else
if (coins[i] > 0 & change - 100 >= 0) {
coins[3] = coins[3]--;
change = change - 100;
} else
if (coins[i] > 0 & change - 50 >= 0) {
coins[2] = coins[2]--;
change = change - 50;
} else
if (coins[i] > 0 & change - 20 >= 0) {
coins[1] = coins[1]--;
change = change - 20;
} else
if (coins[i] > 0 & change - 10 >= 0) {
coins[0] = coins[0]--;
change = change - 10;
}
}
}
return coins;
}
I am stuck on how to deduct the values from coins array and return it.
EDIT: New code
The brute force solution is to try up to the available number of coins of the highest denomination (stopping when you run out or the amount would become negative) and for each of these recurse on solving the remaining amount with a shorter list that excludes that denomination, and pick the minimum of these. If the base case is 1c the problem can always be solved, and the base case is return n otherwise it is n/d0 (d0 representing the lowest denomination), but care must be taken to return a large value when not evenly divisible so the optimization can pick a different branch. Memoization is possible, and parameterized by the remaining amount and the next denomination to try. So the memo table size would be is O(n*d), where n is the starting amount and d is the number of denominations.
So the problem can be solved in pseudo-polynomial time.
The wikipedia link is sparse on details on how to decide if a greedy algorithm such as yours will work. A better reference is linked in this CS StackExchange question. Essentially, if the coin system is canonical, a greedy algorithm will provide an optimal solution. So, is [1, 2, 5, 10, 20] canonical? (using 10s of cents for units, so that the sequence starts in 1)
According to this article, a 5-coin system is non-canonical if and only if it satisfies exactly one of the following conditions:
[1, c2, c3] is non-canonical (false for [1, 2, 5])
it cannot be written as [1, 2, c3, c3+1, 2*c3] (true for [1, 2, 5, 10, 20])
the greedyAnswerSize((k+1) * c4) > k+1 with k*c4 < c5 < (k+1) * c4; in this case, this would require a k*10 < 20 < (k+1)*10; there is no integer k in that range, so this is false for [1, 2, 5, 10, 20].
Therefore, since the greedy algorithm will not provide optimal answers (and even if it did, I doubt that it would work with limited coins), you should try dynamic programming or some enlightened backtracking:
import java.util.HashSet;
import java.util.PriorityQueue;
public class Main {
public static class Answer implements Comparable<Answer> {
public static final int coins[] = {1, 2, 5, 10, 20};
private int availableCoins[] = new int[coins.length];
private int totalAvailable;
private int totalRemaining;
private int coinsUsed;
public Answer(int availableCoins[], int totalRemaining) {
for (int i=0; i<coins.length; i++) {
this.availableCoins[i] = availableCoins[i];
totalAvailable += coins[i] * availableCoins[i];
}
this.totalRemaining = totalRemaining;
}
public boolean hasCoin(int coinIndex) {
return availableCoins[coinIndex] > 0;
}
public boolean isPossibleBest(Answer oldBest) {
boolean r = totalRemaining >= 0
&& totalAvailable >= totalRemaining
&& (oldBest == null || oldBest.coinsUsed > coinsUsed);
return r;
}
public boolean isAnswer() {
return totalRemaining == 0;
}
public Answer useCoin(int coinIndex) {
Answer a = new Answer(availableCoins, totalRemaining - coins[coinIndex]);
a.availableCoins[coinIndex]--;
a.totalAvailable = totalAvailable - coins[coinIndex];
a.coinsUsed = coinsUsed+1;
return a;
}
public int getCoinsUsed() {
return coinsUsed;
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder("{");
for (int c : availableCoins) sb.append(c + ",");
sb.setCharAt(sb.length()-1, '}');
return sb.toString();
}
// try to be greedy first
#Override
public int compareTo(Answer a) {
int r = totalRemaining - a.totalRemaining;
return (r==0) ? coinsUsed - a.coinsUsed : r;
}
}
// returns an minimal set of coins to solve
public static int makeChange(int change, int[] availableCoins) {
PriorityQueue<Answer> queue = new PriorityQueue<Answer>();
queue.add(new Answer(availableCoins, change));
HashSet<String> known = new HashSet<String>();
Answer best = null;
int expansions = 0;
while ( ! queue.isEmpty()) {
Answer current = queue.remove();
expansions ++;
String s = current.toString();
if (current.isPossibleBest(best) && ! known.contains(s)) {
known.add(s);
if (current.isAnswer()) {
best = current;
} else {
for (int i=0; i<Answer.coins.length; i++) {
if (current.hasCoin(i)) {
queue.add(current.useCoin(i));
}
}
}
}
}
// debug
System.out.println("After " + expansions + " expansions");
return (best != null) ? best.getCoinsUsed() : -1;
}
public static void main(String[] args) {
for (int i=0; i<100; i++) {
System.out.println("Solving for " + i + ":"
+ makeChange(i, new int[]{100,5,2,5,1}));
}
}
}
You are in wrong direction. This program will not give you an optimal solution. To get optimal solution go with dynamic algorithms implemented and discussed here. Please visit these few links:
link 1
link 2
link 3
Well I have "spiderman" which climbs the building which has n floors.
How many options does he have to get to the n'th floor if he knows to climb everytime one floor or two floors.
Here is what I did so far: (No arrays, no For loops, keep it simple)
public static int spiderman(int n,int i,int sum){
if ( n == 0 ) return 0;
if ( n < i) return 0;
if ( n == i) return 1;
sum += i;
return spiderman(n,i + 1,sum) + spiderman(n,i + 2,sum);
}
public static void main( String [] args ){
System.out.println(spiderman(4,0,0)); //Should return 5
}
Output:spiderman(4) returns 5. --Solved!
i think your if ( n > i) return 0; should be if ( n < i) return 0;, reverse the sign.
also, you sum variable does not seem necessary. It doesn't do anything and has no influence on the result of your function
Bonus:
The question you need to answer is actually similar to the Fibonacci sequence. Which means you will see the following pattern:
spiderman(n, 0, 0) == spiderman(n - 1, 0, 0) + spiderman(n - 2, 0, 0).
Because the number of ways to get to a floor is equal to the number of ways he can get to the floor below + 2 floors below, becuase those are the floors from which he can reach his goal.
Therefore this is an alternative solution to your problem
public static int spiderman(int n){
if (n < 3) return n;
return spiderman(n - 1) + spiderman(n -2);
}