Skip to content

Commit 8080f27

Browse files
Fix and optimizations
1 parent 8efb735 commit 8080f27

File tree

2 files changed

+66
-43
lines changed

2 files changed

+66
-43
lines changed

MinimumCoinChange/Sources/MinimumCoinChange.swift

Lines changed: 52 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,91 @@
11
//
2-
// Minimum Coin Change Problem
2+
// Minimum Coin Change Problem Playground
33
// Compare Greedy Algorithm and Dynamic Programming Algorithm in Swift
44
//
55
// Created by Jacopo Mangiavacchi on 04/03/17.
66
//
77

88
import Foundation
99

10+
public enum MinimumCoinChangeError: Error {
11+
case noRestPossibleForTheGivenValue
12+
}
13+
1014
public struct MinimumCoinChange {
11-
private let sortedCoinSet: [Int]
12-
private var cache: [Int : [Int]]
15+
internal let sortedCoinSet: [Int]
1316

1417
public init(coinSet: [Int]) {
1518
self.sortedCoinSet = coinSet.sorted(by: { $0 > $1} )
16-
self.cache = [:]
1719
}
1820

1921
//Greedy Algorithm
20-
public func changeGreedy(_ value: Int) -> [Int] {
22+
public func changeGreedy(_ value: Int) throws -> [Int] {
23+
guard value > 0 else { return [] }
24+
2125
var change: [Int] = []
2226
var newValue = value
23-
27+
2428
for coin in sortedCoinSet {
2529
while newValue - coin >= 0 {
2630
change.append(coin)
2731
newValue -= coin
2832
}
2933

30-
if newValue == 0 {
34+
if newValue == 0 {
3135
break
3236
}
3337
}
3438

39+
if newValue > 0 {
40+
throw MinimumCoinChangeError.noRestPossibleForTheGivenValue
41+
}
42+
3543
return change
3644
}
3745

3846
//Dynamic Programming Algorithm
39-
public mutating func changeDynamic(_ value: Int) -> [Int] {
40-
if value <= 0 {
41-
return []
42-
}
47+
public func changeDynamic(_ value: Int) throws -> [Int] {
48+
guard value > 0 else { return [] }
4349

44-
if let cached = cache[value] {
45-
return cached
46-
}
47-
48-
var change: [Int] = []
49-
50-
var potentialChangeArray: [[Int]] = []
51-
52-
for coin in sortedCoinSet {
53-
if value - coin >= 0 {
54-
var potentialChange: [Int] = []
55-
potentialChange.append(coin)
56-
let newPotentialValue = value - coin
50+
var cache: [Int : [Int]] = [:]
5751

58-
if value > 0 {
59-
potentialChange.append(contentsOf: changeDynamic(newPotentialValue))
60-
}
52+
func _changeDynamic(_ value: Int) -> [Int] {
53+
guard value > 0 else { return [] }
6154

62-
//print("value: \(value) coin: \(coin) potentialChange: \(potentialChange)")
63-
potentialChangeArray.append(potentialChange)
55+
if let cached = cache[value] {
56+
return cached
6457
}
58+
59+
var change: [Int] = []
60+
61+
var potentialChangeArray: [[Int]] = []
62+
63+
for coin in sortedCoinSet {
64+
if value - coin >= 0 {
65+
var potentialChange: [Int] = [coin]
66+
potentialChange.append(contentsOf: _changeDynamic(value - coin))
67+
potentialChangeArray.append(potentialChange)
68+
}
69+
}
70+
71+
if potentialChangeArray.count > 0 {
72+
let sortedPotentialChangeArray = potentialChangeArray.sorted(by: { $0.count < $1.count })
73+
change = sortedPotentialChangeArray[0]
74+
}
75+
76+
if change.reduce(0, +) == value {
77+
cache[value] = change
78+
}
79+
80+
return change
6581
}
66-
67-
if potentialChangeArray.count > 0 {
68-
let sortedPotentialChangeArray = potentialChangeArray.sorted(by: { $0.count < $1.count })
69-
change = sortedPotentialChangeArray[0]
70-
}
71-
72-
cache[value] = change
82+
83+
let change: [Int] = _changeDynamic(value)
84+
85+
if change.reduce(0, +) != value {
86+
throw MinimumCoinChangeError.noRestPossibleForTheGivenValue
87+
}
88+
7389
return change
7490
}
7591
}
76-

MinimumCoinChange/Tests/MinimumCoinChangeTests/MinimumCoinChangeTests.swift

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,24 @@ import XCTest
33

44
class MinimumCoinChangeTests: XCTestCase {
55
func testExample() {
6-
var mcc = MinimumCoinChange(coinSet: [1, 2, 5, 10, 20, 25])
6+
let mcc = MinimumCoinChange(coinSet: [1, 2, 5, 10, 20, 25])
7+
print("Coin set: \(mcc.sortedCoinSet)")
78

8-
for i in 0..<100 {
9-
let greedy = mcc.changeGreedy(i)
10-
let dynamic = mcc.changeDynamic(i)
9+
do {
10+
for i in 0..<100 {
11+
let greedy = try mcc.changeGreedy(i)
12+
let dynamic = try mcc.changeDynamic(i)
1113

12-
if greedy.count != dynamic.count {
13-
print("\(i): greedy = \(greedy) dynamic = \(dynamic)")
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+
}
1419
}
1520
}
21+
catch {
22+
XCTFail("Test Failed: impossible to change with the given coin set")
23+
}
1624
}
1725

1826
static var allTests = [

0 commit comments

Comments
 (0)