1 /* 2 * CombinatoricOperator.java 3 * 4 * Created on 7-mrt-2006 5 * 6 * Copyright (C) 2006 Hendrik Maryns <hendrik@sfs.uni-tuebingen.de>. 7 * 8 * This program is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU General Public License 10 * as published by the Free Software Foundation; either version 2 11 * of the License, or (at your option) any later version. 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 19 */ 20 package combinatorics; 21 22 import java.lang.reflect.Array; 23 import java.util.Collection; 24 import java.util.Iterator; 25 26 /** 27 * A common superclass for all combinatoric operators. Makes use of the 28 * template method pattern to define all others. </p> 29 * <p>Given an array of objects of type T, it returns all results of a certain 30 * combinatoric operation.</p> 31 * 32 * <p>For efficiency, I changed this class to use ints for the counters. 33 * This means that e.g. for permutations, the maximal size of the array is 34 * about 20. If larger arrays are needed, BigInteger should be used 35 * (reintroduced). 36 * 37 * TODO: make simultaneous iteration possible. 38 * 39 * @author <a href="mailto:hendrik.maryns@uni-tuebingen.de">Hendrik Maryns</a> 40 * @param <T> 41 * The type of the objects to operate on. 42 */ 43 public abstract class CombinatoricOperator<T> implements Iterator<T[]>, 44 Iterable<T[]> { 45 46 /** 47 * Initialise a new operator, with given elements and size of the arrays 48 * to be returned. 49 * 50 * @param elements 51 * The elements on which this combinatoric operator has to act. 52 * @param r 53 * The size of the arrays to compute. 54 * @pre r should not be smaller than 0. 55 * | 0 <= r 56 * @post The total number of iterations is set to the correct number. 57 * | new.getTotal() == initialiseTotal(); 58 * @post The number of variations left is set to the total number. 59 * | new.getNumLeft() == new.getTotal() 60 */ 61 protected CombinatoricOperator(final T[] elements, final int r) { 62 assert 0 <= r; 63 this.indices = new int[r]; 64 this.elements = elements.clone(); 65 this.total = (int)this.initialiseTotal(elements.length, r); 66 this.reset(); 67 } 68 69 /** 70 * The elements the operator works upon. 71 */ 72 protected T[] elements; 73 74 /** 75 * An integer array backing up the original one to keep track of the 76 * indices. 77 */ 78 protected int[] indices; 79 80 /** 81 * Initialise the array of indices. By default, it is initialised with 82 * incrementing integers. 83 */ 84 protected void initialiseIndices() { 85 for (int i = 0; i < this.indices.length; i++) { 86 this.indices[i] = i; 87 } 88 } 89 90 /** 91 * The variations still to go. 92 */ 93 private int numLeft; 94 95 /** 96 * The total number of variations to be computed. 97 */ 98 final private int total; 99 100 /** 101 * Compute the total number of elements to return. 102 * 103 * @param n 104 * The number of elements the operator works on. 105 * @param r 106 * The size of the arrays to return. 107 * @return The total number of elements is always bigger than 0. 108 * | result >= 0 109 */ 110 protected abstract double initialiseTotal(int n, int r); 111 112 /** 113 * Reset the iteration. 114 * 115 * @post The number of iterations to go is the same as the total number 116 * of iterations. 117 * | new.getNumLeft() == getTotal() 118 */ 119 public void reset() { 120 this.initialiseIndices(); 121 this.numLeft = this.total; 122 } 123 124 /** 125 * Return number of variations not yet generated. 126 * 127 * @return Return number of variations not yet generated. 128 */ 129 public double getNumLeft() { 130 return this.numLeft; 131 } 132 133 /** 134 * Return the total number of variations. 135 * 136 * @return The factorial of the number of elements divided by the 137 * factorials of the size of the variations and the number of 138 * elements minus the size of the variations. 139 * That is, with n the number of elements and r the size of the 140 * variations: n^r 141 */ 142 public double getTotal() { 143 return this.total; 144 } 145 146 /** 147 * Returns <tt>true</tt> if the iteration has more elements. This is the 148 * case if not all n! permutations have been covered. 149 * 150 * @return The number of permutations to go is bigger than zero. 151 * | result == getNumLeft() > 0; 152 * @see java.util.Iterator#hasNext() 153 */ 154 public boolean hasNext() { 155 return this.numLeft > 0; 156 } 157 158 /** 159 * Compute the next combination. 160 * 161 * @see java.util.Iterator#next() 162 */ 163 public T[] next() { 164 if (this.numLeft != this.total) { 165 this.computeNext(); 166 } 167 this.numLeft--; 168 return this.getResult(this.indices); 169 } 170 171 /** 172 * Compute the next array of indices. 173 */ 174 protected abstract void computeNext(); 175 176 /** 177 * Compute the result, based on the given array of indices. 178 * 179 * @param indexes 180 * An array of indices into the element array. 181 * @return An array consisting of the elements at positions of the given 182 * array. 183 * | result[i] == elements[indexes[i]] 184 */ 185 @SuppressWarnings("unchecked") 186 private T[] getResult(final int[] indexes) { 187 final T[] result = (T[]) Array.newInstance(this.elements.getClass() 188 .getComponentType(), indexes.length); 189 for (int i = 0; i < indexes.length; i++) { 190 result[i] = this.elements[indexes[i]]; 191 } 192 return result; 193 } 194 195 /** 196 * Not supported. 197 * 198 * @see java.util.Iterator#remove() 199 */ 200 public void remove() { 201 throw new UnsupportedOperationException(); 202 } 203 204 /** 205 * A combinatoric operator is itself an iterator. 206 * 207 * @return Itself. 208 * | result == this 209 * @see java.lang.Iterable#iterator() 210 */ 211 public Iterator<T[]> iterator() { 212 return this; 213 } 214 215 /** 216 * Compute the factorial of n. 217 */ 218 public static double factorial(final int n) { 219 double fact = 1; 220 for (int i = n; i > 1; i--) { 221 fact = fact*i; 222 } 223 return fact; 224 } 225 226 /** 227 * Return an array that contains all the elements of the given collection. 228 * Convenience method for clients of the class that have to transform 229 * collections. 230 * 231 * @param coll 232 * The collection to transform. 233 * @param type 234 * The type of the elements in the collection. Unfortunately, this 235 * method argument is necessary to create an array of the 236 * appropriate type. Blame Java generics. 237 * @return An array containing all elements of the collection. 238 * | for each element in result 239 * | coll.contains(element) 240 */ 241 @SuppressWarnings("unchecked") 242 public static <T> T[] collectionToArray(final Collection<T> coll, final Class<T> type) { 243 T[] result = (T[]) Array.newInstance(type, coll.size()); 244 result = coll.toArray(result); 245 return result; 246 } 247 248 }