Skip to content

Commit d7d8326

Browse files
authored
Merge pull request #426 from JacopoMangiavacchi/master
Minimum Coin Change Problem - Dynamic Programming in Swift
2 parents b2f50be + d5c9d17 commit d7d8326

File tree

7 files changed

+193
-0
lines changed

7 files changed

+193
-0
lines changed

MinimumCoinChange/LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2017 Jacopo Mangiavacchi
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

MinimumCoinChange/Package.swift

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// swift-tools-version:3.1
2+
3+
import PackageDescription
4+
5+
let package = Package(
6+
name: "MinimumCoinChange"
7+
)

MinimumCoinChange/README.md

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Minimum Coin Change
2+
Minimum Coin Change problem algorithm implemented in Swift comparing dynamic programming algorithm design to traditional greedy approach.
3+
4+
Written for Swift Algorithm Club by Jacopo Mangiavacchi
5+
6+
![Coins](eurocoins.gif)
7+
8+
# Introduction
9+
10+
In the traditional coin change problem you have to find all the different ways to change some given money in a particular amount of coins using a given amount of set of coins (i.e. 1 cent, 2 cents, 5 cents, 10 cents etc.).
11+
12+
For example using Euro cents the total of 4 cents value of money can be changed in these possible ways:
13+
14+
- Four 1 cent coins
15+
- Two 2 cent coins
16+
- One 2 cent coin and two 1 cent coins
17+
18+
The minimum coin change problem is a variation of the generic coin change problem where you need to find the best option for changing the money returning the less number of coins.
19+
20+
For example using Euro cents the best possible change for 4 cents are two 2 cent coins with a total of two coins.
21+
22+
23+
# Greedy Solution
24+
25+
A simple approach for implementing the Minimum Coin Change algorithm in a very efficient way is to start subtracting from the input value the greater possible coin value from the given amount of set of coins available and iterate subtracting the next greater possible coin value on the resulting difference.
26+
27+
For example from the total of 4 Euro cents of the example above you can subtract initially 2 cents as the other biggest coins value (from 5 cents to above) are to bigger for the current 4 Euro cent value. Once used the first 2 cents coin you iterate again with the same logic for the rest of 2 cents and select another 2 cents coin and finally return the two 2 cents coins as the best change.
28+
29+
Most of the time the result for this greedy approach is optimal but for some set of coins the result will not be the optimal.
30+
31+
Indeed, if we use the a set of these three different coins set with values 1, 3 and 4 and execute this greedy algorithm for asking the best change for the value 6 we will get one coin of 4 and two coins of 1 instead of two coins of 3.
32+
33+
34+
# Dynamic Programming Solution
35+
36+
A classic dynamic programming strategy will iterate selecting in order a possible coin from the given amount of set of coins and finding using recursive calls the minimum coin change on the difference from the passed value and the selected coin. For any interaction the algorithm select from all possible combinations the one with the less number of coins used.
37+
38+
The dynamic programming approach will always select the optimal change but it will require a number of steps that is at least quadratic in the goal amount to change.
39+
40+
In this Swift implementation in order to optimize the overall performance we use an internal data structure for caching the result for best minimum coin change for previous values.
41+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
//
2+
// Minimum Coin Change Problem Playground
3+
// Compare Greedy Algorithm and Dynamic Programming Algorithm in Swift
4+
//
5+
// Created by Jacopo Mangiavacchi on 04/03/17.
6+
//
7+
8+
import Foundation
9+
10+
public enum MinimumCoinChangeError: Error {
11+
case noRestPossibleForTheGivenValue
12+
}
13+
14+
public struct MinimumCoinChange {
15+
internal let sortedCoinSet: [Int]
16+
17+
public init(coinSet: [Int]) {
18+
self.sortedCoinSet = coinSet.sorted(by: { $0 > $1} )
19+
}
20+
21+
//Greedy Algorithm
22+
public func changeGreedy(_ value: Int) throws -> [Int] {
23+
guard value > 0 else { return [] }
24+
25+
var change: [Int] = []
26+
var newValue = value
27+
28+
for coin in sortedCoinSet {
29+
while newValue - coin >= 0 {
30+
change.append(coin)
31+
newValue -= coin
32+
}
33+
34+
if newValue == 0 {
35+
break
36+
}
37+
}
38+
39+
if newValue > 0 {
40+
throw MinimumCoinChangeError.noRestPossibleForTheGivenValue
41+
}
42+
43+
return change
44+
}
45+
46+
//Dynamic Programming Algorithm
47+
public func changeDynamic(_ value: Int) throws -> [Int] {
48+
guard value > 0 else { return [] }
49+
50+
var cache: [Int : [Int]] = [:]
51+
52+
func _changeDynamic(_ value: Int) -> [Int] {
53+
guard value > 0 else { return [] }
54+
55+
if let cached = cache[value] {
56+
return cached
57+
}
58+
59+
var potentialChangeArray: [[Int]] = []
60+
61+
for coin in sortedCoinSet {
62+
if value - coin >= 0 {
63+
var potentialChange: [Int] = [coin]
64+
potentialChange.append(contentsOf: _changeDynamic(value - coin))
65+
66+
if potentialChange.reduce(0, +) == value {
67+
potentialChangeArray.append(potentialChange)
68+
}
69+
}
70+
}
71+
72+
if potentialChangeArray.count > 0 {
73+
let sortedPotentialChangeArray = potentialChangeArray.sorted(by: { $0.count < $1.count })
74+
cache[value] = sortedPotentialChangeArray[0]
75+
return sortedPotentialChangeArray[0]
76+
}
77+
78+
return []
79+
}
80+
81+
let change: [Int] = _changeDynamic(value)
82+
83+
if change.reduce(0, +) != value {
84+
throw MinimumCoinChangeError.noRestPossibleForTheGivenValue
85+
}
86+
87+
return change
88+
}
89+
}
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import XCTest
2+
@testable import MinimumCoinChangeTests
3+
4+
XCTMain([
5+
testCase(MinimumCoinChangeTests.allTests),
6+
])
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import XCTest
2+
@testable import MinimumCoinChange
3+
4+
class MinimumCoinChangeTests: XCTestCase {
5+
func testExample() {
6+
let mcc = MinimumCoinChange(coinSet: [1, 2, 5, 10, 20, 25])
7+
print("Coin set: \(mcc.sortedCoinSet)")
8+
9+
do {
10+
for i in 0..<100 {
11+
let greedy = try mcc.changeGreedy(i)
12+
let dynamic = try mcc.changeDynamic(i)
13+
14+
XCTAssertEqual(greedy.reduce(0, +), dynamic.reduce(0, +), "Greedy and Dynamic return two different changes")
15+
16+
if greedy.count != dynamic.count {
17+
print("\(i): greedy = \(greedy) dynamic = \(dynamic)")
18+
}
19+
}
20+
}
21+
catch {
22+
XCTFail("Test Failed: impossible to change with the given coin set")
23+
}
24+
}
25+
26+
static var allTests = [
27+
("testExample", testExample),
28+
]
29+
}

MinimumCoinChange/eurocoins.gif

87.9 KB
Loading

0 commit comments

Comments
 (0)