Просмотр исходного кода

Merge pull request #273 from exercism/change

change: add to track
FridaTveit 9 лет назад
Родитель
Сommit
bd611c38bd

+ 7
- 1
config.json Просмотреть файл

@@ -62,7 +62,8 @@
62 62
     "diamond",
63 63
     "secret-handshake",
64 64
     "flatten-array",
65
-    "perfect-numbers"
65
+    "perfect-numbers",
66
+    "change"
66 67
   ],
67 68
   "exercises": [
68 69
     {
@@ -359,6 +360,11 @@
359 360
       "slug": "perfect-numbers",
360 361
       "difficulty": 1,
361 362
       "topics": []
363
+    },
364
+    {
365
+      "slug": "change",
366
+      "difficulty": 1,
367
+      "topics": []
362 368
     }
363 369
   ],
364 370
   "deprecated": [

+ 17
- 0
exercises/change/build.gradle Просмотреть файл

@@ -0,0 +1,17 @@
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 Просмотреть файл

@@ -0,0 +1,63 @@
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 Просмотреть файл

@@ -0,0 +1,5 @@
1
+final class ChangeCalculator {
2
+
3
+
4
+
5
+}

+ 114
- 0
exercises/change/src/test/java/ChangeCalculatorTest.java Просмотреть файл

@@ -0,0 +1,114 @@
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 testChangeLargerThanAllCoinsInCurrencyThatCannotBeRepresented() {
95
+        ChangeCalculator changeCalculator = new ChangeCalculator(asList(5, 10));
96
+
97
+        expectedException.expect(IllegalArgumentException.class);
98
+        expectedException.expectMessage("The total 94 cannot be represented in the given currency.");
99
+
100
+        changeCalculator.computeMostEfficientChange(94);
101
+    }
102
+
103
+    @Ignore
104
+    @Test
105
+    public void testNegativeChangeIsRejected() {
106
+        ChangeCalculator changeCalculator = new ChangeCalculator(asList(1, 2, 5));
107
+
108
+        expectedException.expect(IllegalArgumentException.class);
109
+        expectedException.expectMessage("Negative totals are not allowed.");
110
+
111
+        changeCalculator.computeMostEfficientChange(-5);
112
+    }
113
+
114
+}

+ 1
- 0
exercises/settings.gradle Просмотреть файл

@@ -10,6 +10,7 @@ include 'binary-search'
10 10
 include 'binary-search-tree'
11 11
 include 'bob'
12 12
 include 'bracket-push'
13
+include 'change'
13 14
 include 'clock'
14 15
 include 'crypto-square'
15 16
 include 'custom-set'