Iterative Cartesian Product in Java

前端 未结 9 1842
既然无缘
既然无缘 2020-11-27 07:00

I want to compute the cartesian product of an arbitrary number of nonempty sets in Java.

I\'ve wrote that iterative code...

public s         


        
相关标签:
9条回答
  • 2020-11-27 07:38

    I've written a solution that doesn't require you to fill up a large collection in memory. Unfortunately, the code required is hundreds of lines long. You may have to wait until it appears in the Guava project (https://github.com/google/guava), which I hope will be by the end of the year. Sorry. :(

    Note that you may not need such a utility if the number of sets you're cartesian-producting is a fixed number known at compile time -- you could just use that number of nested for loops.

    EDIT: the code is released now.

    Sets.cartesianProduct()

    I think you'll be very happy with it. It only creates the individual lists as you ask for them; doesn't fill up memory with all MxNxPxQ of them.

    If you want to inspect the source, it's here.

    Enjoy!

    0 讨论(0)
  • 2020-11-27 07:43

    Index-based solution

    Working with the indices is a simple alternative that is fast and memory-efficient and can handle any number of sets. Implementing Iterable allows easy use in a for-each loop. See the #main method for a usage example.

    public class CartesianProduct implements Iterable<int[]>, Iterator<int[]> {
    
    private final int[] _lengths;
    private final int[] _indices;
    private boolean _hasNext = true;
    
    public CartesianProduct(int[] lengths) {
        _lengths = lengths;
        _indices = new int[lengths.length];
    }
    
    public boolean hasNext() {
        return _hasNext;
    }
    
    public int[] next() {
        int[] result = Arrays.copyOf(_indices, _indices.length);
        for (int i = _indices.length - 1; i >= 0; i--) {
            if (_indices[i] == _lengths[i] - 1) {
                _indices[i] = 0;
                if (i == 0) {
                    _hasNext = false;
                }
            } else {
                _indices[i]++;
                break;
            }
        }
        return result;
    }
    
    public Iterator<int[]> iterator() {
        return this;
    }
    
    public void remove() {
        throw new UnsupportedOperationException();
    }
    
    /**
     * Usage example. Prints out
     * 
     * <pre>
     * [0, 0, 0] a, NANOSECONDS, 1
     * [0, 0, 1] a, NANOSECONDS, 2
     * [0, 0, 2] a, NANOSECONDS, 3
     * [0, 0, 3] a, NANOSECONDS, 4
     * [0, 1, 0] a, MICROSECONDS, 1
     * [0, 1, 1] a, MICROSECONDS, 2
     * [0, 1, 2] a, MICROSECONDS, 3
     * [0, 1, 3] a, MICROSECONDS, 4
     * [0, 2, 0] a, MILLISECONDS, 1
     * [0, 2, 1] a, MILLISECONDS, 2
     * [0, 2, 2] a, MILLISECONDS, 3
     * [0, 2, 3] a, MILLISECONDS, 4
     * [0, 3, 0] a, SECONDS, 1
     * [0, 3, 1] a, SECONDS, 2
     * [0, 3, 2] a, SECONDS, 3
     * [0, 3, 3] a, SECONDS, 4
     * [0, 4, 0] a, MINUTES, 1
     * [0, 4, 1] a, MINUTES, 2
     * ...
     * </pre>
     */
    public static void main(String[] args) {
        String[] list1 = { "a", "b", "c", };
        TimeUnit[] list2 = TimeUnit.values();
        int[] list3 = new int[] { 1, 2, 3, 4 };
    
        int[] lengths = new int[] { list1.length, list2.length, list3.length };
        for (int[] indices : new CartesianProduct(lengths)) {
            System.out.println(Arrays.toString(indices) //
                    + " " + list1[indices[0]] //
                    + ", " + list2[indices[1]] //
                    + ", " + list3[indices[2]]);
        }
    }
    

    }

    0 讨论(0)
  • 2020-11-27 07:46

    Using Google Guava 19 and Java 8 is very simple:

    Say you have the List of all arrays you want to associate...

    public static void main(String[] args) {
      List<String[]> elements = Arrays.asList(
        new String[]{"John", "Mary"}, 
        new String[]{"Eats", "Works", "Plays"},
        new String[]{"Food", "Computer", "Guitar"}
      );
    
      // Create a list of immutableLists of strings
      List<ImmutableList<String>> immutableElements = makeListofImmutable(elements);
    
      // Use Guava's Lists.cartesianProduct, since Guava 19
      List<List<String>> cartesianProduct = Lists.cartesianProduct(immutableElements);
    
      System.out.println(cartesianProduct);
    }
    

    The method to make the list of immutable lists is as follows:

    /**
     * @param values the list of all profiles provided by the client in matrix.json
     * @return the list of ImmutableList to compute the Cartesian product of values
     */
    private static List<ImmutableList<String>> makeListofImmutable(List<String[]> values) {
      List<ImmutableList<String>> converted = new LinkedList<>();
      values.forEach(array -> {
        converted.add(ImmutableList.copyOf(array));
      });
      return converted;
    }
    

    The output is as follows:

    [
      [John, Eats, Food], [John, Eats, Computer], [John, Eats, Guitar],
      [John, Works, Food], [John, Works, Computer], [John, Works, Guitar], 
      [John, Plays, Food], [John, Plays, Computer], [John, Plays, Guitar],
      [Mary, Eats, Food], [Mary, Eats, Computer], [Mary, Eats, Guitar],
      [Mary, Works, Food], [Mary, Works, Computer], [Mary, Works, Guitar],
      [Mary, Plays, Food], [Mary, Plays, Computer], [Mary, Plays, Guitar]
    ]
    
    0 讨论(0)
提交回复
热议问题