瀏覽代碼

change: add to track

Stuart Kent 9 年之前
父節點
當前提交
5b5d8dd236

+ 7
- 1
config.json 查看文件

60
     "matrix",
60
     "matrix",
61
     "diamond",
61
     "diamond",
62
     "secret-handshake",
62
     "secret-handshake",
63
-    "flatten-array"
63
+    "flatten-array",
64
+    "change"
64
   ],
65
   ],
65
   "exercises": [
66
   "exercises": [
66
     {
67
     {
347
       "slug": "flatten-array",
348
       "slug": "flatten-array",
348
       "difficulty": 1,
349
       "difficulty": 1,
349
       "topics": []
350
       "topics": []
351
+    },
352
+    {
353
+      "slug": "change",
354
+      "difficulty": 1,
355
+      "topics": []
350
     }
356
     }
351
   ],
357
   ],
352
   "deprecated": [
358
   "deprecated": [

+ 17
- 0
exercises/change/build.gradle 查看文件

1
+apply plugin: "java"
2
+apply plugin: "eclipse"
3
+apply plugin: "idea"
4
+
5
+repositories {
6
+  mavenCentral()
7
+}
8
+
9
+dependencies {
10
+  testCompile "junit:junit:4.12"
11
+}
12
+test {
13
+  testLogging {
14
+    exceptionFormat = 'full'
15
+    events = ["passed", "failed", "skipped"]
16
+  }
17
+}

+ 63
- 0
exercises/change/src/example/java/ChangeCalculator.java 查看文件

1
+import java.util.*;
2
+import java.util.stream.Collectors;
3
+
4
+import static java.util.Comparator.comparingInt;
5
+
6
+final class ChangeCalculator {
7
+
8
+    private final List<Integer> currencyCoins;
9
+
10
+    ChangeCalculator(final List<Integer> currencyCoins) {
11
+        this.currencyCoins = currencyCoins;
12
+        Collections.sort(currencyCoins);
13
+    }
14
+
15
+    List<Integer> computeMostEfficientChange(final int grandTotal) {
16
+        if (grandTotal < 0) {
17
+            throw new IllegalArgumentException("Negative totals are not allowed.");
18
+        }
19
+
20
+        final Map<Integer, List<Integer>> minimalCoinsMap = new HashMap<>();
21
+        minimalCoinsMap.put(0, new ArrayList<>());
22
+
23
+        for (int total = 1; total <= grandTotal; total++) {
24
+            final int localTotal = total;
25
+
26
+            final List<Integer> minimalCoins = getCoinsNoLargerThan(total)
27
+                    .stream()
28
+                    .map(coin -> {
29
+                        final List<Integer> minimalRemainderCoins = minimalCoinsMap.get(localTotal - coin);
30
+                        return minimalRemainderCoins != null ? prepend(coin, minimalRemainderCoins) : null;
31
+                    })
32
+                    .filter(Objects::nonNull)
33
+                    .sorted(comparingInt(List::size))
34
+                    .findFirst()
35
+                    .orElse(null);
36
+
37
+            minimalCoinsMap.put(localTotal, minimalCoins);
38
+        }
39
+
40
+        final List<Integer> resultCandidate = minimalCoinsMap.get(grandTotal);
41
+
42
+        if (resultCandidate == null) {
43
+            throw new IllegalArgumentException(
44
+                    "The total " + grandTotal + " cannot be represented in the given currency.");
45
+        }
46
+
47
+        return resultCandidate;
48
+    }
49
+
50
+    private List<Integer> getCoinsNoLargerThan(final int threshold) {
51
+        return currencyCoins.stream()
52
+                .filter(coin -> coin <= threshold)
53
+                .collect(Collectors.toList());
54
+    }
55
+
56
+    private List<Integer> prepend(final int integer, final List<Integer> integers) {
57
+        final List<Integer> result = new ArrayList<>();
58
+        result.add(integer);
59
+        result.addAll(integers);
60
+        return result;
61
+    }
62
+
63
+}

+ 5
- 0
exercises/change/src/main/java/ChangeCalculator.java 查看文件

1
+final class ChangeCalculator {
2
+
3
+
4
+
5
+}

+ 103
- 0
exercises/change/src/test/java/ChangeCalculatorTest.java 查看文件

1
+import org.junit.Ignore;
2
+import org.junit.Rule;
3
+import org.junit.Test;
4
+import org.junit.rules.ExpectedException;
5
+
6
+import static java.util.Arrays.asList;
7
+import static java.util.Collections.emptyList;
8
+import static java.util.Collections.singletonList;
9
+import static org.junit.Assert.assertEquals;
10
+
11
+public final class ChangeCalculatorTest {
12
+
13
+    /*
14
+     * See https://github.com/junit-team/junit4/wiki/Rules for information on JUnit Rules in general and
15
+     * ExpectedExceptions in particular.
16
+     */
17
+    @Rule
18
+    public ExpectedException expectedException = ExpectedException.none();
19
+
20
+    @Test
21
+    public void testChangeThatCanBeGivenInASingleCoin() {
22
+        ChangeCalculator changeCalculator = new ChangeCalculator(asList(1, 5, 10, 25, 100));
23
+
24
+        assertEquals(
25
+                singletonList(25),
26
+                changeCalculator.computeMostEfficientChange(25));
27
+    }
28
+
29
+    @Ignore
30
+    @Test
31
+    public void testChangeThatMustBeGivenInMultipleCoins() {
32
+        ChangeCalculator changeCalculator = new ChangeCalculator(asList(1, 5, 10, 25, 100));
33
+
34
+        assertEquals(
35
+                asList(5, 10),
36
+                changeCalculator.computeMostEfficientChange(15));
37
+    }
38
+
39
+    @Ignore
40
+    @Test
41
+    // https://en.wikipedia.org/wiki/Change-making_problem#Greedy_method
42
+    public void testLilliputianCurrencyForWhichGreedyAlgorithmFails() {
43
+        ChangeCalculator changeCalculator = new ChangeCalculator(asList(1, 4, 15, 20, 50));
44
+
45
+        assertEquals(
46
+                asList(4, 4, 15),
47
+                changeCalculator.computeMostEfficientChange(23));
48
+    }
49
+
50
+    @Ignore
51
+    @Test
52
+    // https://en.wikipedia.org/wiki/Change-making_problem#Greedy_method
53
+    public void testLowerElbonianCurrencyForWhichGreedyAlgorithmFails() {
54
+        ChangeCalculator changeCalculator = new ChangeCalculator(asList(1, 5, 10, 21, 25));
55
+
56
+        assertEquals(
57
+                asList(21, 21, 21),
58
+                changeCalculator.computeMostEfficientChange(63));
59
+    }
60
+
61
+    @Ignore
62
+    @Test
63
+    public void testLargeAmountOfChange() {
64
+        ChangeCalculator changeCalculator = new ChangeCalculator(asList(1, 2, 5, 10, 20, 50, 100));
65
+
66
+        assertEquals(
67
+                asList(2, 2, 5, 20, 20, 50, 100, 100, 100, 100, 100, 100, 100, 100, 100),
68
+                changeCalculator.computeMostEfficientChange(999));
69
+    }
70
+
71
+    @Ignore
72
+    @Test
73
+    public void testZeroChange() {
74
+        ChangeCalculator changeCalculator = new ChangeCalculator(asList(1, 5, 10, 21, 25));
75
+
76
+        assertEquals(
77
+                emptyList(),
78
+                changeCalculator.computeMostEfficientChange(0));
79
+    }
80
+
81
+    @Ignore
82
+    @Test
83
+    public void testChangeLessThanSmallestCoinInCurrencyCannotBeRepresented() {
84
+        ChangeCalculator changeCalculator = new ChangeCalculator(asList(5, 10));
85
+
86
+        expectedException.expect(IllegalArgumentException.class);
87
+        expectedException.expectMessage("The total 3 cannot be represented in the given currency.");
88
+
89
+        changeCalculator.computeMostEfficientChange(3);
90
+    }
91
+
92
+    @Ignore
93
+    @Test
94
+    public void testNegativeChangeIsRejected() {
95
+        ChangeCalculator changeCalculator = new ChangeCalculator(asList(1, 2, 5));
96
+
97
+        expectedException.expect(IllegalArgumentException.class);
98
+        expectedException.expectMessage("Negative totals are not allowed.");
99
+
100
+        changeCalculator.computeMostEfficientChange(-5);
101
+    }
102
+
103
+}

+ 1
- 0
exercises/settings.gradle 查看文件

10
 include 'binary-search-tree'
10
 include 'binary-search-tree'
11
 include 'bob'
11
 include 'bob'
12
 include 'bracket-push'
12
 include 'bracket-push'
13
+include 'change'
13
 include 'crypto-square'
14
 include 'crypto-square'
14
 include 'custom-set'
15
 include 'custom-set'
15
 include 'diamond'
16
 include 'diamond'