HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Combination generator in Java - 2nd iteration

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
combinationjavageneratoriteration2nd

Problem

I have refactored the previous combination generator, and now it is an iterator returning combinations as lists. It hides larger constant factors as the previous version, yet it is really easy to use:

for (List combination : new CombinationIterable<>(allStrings)) {
    System.out.println(combination);
}


CombinationIterable.java:

```
package net.coderodde.util;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

public class CombinationIterable implements Iterable> {

private final List allElements;

public CombinationIterable(List allElements) {
this.allElements = new ArrayList<>(allElements);
}

@Override
public Iterator> iterator() {
return new CombinationIterator<>(allElements);
}

private static final class CombinationIterator
implements Iterator> {

private final List allElements;
private final int[] indices;
private List nextCombination;
private int currentCombinationSize;

CombinationIterator(List allElements) {
this.allElements = new ArrayList<>(allElements);
this.indices = new int[allElements.size()];

if (!allElements.isEmpty()) {
// Create the first combination.
currentCombinationSize = 1;
nextCombination = new ArrayList<>(1);
nextCombination.add(allElements.get(0));
}
}

@Override
public boolean hasNext() {
return nextCombination != null;
}

@Override
public List next() {
if (nextCombination == null) {
throw new NoSuchElementException("No combinations left.");
}

List combination = nextCombination;
generateNextCombination();
return combination;
}

private void loadCombination() {
List combination = new ArrayList<>(c

Solution

Only minor remarks - not so much related to the code itself, but rather to the concepts:

In your previous question, you emphasized that the combinations should be returned in lexicographic order. From my understanding, this is not the case: The output is

1: [A]
 2: [B]
 3: [C]
 4: [D]
 5: [E]
 6: [A, B]
 7: [A, C]
 8: [A, D]
 9: [A, E]
...


Imagining these as "words in a dictionary" (in line with the Wikipedia page about lexicographical_order), "AB" would come before "B" - but maybe this is just a misinterpretation on my side, and you are considering the words to be "filled with some character that comes before 'A'" at the beginning, as in

____A
____B
____C
...
___AB
___AC
...
_ABCD
_ABCE
...
ABCDE


The word "Combination" usually has a predefined meaning that differs from how you use it: A Combination is usually a selection of a certain number of elements from a given set. (Differentiated between "combinations with repetition" and "combinations without repetition").

What you are computing there are actually the elements of the Power Set of the given list (which usually also involves the empty list - but this is just a detail).

This is also what the comments referred to: When looking closely at the elements of the power set, you'll see a resemblance of these elements and the bit patterns of the binary representations of numbers:

EDCBA   Result:
0   binary:    00000   {     }
1   binary:    00001   {    A}
2   binary:    00010   {   B }
3   binary:    00011   {   BA}
...
9   binary:    01001   { D  A}
...
31  binary:    11111   {EDCBA}


This can be imagined as "taking the elements into the result when the binary representation of the corresponding number has a '1' at the respective position".

Unfortunately, the order would then be different from your current one, so this may not be applicable here.

In terms of API design, there is probably not much more to say: The implementation as an Iterable makes it very easy to use it, as there are only two (relevant) public methods with well-known semantics, and, as far as I can see, they are implemented properly. One could consider different, minor restructurings of the private parts, but none that would objectively be "better" than the current solution.

A side note: A while ago I created some similar classes at https://github.com/javagl/Combinatorics . They are also implemented as Iterables, including a CombinationIterable, but as mentioned above, this computes combinations - in contrast to the PowerSetIterable, which computes the power set that you actually seem to be looking for - it contains a few words about the implementation using the binary number representations.

Code Snippets

1: [A]
 2: [B]
 3: [C]
 4: [D]
 5: [E]
 6: [A, B]
 7: [A, C]
 8: [A, D]
 9: [A, E]
...
____A
____B
____C
...
___AB
___AC
...
_ABCD
_ABCE
...
ABCDE
EDCBA   Result:
0   binary:    00000   {     }
1   binary:    00001   {    A}
2   binary:    00010   {   B }
3   binary:    00011   {   BA}
...
9   binary:    01001   { D  A}
...
31  binary:    11111   {EDCBA}

Context

StackExchange Code Review Q#119887, answer score: 2

Revisions (0)

No revisions yet.