Reputation: 651
I want to find cartesian product of set of elements. Here's an example
example 1:
sets : (ab) (bc) (ca)
cartesian product is:
abc aba acc aca bbc bba bcc bca
example 2:
sets : (zyx) b c
cartesian product is:
zbc ybc xbc
So I am thinking of an algorithm to execute in Java which can find cartesian product of particular amount of groups defined at compile time at the start.
Upvotes: 5
Views: 4844
Reputation:
Note: A Set
is a collection that contains no duplicate elements. If you have duplicate elements in different sets, then each set from the Cartesian product will contain only one of them.
You can create a generic method to get a Cartesian product and specify the types of collections to store it. For example, a Set
or a List
.
The map
method represents each element of a collection as a singleton collection and specifies the format of the result.
The reduce
method sums pairs of 2D collections into a single 2D collection.
public static void main(String[] args) {
List<Set<String>> sets = List.of(
Set.of("A", "B"), Set.of("B", "C"), Set.of("C", "A"));
List<Set<String>> cpSet = cartesianProduct(HashSet::new, sets);
List<List<String>> cpList = cartesianProduct(ArrayList::new, sets);
// output, order may vary
System.out.println(toString(cpSet));
//ABC, AB, AC, AC, BC, BA, BC, BCA
System.out.println(toString(cpList));
//ABC, ABA, ACC, ACA, BBC, BBA, BCC, BCA
}
/**
* @param cols the input collection of collections
* @param nCol the supplier of the output collection
* @param <E> the type of the element of the collection
* @param <R> the type of the return collections
* @return List<R> the cartesian product of the multiple collections
*/
public static <E, R extends Collection<E>> List<R> cartesianProduct(
Supplier<R> nCol, Collection<? extends Collection<E>> cols) {
// check if the input parameters are not null
if (nCol == null || cols == null) return null;
return cols.stream()
// non-null and non-empty collections
.filter(col -> col != null && col.size() > 0)
// represent each element of a collection as a singleton collection
.map(col -> col.stream()
.map(e -> Stream.of(e).collect(Collectors.toCollection(nCol)))
// Stream<List<R>>
.collect(Collectors.toList()))
// summation of pairs of inner collections
.reduce((col1, col2) -> col1.stream()
// combinations of inner collections
.flatMap(inner1 -> col2.stream()
// concatenate into a single collection
.map(inner2 -> Stream.of(inner1, inner2)
.flatMap(Collection::stream)
.collect(Collectors.toCollection(nCol))))
// list of combinations
.collect(Collectors.toList()))
// otherwise an empty list
.orElse(Collections.emptyList());
}
// supplementary method, returns a formatted string
static <E extends String> String toString(List<? extends Collection<E>> cols) {
return cols.stream().map(col -> String.join("", col))
.collect(Collectors.joining(", "));
}
See also: Cartesian product of an arbitrary number of sets
Upvotes: 0
Reputation: 1948
You can use the Sets.cartesianProduct()
method from Google's Guava libraries to generate Cartesian products:
com.google.common.collect.Sets.cartesianProduct(Set[] yourSets)
If only everything was that easy!
Upvotes: 9
Reputation: 36229
Define your own Iterator/Iterable:
import java.util.*;
class CartesianIterator <T> implements Iterator <List <T>> {
private final List <List <T>> lilio;
private int current = 0;
private final long last;
public CartesianIterator (final List <List <T>> llo) {
lilio = llo;
long product = 1L;
for (List <T> lio: lilio)
product *= lio.size ();
last = product;
}
public boolean hasNext () {
return current != last;
}
public List <T> next () {
++current;
return get (current - 1, lilio);
}
public void remove () {
++current;
}
private List<T> get (final int n, final List <List <T>> lili) {
switch (lili.size ())
{
case 0: return new ArrayList <T> (); // no break past return;
default: {
List <T> inner = lili.get (0);
List <T> lo = new ArrayList <T> ();
lo.add (inner.get (n % inner.size ()));
lo.addAll (get (n / inner.size (), lili.subList (1, lili.size ())));
return lo;
}
}
}
}
class CartesianIterable <T> implements Iterable <List <T>> {
private List <List <T>> lilio;
public CartesianIterable (List <List <T>> llo) {
lilio = llo;
}
public Iterator <List <T>> iterator () {
return new CartesianIterator <T> (lilio);
}
}
And test it with your Data:
class CartesianIteratorTest {
public static void main (String[] args) {
List <Character> la = Arrays.asList (new Character [] {'a', 'b'});
List <Character> lb = Arrays.asList (new Character [] {'b', 'c'});
List <Character> lc = Arrays.asList (new Character [] {'c', 'a'});
List <List <Character>> llc = new ArrayList <List <Character>> ();
llc.add (la);
llc.add (lb);
llc.add (lc);
CartesianIterable <Character> ci = new CartesianIterable <Character> (llc);
for (List<Character> lo: ci)
show (lo);
la = Arrays.asList (new Character [] {'x', 'y', 'z'});
lb = Arrays.asList (new Character [] {'b'});
lc = Arrays.asList (new Character [] {'c'});
llc = new ArrayList <List <Character>> ();
llc.add (la);
llc.add (lb);
llc.add (lc);
ci = new CartesianIterable <Character> (llc);
for (List<Character> lo: ci)
show (lo);
}
public static void show (List <Character> lo) {
System.out.print ("(");
for (Object o: lo)
System.out.print (o);
System.out.println (")");
}
}
Result:
(abc)
(bbc)
(acc)
(bcc)
(aba)
(bba)
(aca)
(bca)
(xbc)
(ybc)
(zbc)
Upvotes: 2