Iterative Cartesian Product in Java

前端 未结 9 1872
既然无缘
既然无缘 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:23

    Here's an iterative, lazy implementation I wrote. The interface is very similar to Google's Sets.cartesianProduct, but it's a bit more flexible: it deals in Iterables instead of Sets. This code and its unit tests are at https://gist.github.com/1911614.

    /* Copyright 2012 LinkedIn Corp.
    
       Licensed under the Apache License, Version 2.0 (the "License");
       you may not use this file except in compliance with the License.
       You may obtain a copy of the License at
    
           http://www.apache.org/licenses/LICENSE-2.0
    
       Unless required by applicable law or agreed to in writing, software
       distributed under the License is distributed on an "AS IS" BASIS,
       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       See the License for the specific language governing permissions and
       limitations under the License.
     */
    
    import com.google.common.base.Function;
    import com.google.common.collect.Iterables;
    import java.lang.reflect.Array;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.Iterator;
    import java.util.List;
    import java.util.NoSuchElementException;
    
    /**
     * Implements the Cartesian product of ordered collections.
     * 
     * @author John Kristian
     */
    public class Cartesian {
      /**
       * Generate the Cartesian
       * product of the given axes. For axes [[a1, a2 ...], [b1, b2 ...], [c1, c2 ...]
       * ...] the product is [[a1, b1, c1 ...] ... [a1, b1, c2 ...] ... [a1, b2, c1 ...] ...
       * [aN, bN, cN ...]]. In other words, the results are generated in same order as these
       * nested loops:
       * 
       * 
       * for (T a : [a1, a2 ...])
       *   for (T b : [b1, b2 ...])
       *     for (T c : [c1, c2 ...])
       *       ...
       *         result = new T[]{ a, b, c ... };
       * 
    * * Each result is a new array of T, whose elements refer to the elements of the axes. If * you prefer a List, you can call asLists(product(axes)). *

    * Don't change the axes while iterating over their product, as a rule. Changes to an * axis can affect the product or cause iteration to fail (which is usually bad). To * prevent this, you can pass clones of your axes to this method. *

    * The implementation is lazy. This method iterates over the axes, and returns an * Iterable that contains a reference to each axis. Iterating over the product causes * iteration over each axis. Methods of each axis are called as late as practical. */ public static Iterable product(Class resultType, Iterable> axes) { return new Product(resultType, newArray(Iterable.class, axes)); } /** Works like product(resultType, Arrays.asList(axes)), but slightly more efficient. */ public static Iterable product(Class resultType, Iterable... axes) { return new Product(resultType, axes.clone()); } /** * Wrap the given arrays in fixed-size lists. Changes to the lists write through to the * arrays. */ public static Iterable> asLists(Iterable arrays) { return Iterables.transform(arrays, new AsList()); } /** * Arrays.asList, represented as a Function (as used in Google collections). */ public static class AsList implements Function> { @Override public List apply(T[] array) { return Arrays.asList(array); } } /** Create a generic array containing references to the given objects. */ private static T[] newArray(Class elementType, Iterable from) { List list = new ArrayList(); for (T f : from) list.add(f); return list.toArray(newArray(elementType, list.size())); } /** Create a generic array. */ @SuppressWarnings("unchecked") private static T[] newArray(Class elementType, int length) { return (T[]) Array.newInstance(elementType, length); } private static class Product implements Iterable { private final Class _resultType; private final Iterable[] _axes; /** Caution: the given array of axes is contained by reference, not cloned. */ Product(Class resultType, Iterable[] axes) { _resultType = resultType; _axes = axes; } @Override public Iterator iterator() { if (_axes.length <= 0) // an edge case return Collections.singleton(newArray(_resultType, 0)).iterator(); return new ProductIterator(_resultType, _axes); } @Override public String toString() { return "Cartesian.product(" + Arrays.toString(_axes) + ")"; } private static class ProductIterator implements Iterator { private final Iterable[] _axes; private final Iterator[] _iterators; // one per axis private final T[] _result; // a copy of the last result /** * The minimum index such that this.next() will return an array that contains * _iterators[index].next(). There are some special sentinel values: NEW means this * is a freshly constructed iterator, DONE means all combinations have been * exhausted (so this.hasNext() == false) and _iterators.length means the value is * unknown (to be determined by this.hasNext). */ private int _nextIndex = NEW; private static final int NEW = -2; private static final int DONE = -1; /** Caution: the given array of axes is contained by reference, not cloned. */ ProductIterator(Class resultType, Iterable[] axes) { _axes = axes; _iterators = Cartesian.> newArray(Iterator.class, _axes.length); for (int a = 0; a < _axes.length; ++a) { _iterators[a] = axes[a].iterator(); } _result = newArray(resultType, _iterators.length); } private void close() { _nextIndex = DONE; // Release references, to encourage garbage collection: Arrays.fill(_iterators, null); Arrays.fill(_result, null); } @Override public boolean hasNext() { if (_nextIndex == NEW) { // This is the first call to hasNext(). _nextIndex = 0; // start here for (Iterator iter : _iterators) { if (!iter.hasNext()) { close(); // no combinations break; } } } else if (_nextIndex >= _iterators.length) { // This is the first call to hasNext() after next() returned a result. // Determine the _nextIndex to be used by next(): for (_nextIndex = _iterators.length - 1; _nextIndex >= 0; --_nextIndex) { Iterator iter = _iterators[_nextIndex]; if (iter.hasNext()) { break; // start here } if (_nextIndex == 0) { // All combinations have been generated. close(); break; } // Repeat this axis, with the next value from the previous axis. iter = _axes[_nextIndex].iterator(); _iterators[_nextIndex] = iter; if (!iter.hasNext()) { // Oops; this axis can't be repeated. close(); // no more combinations break; } } } return _nextIndex >= 0; } @Override public T[] next() { if (!hasNext()) throw new NoSuchElementException("!hasNext"); for (; _nextIndex < _iterators.length; ++_nextIndex) { _result[_nextIndex] = _iterators[_nextIndex].next(); } return _result.clone(); } @Override public void remove() { for (Iterator iter : _iterators) { iter.remove(); } } @Override public String toString() { return "Cartesian.product(" + Arrays.toString(_axes) + ").iterator()"; } } } }

提交回复
热议问题