Recently I challenged my co-worker to write an algorithm to solve this problem:
Find the least number of coins required that can make any change from
This the code in c# to find the solution.
public struct CoinCount
{
public int coinValue;
public int noOfCoins;
}
/// <summary>
/// Find and returns the no of coins in each coins in coinSet
/// </summary>
/// <param name="coinSet">sorted coins value in assending order</param>
/// <returns></returns>
public CoinCount[] FindCoinsCountFor1to99Collection(int[] coinSet)
{
// Add extra coin value 100 in the coin set. Since it need to find the collection upto 99.
CoinCount[] result = new CoinCount[coinSet.Length];
List<int> coinValues = new List<int>();
coinValues.AddRange(coinSet);
coinValues.Add(100);
// Selected coin total values
int totalCount = 0;
for (int i = 0; i < coinValues.Count - 1; i++)
{
int count = 0;
if (totalCount <= coinValues[i])
{
// Find the coins count
int remainValue = coinValues[i + 1] - totalCount;
count = (int)Math.Ceiling((remainValue * 1.0) / coinValues[i]);
}
else
{
if (totalCount <= coinValues[i + 1])
count = 1;
else
count = 0;
}
result[i] = new CoinCount() { coinValue = coinValues[i], noOfCoins = count };
totalCount += coinValues[i] * count;
}
return result;
}
I wrote this algorithm for similar kind of problem with DP, may it help
public class MinimumCoinProblem {
private static void calculateMinumCoins(int[] array_Coins, int sum) {
int[] array_best = new int[sum];
for (int i = 0; i < sum; i++) {
for (int j = 0; j < array_Coins.length; j++) {
if (array_Coins[j] <= i && (array_best[i] == 0 || (array_best[i - array_Coins[j]] + 1) <= array_best[i])) {
array_best[i] = array_best[i - array_Coins[j]] + 1;
}
}
}
System.err.println("The Value is" + array_best[14]);
}
public static void main(String[] args) {
int[] sequence1 = {11, 9,1, 3, 5,2 ,20};
int sum = 30;
calculateMinumCoins(sequence1, sum);
}
}
Inspired from this https://www.youtube.com/watch?v=GafjS0FfAC0
following
1) optimal sub problem
2) Overlapping sub problem principles
introduced in the video
using System;
using System.Collections.Generic;
using System.Linq;
namespace UnitTests.moneyChange
{
public class MoneyChangeCalc
{
private static int[] _coinTypes;
private Dictionary<int, int> _solutions;
public MoneyChangeCalc(int[] coinTypes)
{
_coinTypes = coinTypes;
Reset();
}
public int Minimun(int amount)
{
for (int i = 2; i <= amount; i++)
{
IList<int> candidates = FulfillCandidates(i);
try
{
_solutions.Add(i, candidates.Any() ? (candidates.Min() + 1) : 0);
}
catch (ArgumentException)
{
Console.WriteLine("key [{0}] = {1} already added", i, _solutions[i]);
}
}
int minimun2;
_solutions.TryGetValue(amount, out minimun2);
return minimun2;
}
internal IList<int> FulfillCandidates(int amount)
{
IList<int> candidates = new List<int>(3);
foreach (int coinType in _coinTypes)
{
int sub = amount - coinType;
if (sub < 0) continue;
int candidate;
if (_solutions.TryGetValue(sub, out candidate))
candidates.Add(candidate);
}
return candidates;
}
private void Reset()
{
_solutions = new Dictionary<int, int>
{
{0,0}, {_coinTypes[0] ,1}
};
}
}
}
Test cases:
using NUnit.Framework;
using System.Collections;
namespace UnitTests.moneyChange
{
[TestFixture]
public class MoneyChangeTest
{
[TestCaseSource("TestCasesData")]
public int Test_minimun2(int amount, int[] coinTypes)
{
var moneyChangeCalc = new MoneyChangeCalc(coinTypes);
return moneyChangeCalc.Minimun(amount);
}
private static IEnumerable TestCasesData
{
get
{
yield return new TestCaseData(6, new[] { 1, 3, 4 }).Returns(2);
yield return new TestCaseData(3, new[] { 2, 4, 6 }).Returns(0);
yield return new TestCaseData(10, new[] { 1, 3, 4 }).Returns(3);
yield return new TestCaseData(100, new[] { 1, 5, 10, 20 }).Returns(5);
}
}
}
}
You can very quickly find an upper bound.
Say, you take three quarters. Then you would only have to fill in the 'gaps' 1-24, 26-49, 51-74, 76-99 with other coins.
Trivially, that would work with 2 dimes, 1 nickel, and 4 pennies.
So, 3 + 4 + 2 + 1 should be an upper bound for your number of coins, Whenever your brute-force algorithm goes above thta, you can instantly stop searching any deeper.
The rest of the search should perform fast enough for any purpose with dynamic programming.
(edit: fixed answer as per Gabe's observation)
Here's a simple c# solution using Linq.
internal class Program
{
public static IEnumerable<Coin> Coins = new List<Coin>
{
new Coin {Name = "Dime", Value = 10},
new Coin {Name = "Penny", Value = 1},
new Coin {Name = "Nickel", Value = 5},
new Coin {Name = "Quarter", Value = 25}
};
private static void Main(string[] args)
{
PrintChange(34);
Console.ReadKey();
}
public static void PrintChange(int amount)
{
decimal remaining = amount;
//Order coins by value in descending order
var coinsDescending = Coins.OrderByDescending(v => v.Value);
foreach (var coin in coinsDescending)
{
//Continue to smaller coin when current is larger than remainder
if (remaining < coin.Value) continue;
// Get # of coins that fit in remaining amount
var quotient = (int)(remaining / coin.Value);
Console.WriteLine(new string('-',28));
Console.WriteLine("{0,10}{1,15}", coin.Name, quotient);
//Subtract fitting coins from remaining amount
remaining -= quotient * coin.Value;
if (remaining <= 0) break; //Exit when no remainder left
}
Console.WriteLine(new string('-', 28));
}
public class Coin
{
public string Name { get; set; }
public int Value { get; set; }
}
}
You need at least 4 pennies, since you want to get 4 as a change, and you can do that only with pennies.
It isn't optimal to have more than 4 pennies. Instead of 4+x pennies, you can have 4 pennies and x nickels - they span at least the same range.
So you have exactly 4 pennies.
You need at least 1 nickel, since you want to get 5 as a change.
It isn't optimal to have more than 1 nickel. Instead of 1+x nickels, you can have 1 nickel and x dimes - they span at least the same range.
So you have exactly 1 nickel.
You need at least 2 dimes, since you want to get 20.
This means you have 4 pennies, 1 nickel and at least 2 dimes.
If you had less than 10 coins, you would have less than 3 quarters. But then the maximal possible change you could get using all coins is 4 + 5 + 20 + 50 = 79, not enough.
This means you have at least 10 coins. Thomas's answer shows that in fact if you have 4 pennies, 1 nickel, 2 dimes and 3 quarters, all is well.