diff --git a/src/misc/NChooseK.java b/src/misc/NChooseK.java index e4f7457fe1f3a986594d8d426769b68ba54e9b5c..1ce2a55d956fc6a0e2b95b835ed5f5411a97885f 100644 --- a/src/misc/NChooseK.java +++ b/src/misc/NChooseK.java @@ -4,60 +4,43 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -class NChooseK { +class NChooseK { + List L = new ArrayList<>(); + /** - Calculates the binomial coefficient {@code n} choose {@code k}: the number - of {@code k}-element subsets that can be selected from an {@code n}-element - set. + Generates {@code k}-element combinations from an {@code n}-element set (i.e. + {@code n} choose {@code k}). +

Asymptotic analysis:

• time_worst=O(n^k)
• space_worst=O(n)
- @param n a number of possibilities - @param k a number of outcomes - @return the binomial coefficient {@code n} choose {@code k} + @param A the target array + @param k the number of elements per combination + @return the {@code k}-element combinations of {@code A} */ - int rec(int n, int k) { - return (k == 0 || k == n) ? 1 : rec(n - 1, k) + rec(n - 1, k - 1); + List generate(T[] A, int k) { + helper(A, A.length, k, 0, 0); + return L; } - class Combinator { - List L = new ArrayList<>(); - - /** - Asymptotic analysis: -
-
• time_worst=O(n^k) -
• space_worst=O(n) -
- - @param A the target array - @param k the number of combinations - @return the {@code k}-element combinations of {@code A} - */ - List generate(T[] A, int k) { - helper(A, 0, k, 0); - return L; - } - - private void helper(T[] A, int n, int k, int start) { - if (n == k) { - L.add(Arrays.copyOfRange(A, 0, k)); - } else { - for (int i = start; i < A.length; i++) { - swap(A, i, n); - helper(A, n + 1, k, i + 1); - // backtrack - swap(A, i, n); - } + private void helper(T[] A, int n, int k, int i, int j) { + if (i == k) { + L.add(Arrays.copyOfRange(A, 0, k)); + } else { + if (j < n) { + swap(A, i, j); + helper(A, n, k, i + 1, j + 1); + helper(A, n, k, i, j + 1); + swap(A, i, j); } } + } - private void swap(T[] A, int i, int j) { - T t = A[i]; - A[i] = A[j]; - A[j] = t; - } + private void swap(T[] A, int i, int j) { + T t = A[i]; + A[i] = A[j]; + A[j] = t; } } diff --git a/testsrc/unit/misc/NChooseKTest.java b/testsrc/unit/misc/NChooseKTest.java index a32502f5a89ec80fbf8d4da3e395d28d2927c649..e32fc2c4e3dc502ad31157eca399e051d9a6eee5 100644 --- a/testsrc/unit/misc/NChooseKTest.java +++ b/testsrc/unit/misc/NChooseKTest.java @@ -1,8 +1,6 @@ package misc; -import misc.NChooseK.Combinator; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import java.util.Arrays; @@ -13,79 +11,53 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class NChooseKTest { - private NChooseK chooseK; + private NChooseK nChooseK; + private Integer[] A; @BeforeEach void beforeEach() { - chooseK = new NChooseK(); + nChooseK = new NChooseK<>(); + A = new Integer[] { 1, 2, 3, 4 }; } @Test - void itCalculatesTheBinomialCoefficient() { - assertEquals(1, chooseK.rec(5, 0)); - assertEquals(5, chooseK.rec(5, 1)); - assertEquals(10, chooseK.rec(5, 2)); - assertEquals(10, chooseK.rec(5, 3)); - assertEquals(5, chooseK.rec(5, 4)); - assertEquals(1, chooseK.rec(5, 5)); + void itGeneratesOneCombinationWithZeroElements() { + var expected = "[[]]"; + var actual = toS(nChooseK.generate(A, 0)); + assertEquals(expected, actual); } @Test - void itCalculatesTheBaseCase() { - assertEquals(1, chooseK.rec(1, 0)); - assertEquals(1, chooseK.rec(1, 1)); + void itGeneratesFourCombinationsWithOneElement() { + var expected = "[[1], [2], [3], [4]]"; + var actual = toS(nChooseK.generate(A, 1)); + assertEquals(expected, actual); } - @Nested - class WhenItGeneratesCombinations { - private Combinator combinator; - private Integer[] A; - - @BeforeEach - void beforeEach() { - combinator = chooseK.new Combinator<>(); - A = new Integer[] { 1, 2, 3, 4 }; - } - - @Test - void itGeneratesOneCombinationWithZeroElements() { - var expected = "[[]]"; - var actual = toS(combinator.generate(A, 0)); - assertEquals(expected, actual); - } - - @Test - void itGeneratesFourCombinationsWithOneElement() { - var expected = "[[1], [2], [3], [4]]"; - var actual = toS(combinator.generate(A, 1)); - assertEquals(expected, actual); - } - - @Test - void itGeneratesSixCombinationsWithTwoElements() { - var expected = "[[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]"; - var actual = toS(combinator.generate(A, 2)); - assertEquals(expected, actual); - } + @Test + void itGeneratesSixCombinationsWithTwoElements() { + var expected = "[[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]"; + var actual = toS(nChooseK.generate(A, 2)); + assertEquals(expected, actual); + } - @Test - void itGeneratesFourCombinationsWithThreeElements() { - var expected = "[[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]"; - var actual = toS(combinator.generate(A, 3)); - assertEquals(expected, actual); - } + @Test + void itGeneratesFourCombinationsWithThreeElements() { + var expected = "[[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]"; + var actual = toS(nChooseK.generate(A, 3)); + assertEquals(expected, actual); + } - @Test - void itGeneratesOneCombinationWithFourElements() { - var expected = "[[1, 2, 3, 4]]"; - var actual = toS(combinator.generate(A, 4)); - assertEquals(expected, actual); - } + @Test + void itGeneratesOneCombinationWithFourElements() { + var expected = "[[1, 2, 3, 4]]"; + var actual = toS(nChooseK.generate(A, 4)); + assertEquals(expected, actual); + } - @Test - void itGeneratesZeroCombinations() { - assertTrue(combinator.generate(A, 5).isEmpty()); - } + @Test + void itGeneratesZeroCombinationsWithFiveElements() { + assertTrue(nChooseK.generate(A, 5).isEmpty()); } private String toS(List L) {