Cartesian product of arbitrary sets in Java

后端 未结 9 2224
小鲜肉
小鲜肉 2020-11-22 07:28

Do you know some neat Java libaries that allow you to make cartesian product of two (or more) sets?

For example: I have three sets. One with objects of class Person

9条回答
  •  情歌与酒
    2020-11-22 07:37

    Here is an Iterable, which allows you to use a simplified for-loop:

    import java.util.*;
    
    // let's begin with the demo. Instead of Person and Gift, 
    // I use the well known char and int. 
    class CartesianIteratorTest {
    
        public static void main (String[] args) {
            List  lc = Arrays.asList (new Object [] {'A', 'B', 'C', 'D'});
            List  lC = Arrays.asList (new Object [] {'a', 'b', 'c'});   
            List  li = Arrays.asList (new Object [] {1, 2, 3, 4});
                // sometimes, a generic solution like List >
                // might be possible to use - typically, a mixture of types is 
                // the common nominator 
            List > llo = new ArrayList > ();
            llo.add (lc);
            llo.add (lC);
            llo.add (li);
    
            // Preparing the List of Lists is some work, but then ...    
            CartesianIterable  ci = new CartesianIterable  (llo);
    
            for (List  lo: ci)
                show (lo);
        }
    
        public static void show (List  lo) {
            System.out.print ("(");
            for (Object o: lo)
                System.out.print (o + ", ");
            System.out.println (")");
        }
    }
    
    
    

    How is it done? We need an Iterable, to use the simplified for-loop, and an Iterator has to be returned from the Iterable. We return a List of Objects - this could be a Set instead of List, but Set has no indexed access, so it would be a bit more complicated, to implement it with Set instead of List. Instead of a generic solution, Object would have been fine for many purposes, but generics allow for more restrictions.

    class CartesianIterator  implements Iterator > {
    
        private final List > lilio;    
        private int current = 0;
        private final long last;
    
        public CartesianIterator (final List > llo) {
            lilio = llo;
            long product = 1L;
            for (List  lio: lilio)
                product *= lio.size ();
            last = product;
        } 
    
        public boolean hasNext () {
            return current != last;
        }
    
        public List  next () {
            ++current;
            return get (current - 1, lilio);
        }
    
        public void remove () {
            ++current;
        }
    
        private List get (final int n, final List > lili) {
            switch (lili.size ())
            {
                case 0: return new ArrayList  (); // no break past return;
                default: {
                    List  inner = lili.get (0);
                    List  lo = new ArrayList  ();
                    lo.add (inner.get (n % inner.size ()));
                    lo.addAll (get (n / inner.size (), lili.subList (1, lili.size ())));
                    return lo;
                }
            }
        }
    }
    

    The mathematical work is done in the 'get'-method. Think about 2 sets of 10 elements. You have a total of 100 combinations, enumerated from 00, 01, 02, ... 10, ... to 99. For 5 X 10 elements 50, for 2 X 3 elements 6 combinations. The modulo of the sublist size helps to pick one element for each iteration.

    Iterable i the least interesting thing here:

    class CartesianIterable  implements Iterable > {
    
        private List > lilio;  
    
        public CartesianIterable (List > llo) {
            lilio = llo;
        }
    
        public Iterator > iterator () {
            return new CartesianIterator  (lilio);
        }
    }
    

    To implement Iterable, which allows the for-each kind of loop, we have to implement iterator (), and for Iterator we have to implement hasNext (), next () and remove ().

    Result:

    (A, a, 1, )
    (B, a, 1, )
    (C, a, 1, )
    (D, a, 1, )
    (A, b, 1, )
    (B, b, 1, )
    (C, b, 1, )
    (D, b, 1, )
    ...
    (A, a, 2, )
    ...
    (C, c, 4, )
    (D, c, 4, )
    

    提交回复
    热议问题