001// ============================================================================ 002// Copyright 2006-2012 Daniel W. Dyer 003// 004// Licensed under the Apache License, Version 2.0 (the "License"); 005// you may not use this file except in compliance with the License. 006// You may obtain a copy of the License at 007// 008// http://www.apache.org/licenses/LICENSE-2.0 009// 010// Unless required by applicable law or agreed to in writing, software 011// distributed under the License is distributed on an "AS IS" BASIS, 012// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013// See the License for the specific language governing permissions and 014// limitations under the License. 015// ============================================================================ 016package squidpony.squidmath; 017 018import java.io.Serializable; 019import java.math.BigInteger; 020import java.util.ArrayList; 021import java.util.Collection; 022import java.util.Iterator; 023import java.util.List; 024 025/** 026 * Combination generator for generating all combinations of a given size from 027 * the specified set of elements. For performance reasons, this implementation 028 * is restricted to operating with set sizes and combination lengths that produce 029 * no more than 2^63 different combinations. 030 * <br> 031 * Originally part of the <a href="http://maths.uncommons.org/">Uncommon Maths software package</a>. 032 * @param <T> The type of element that the combinations are made from. 033 * @author Daniel Dyer (modified from the original version written by Michael 034 * Gilleland of Merriam Park Software - 035 * <a href="http://www.merriampark.com/perm.htm">http://www.merriampark.com/comb.htm</a>). 036 * @see PermutationGenerator 037 */ 038public class CombinationGenerator<T> implements Iterable<List<T>>, Serializable 039{ 040 private static final long serialVersionUID = 5998145341506278361L; 041 042 private final T[] elements; 043 private final int[] combinationIndices; 044 private long remainingCombinations; 045 private long totalCombinations; 046 047 private CombinationGenerator() 048 { 049 elements = null; 050 combinationIndices = null; 051 }; 052 /** 053 * Create a combination generator that generates all combinations of 054 * a specified length from the given set. 055 * @param elements The set from which to generate combinations; will be used directly (not copied) 056 * @param combinationLength The length of the combinations to be generated. 057 */ 058 public CombinationGenerator(T[] elements, 059 int combinationLength) 060 { 061 if (combinationLength > elements.length) 062 { 063 throw new IllegalArgumentException("Combination length cannot be greater than set size."); 064 } 065 066 this.elements = elements; 067 this.combinationIndices = new int[combinationLength]; 068 069 BigInteger sizeFactorial = MathExtras.bigFactorial(elements.length); 070 BigInteger lengthFactorial = MathExtras.bigFactorial(combinationLength); 071 BigInteger differenceFactorial = MathExtras.bigFactorial(elements.length - combinationLength); 072 BigInteger total = sizeFactorial.divide(differenceFactorial.multiply(lengthFactorial)); 073 074 if (total.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) 075 { 076 throw new IllegalArgumentException("Total number of combinations must not be more than 2^63."); 077 } 078 079 totalCombinations = total.longValue(); 080 reset(); 081 } 082 083 084 /** 085 * Create a combination generator that generates all combinations of 086 * a specified length from the given set. 087 * @param elements The set from which to generate combinations. 088 * @param combinationLength The length of the combinations to be generated. 089 * @param filler An array of T with the same length as elements or less (often 0); 090 * needed because GWT can't create a generic array. If elements is not 091 * a Collection defined in the JDK (by GWT), then this should have 092 * exactly the same length as elements, since GWT can create larger 093 * versions of an array on its own, but our code can't easily. 094 */ 095 public CombinationGenerator(Collection<T> elements, 096 int combinationLength, 097 T[] filler) 098 { 099 this(elements.toArray(filler), combinationLength); 100 } 101 102 103 /** 104 * Reset the combination generator. 105 */ 106 public final void reset() 107 { 108 for (int i = 0; i < combinationIndices.length; i++) 109 { 110 combinationIndices[i] = i; 111 } 112 remainingCombinations = totalCombinations; 113 } 114 115 116 /** 117 * @return The number of combinations not yet generated. 118 */ 119 public long getRemainingCombinations() 120 { 121 return remainingCombinations; 122 } 123 124 125 /** 126 * Are there more combinations? 127 * @return true if there are more combinations available, false otherwise. 128 */ 129 public boolean hasMore() 130 { 131 return remainingCombinations > 0; 132 } 133 134 135 /** 136 * @return The total number of combinations. 137 */ 138 public long getTotalCombinations() 139 { 140 return totalCombinations; 141 } 142 143 144 /** 145 * Generate the next combination and return an array containing 146 * the appropriate elements. This overloaded method allows the caller 147 * to provide an array that will be used and returned. 148 * The purpose of this is to improve performance when iterating over 149 * combinations. This method allows a single array instance to be reused. 150 * @param destination Provides an array to use to create the 151 * combination. The specified array must be the same length as a 152 * combination. 153 * @return The provided array now containing the elements of the combination. 154 */ 155 public T[] nextCombinationAsArray(T[] destination) 156 { 157 if (destination.length != combinationIndices.length) 158 { 159 throw new IllegalArgumentException("Destination array must be the same length as combinations."); 160 } 161 generateNextCombinationIndices(); 162 for (int i = 0; i < combinationIndices.length; i++) 163 { 164 destination[i] = elements[combinationIndices[i]]; 165 } 166 return destination; 167 } 168 169 170 /** 171 * Generate the next combination and return a list containing the 172 * appropriate elements. 173 * @see #nextCombinationAsList(List) 174 * @return A list containing the elements that make up the next combination. 175 */ 176 public List<T> nextCombinationAsList() 177 { 178 return nextCombinationAsList(new ArrayList<T>(elements.length)); 179 } 180 181 182 /** 183 * Generate the next combination and return a list containing 184 * the appropriate elements. This overloaded method allows the caller 185 * to provide a list that will be used and returned. 186 * The purpose of this is to improve performance when iterating over 187 * combinations. If the {@link #nextCombinationAsList()} method is 188 * used it will create a new list every time. When iterating over 189 * combinations this will result in lots of short-lived objects that 190 * have to be garbage collected. This method allows a single list 191 * instance to be reused in such circumstances. 192 * @param destination Provides a list to use to create the 193 * combination. 194 * @return The provided list now containing the elements of the combination. 195 */ 196 public List<T> nextCombinationAsList(List<T> destination) 197 { 198 generateNextCombinationIndices(); 199 // Generate actual combination. 200 destination.clear(); 201 for (int i : combinationIndices) 202 { 203 destination.add(elements[i]); 204 } 205 return destination; 206 } 207 208 209 210 /** 211 * Generate the indices into the elements array for the next combination. The 212 * algorithm is from Kenneth H. Rosen, Discrete Mathematics and Its Applications, 213 * 2nd edition (NY: McGraw-Hill, 1991), p. 286. 214 */ 215 private void generateNextCombinationIndices() 216 { 217 if (remainingCombinations == 0) 218 { 219 throw new IllegalStateException("There are no combinations remaining. " + 220 "Generator must have reset() called to continue."); 221 } 222 else if (remainingCombinations < totalCombinations) 223 { 224 int i = combinationIndices.length - 1; 225 while (combinationIndices[i] == elements.length - combinationIndices.length + i) 226 { 227 i--; 228 } 229 ++combinationIndices[i]; 230 for (int j = i + 1; j < combinationIndices.length; j++) 231 { 232 combinationIndices[j] = combinationIndices[i] + j - i; 233 } 234 } 235 --remainingCombinations; 236 } 237 238 239 /** 240 * <p>Provides a read-only iterator for iterating over the combinations 241 * generated by this object. This method is the implementation of the 242 * {@link Iterable} interface that permits instances of this class to be 243 * used with the new-style for loop.</p> 244 * <p>For example:</p> 245 * <pre> 246 * List<Integer> elements = Arrays.asList(1, 2, 3); 247 * CombinationGenerator<Integer> combinations = new CombinationGenerator(elements, 2); 248 * for (List<Integer> c : combinations) 249 * { 250 * // Do something with each combination. 251 * } 252 * </pre> 253 * @return An iterator. 254 * @since 1.1 255 */ 256 public Iterator<List<T>> iterator() 257 { 258 return new Iterator<List<T>>() 259 { 260 public boolean hasNext() 261 { 262 return hasMore(); 263 } 264 265 266 public List<T> next() 267 { 268 return nextCombinationAsList(); 269 } 270 271 272 public void remove() 273 { 274 throw new UnsupportedOperationException("Iterator does not support removal."); 275 } 276 }; 277 } 278 279}