Money change problem: Greedy vs. Dyn.Pro.
This is a classical problem of Computer Science: it’s used to study both Greedy and Dynamic Programming algorithmic techniques.

I hate having my pocket full of copper!!! -_-
Definition
Given:
- A set of
nDenominationsD[0...n-1]in ascending order, representing a Monetary Coin System - An money amount
A, as input
calculate a solution:
S[0...n-1], with0 <= S[i] <= (A/S[i])and0 < i < n-1
where:
A = Sum[i=0 -> n-1] { D[i] * S[i] }Min{ Sum[i=0 -> n-1] { S[i] } }
In other words
Find the smallest amount of coins to make the given amount.
First, the Greedy solution
The Greedy approach is as expected: tries to take as much largest coins as possible. Nothing fancy.
change_coins_greedy(D[], A):
init S[n]
i = n-1
// Pick as much largest coins as possible
while ( A > 0 ) do:
S[i] = A / D[i]
A = A - S[i] * D[i]
i = i - 1
endwhile
// Set to '0' the result for all the other coins
while ( i >= 0 ) do:
S[i] = 0
i = i - 1
endwhile
This algorithm, of time complexity O(A), doesn’t work for some (rare) situations.
When Greedy is not enough
The Greedy algorithm doesn’t work for Denominations where if 2 denominations D[i] and D[j] exists with:
i < jD[i] < D[j]2 * D[i] > D[j]
For example, taken D = {1, 10, 30, 40} and amount A = 63, the Greedy algorithm will build a solution S = {3, 2, 0, 1}, that is sub-optimal. The optimal solution in this case is S = {3, 0, 2, 0}.
UPDATE: In the comments Vincenzo gives an example where this condition doesn’t still stand but the Greedy Algorithm still produces the best solution.
Dynamically Programmed Solution
In real life the Greedy algorithm should be always enough: I couldn’t find any money system that has the problem described above. And, indeed, the Greedy approach is what every human being “normally” applies when changing money.
But we are Comp-Sci, and we need to find a better solution
– a Dynamically Programmed one.
Given the function M[j], that is the minimum number of coins to make the amount ‘j’, it looks like:
- M[A] = min[i = 0 -> n-1] { M[ A - D[i] ] +1 , M[A] }
Here is the code:
unsigned int* change_coins_dynpro(unsigned short int D[],
unsigned int D_size, unsigned int amount) {
// Min. num. of coins for the given 'amount'
unsigned int min_num_coins[amount], cur_min_num_coins;
// Biggest coin used for the given 'amount'
unsigned int biggest_coin_at[amount], cur_biggest_coin_at;
unsigned int i, j;
unsigned int* solution = NULL;
// Ensure the Denomination System can represent any value
if ( D[0] != 1 ) return NULL;
// Initialize the solution array to Zero
solution = (unsigned int*)calloc(D_size, sizeof(unsigned int));
if ( NULL == solution ) {
return NULL;
}
// Amount '0' requires '0' coins
min_num_coins[0] = 0;
biggest_coin_at[0] = 0;
// For Amounts from '1' to 'amount'
for ( i = 1; i <= amount; ++i ) {
// Start taking 'D[0]' 'i-times'
cur_min_num_coins = (i / D[0]);
cur_biggest_coin_at = 0;
// For coins from 'D[1]' to 'D[D_size -1]'
for ( j = 1; j < D_size; ++j ) {
// If 'D[j]' minimizes the num. of coins to take for amount 'i'
if ( (i >= D[j]) && (cur_min_num_coins >= (min_num_coins[ i - D[j] ] +1)) ) {
cur_min_num_coins = min_num_coins[ i - D[j] ] +1;
cur_biggest_coin_at = j;
}
}
// Store the minimum just found
min_num_coins[i] = cur_min_num_coins;
biggest_coin_at[i] = cur_biggest_coin_at;
}
// Let's build the solution array
while ( amount > 0 ) {
cur_biggest_coin_at = biggest_coin_at[amount];
solution[ cur_biggest_coin_at ] += 1; // Add '1' of this coin to the solution
amount -= D[cur_biggest_coin_at]; // Amount left when picking this coin
}
return solution;
}
Time complexity for this algorithm is O( Amount * num_of_denominations ).
Pretty heavy algorithm, but do you want to compare with the satisfaction of carrying the minimum amount of coins with you?

about 1 month ago
I was on the train and I was playing with this.
The condition you mention as responsible to invalidate di Greedy algorithm turns out to be necessary but not sufficient. Indeed, if you use the following input:
D = {1, 2, 5, 16, 30, 50}
A = 35
which verifies that condition, you can easily check that the Greedy algorithm still works.
On the other hand, you can easily embed your condition in the Greedy algorithm (see pseudo-code at the bottom of the comment), and make it works in some situations where that condition is verified, as in
D = {1, 2, 5, 10, 30, 50}
A = 63
Again, being the condition non sufficient, embedding it in the Greedy algorithm is also not sufficient and your DP solution still is the only one being correct (as in “it works in every case”).
But we can probably find a complementary condition which together with the other one will form a set of condition necessary and sufficient, and therefore the Greedy algorithm can be tweaked accordingly. Not sure, though, that this new potential condition, once implemented, will keep the computational complexity unchanged, as the other one did.
— Pseudo Code embedding the condition in Greedy algorithm:
change_coins_greedy(D[], A):
init S[n]
i = n-1
// Pick as much largest coins as possible
B = A
while ( B > 0 ) do:
// not the first coin being considered and
// the previous coin has been picked at least once
if (i 0) then
// condition to check the monetary system
if (2 * D[i] > D[i + 1]) then
// reset the problem
B = A;
S[i+1] = 0
endif
endif
S[i] = B / D[i]
B = B – S[i] * D[i]
i = i – 1
endwhile
// Set to ‘0′ the result for all the other coins
while ( i >= 0 ) do:
S[i] = 0
i = i – 1
endwhile
about 1 month ago
I think you are actually right. I have been a bit too “easy” on setting that condition.
One small thing about your algorithm: you check “my” codition only for pairs (i,j), where j = i+1. The condition was for any pair (i,j), where j > i.
But this doesn’t change the fact that I was wrong
Thanks for commenting: I’m going to correct the post right now.
about 1 month ago
The condition is verified for every i and j. I explain why. The array representing the monetary system is ordered from the lowest to highest.
D[1] < D[2] < … < D[n-2] < D[n-1] < D[n]
Given the condition above,
2*D[n-2] 2*D[n-2] ≤ 2*D[n-1] ≤ D[n]
By induction you can verify that valid for every i and j.
about 1 month ago
Ok, the HTML filters have messed up my previous comment…
Let’s try again from “Given the condition above,
2*D[n-2] ≤ 2*D[n-1]
is true, implying
2*D[n-1] ≤ D[n] => 2*D[n-2] ≤ 2*D[n-1] ≤ D[n]
By induction you can verify that valid for every i and j.
about 1 month ago
You are right. I’m a retard
Should I remove your bad-formatted comment?
about 1 month ago
Well, merge them, because in the malformed one there is a bit I don’t repeat in the good one.
Anyway, resetting the problem by B = A is also wrong, in my pseudo-code, because A never changes.
Without testing it, I believe A should be updated as well every time a B “is confirmed”, which means that this bit
if (2 * D[i] > D[i + 1]) then
// reset the problem
B = A;
S[i+1] = 0
endif
should become
if (2 * D[i] > D[i + 1]) then
// reset the problem
B = A
S[i+1] = 0
else
A = B
endif
about 1 month ago
Can’t be asked to merge. Let’s just leave the comment flow as it is.