001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.numbers.combinatorics; 019 020import java.io.Serializable; 021import java.util.Arrays; 022import java.util.NoSuchElementException; 023import java.util.Iterator; 024import java.util.Comparator; 025 026import org.apache.commons.numbers.core.ArithmeticUtils; 027 028/** 029 * Utility to create <a href="http://en.wikipedia.org/wiki/Combination"> 030 * combinations</a> {@code (n, k)} of {@code k} elements in a set of 031 * {@code n} elements. 032 */ 033public final class Combinations implements Iterable<int[]> { 034 /** Size of the set from which combinations are drawn. */ 035 private final int n; 036 /** Number of elements in each combination. */ 037 private final int k; 038 039 /** 040 * @param n Size of the set from which subsets are selected. 041 * @param k Size of the subsets to be enumerated. 042 * @throws IllegalArgumentException if {@code n < 0}. 043 * @throws IllegalArgumentException if {@code k > n} or {@code k < 0}. 044 */ 045 private Combinations(int n, 046 int k) { 047 BinomialCoefficient.checkBinomial(n, k); 048 this.n = n; 049 this.k = k; 050 } 051 052 /** 053 * Create an instance. 054 * 055 * @param n Size of the set from which subsets are selected. 056 * @param k Size of the subsets to be enumerated. 057 * @throws IllegalArgumentException if {@code n < 0}. 058 * @throws IllegalArgumentException if {@code k > n} or {@code k < 0}. 059 * @return a new instance. 060 */ 061 public static Combinations of(int n, 062 int k) { 063 return new Combinations(n, k); 064 } 065 066 /** 067 * Gets the size of the set from which combinations are drawn. 068 * 069 * @return the size of the universe. 070 */ 071 public int getN() { 072 return n; 073 } 074 075 /** 076 * Gets the number of elements in each combination. 077 * 078 * @return the size of the subsets to be enumerated. 079 */ 080 public int getK() { 081 return k; 082 } 083 084 /** 085 * Creates an iterator whose range is the k-element subsets of 086 * {0, ..., n - 1} represented as {@code int[]} arrays. 087 * <p> 088 * The iteration order is lexicographic: the arrays returned by the 089 * {@link #iterator() iterator} are sorted in descending order and 090 * they are visited in lexicographic order with significance from 091 * right to left. 092 * For example, {@code new Combinations(4, 2).iterator()} returns 093 * an iterator that will generate the following sequence of arrays 094 * on successive calls to 095 * {@code next()}:<br> 096 * {@code [0, 1], [0, 2], [1, 2], [0, 3], [1, 3], [2, 3]} 097 * </p> 098 * If {@code k == 0} an iterator containing an empty array is returned; 099 * if {@code k == n} an iterator containing [0, ..., n - 1] is returned. 100 */ 101 @Override 102 public Iterator<int[]> iterator() { 103 return k == 0 || k == n ? 104 new SingletonIterator(k) : 105 new LexicographicIterator(n, k); 106 } 107 108 /** 109 * Creates a comparator. 110 * When performing a comparison, if an element of the array is not 111 * within the interval [0, {@code n}), an {@code IllegalArgumentException} 112 * will be thrown. 113 * 114 * @return a comparator. 115 */ 116 public Comparator<int[]> comparator() { 117 return new LexicographicComparator(n, k); 118 } 119 120 /** 121 * Lexicographic combinations iterator. 122 * <p> 123 * Implementation follows Algorithm T in <i>The Art of Computer Programming</i> 124 * Internet Draft (PRE-FASCICLE 3A), "A Draft of Section 7.2.1.3 Generating All 125 * Combinations, D. Knuth, 2004.</p> 126 * <p> 127 * The degenerate cases {@code k == 0} and {@code k == n} are NOT handled by this 128 * implementation. It is assumed that {@code n > k > 0}. 129 * </p> 130 */ 131 private static class LexicographicIterator implements Iterator<int[]> { 132 /** Size of subsets returned by the iterator. */ 133 private final int k; 134 135 /** 136 * c[1], ..., c[k] stores the next combination; c[k + 1], c[k + 2] are 137 * sentinels. 138 * <p> 139 * Note that c[0] is "wasted" but this makes it a little easier to 140 * follow the code. 141 * </p> 142 */ 143 private final int[] c; 144 145 /** Return value for {@link #hasNext()}. */ 146 private boolean more = true; 147 148 /** Marker: smallest index such that {@code c[j + 1] > j}. */ 149 private int j; 150 151 /** 152 * Construct a CombinationIterator to enumerate {@code k}-sets from a set 153 * of size {@code n}. 154 * <p> 155 * NOTE: It is assumed that {@code n > k > 0}. 156 * </p> 157 * 158 * @param n Size of the set from which subsets are enumerated. 159 * @param k Size of the subsets to enumerate. 160 */ 161 LexicographicIterator(int n, int k) { 162 this.k = k; 163 c = new int[k + 3]; 164 // Initialize c to start with lexicographically first k-set 165 for (int i = 1; i <= k; i++) { 166 c[i] = i - 1; 167 } 168 // Initialize sentinels 169 c[k + 1] = n; 170 c[k + 2] = 0; 171 j = k; // Set up invariant: j is smallest index such that c[j + 1] > j 172 } 173 174 /** 175 * {@inheritDoc} 176 */ 177 @Override 178 public boolean hasNext() { 179 return more; 180 } 181 182 /** 183 * {@inheritDoc} 184 */ 185 @Override 186 public int[] next() { 187 if (!more) { 188 throw new NoSuchElementException(); 189 } 190 // Copy return value (prepared by last activation) 191 final int[] ret = new int[k]; 192 System.arraycopy(c, 1, ret, 0, k); 193 194 // Prepare next iteration 195 // T2 and T6 loop 196 int x = 0; 197 if (j > 0) { 198 x = j; 199 c[j] = x; 200 j--; 201 return ret; 202 } 203 // T3 204 if (c[1] + 1 < c[2]) { 205 c[1]++; 206 return ret; 207 } else { 208 j = 2; 209 } 210 // T4 211 boolean stepDone = false; 212 while (!stepDone) { 213 c[j - 1] = j - 2; 214 x = c[j] + 1; 215 if (x == c[j + 1]) { 216 j++; 217 } else { 218 stepDone = true; 219 } 220 } 221 // T5 222 if (j > k) { 223 more = false; 224 return ret; 225 } 226 // T6 227 c[j] = x; 228 j--; 229 return ret; 230 } 231 232 /** 233 * Not supported. 234 */ 235 @Override 236 public void remove() { 237 throw new UnsupportedOperationException(); 238 } 239 } 240 241 /** 242 * Iterator with just one element to handle degenerate cases (full array, 243 * empty array) for combination iterator. 244 */ 245 private static class SingletonIterator implements Iterator<int[]> { 246 /** Number of elements of the singleton array. */ 247 private final int n; 248 /** True on initialization, false after first call to next. */ 249 private boolean more = true; 250 /** 251 * Create a singleton iterator providing the given array. 252 * 253 * @param n Size of the singleton array returned by the iterator. 254 */ 255 SingletonIterator(final int n) { 256 this.n = n; 257 } 258 /** 259 * @return {@code true} until next is called the first time, 260 * then {@code false}. 261 **/ 262 @Override 263 public boolean hasNext() { 264 return more; 265 } 266 /** 267 * @return the singleton at the first activation. 268 * @throws NoSuchElementException after the first activation. 269 */ 270 @Override 271 public int[] next() { 272 if (more) { 273 more = false; 274 // Create singleton. 275 final int[] s = new int[n]; 276 for (int i = 0; i < n; i++) { 277 s[i] = i; 278 } 279 return s; 280 } else { 281 throw new NoSuchElementException(); 282 } 283 } 284 /** 285 * Unsupported. 286 * 287 * @throws UnsupportedOperationException Remove is not supported. 288 */ 289 @Override 290 public void remove() { 291 throw new UnsupportedOperationException(); 292 } 293 } 294 295 /** 296 * Defines a lexicographic ordering of the combinations. 297 * 298 * The comparison is based on the value (in base 10) represented 299 * by the digit (interpreted in base {@code n}) in the input array, 300 * in reverse order. 301 */ 302 private static class LexicographicComparator 303 implements Comparator<int[]>, 304 Serializable { 305 /** Serializable version identifier. */ 306 private static final long serialVersionUID = 20170520L; 307 /** Size of the set from which combinations are drawn. */ 308 private final int n; 309 /** Number of elements in each combination. */ 310 private final int k; 311 312 /** 313 * @param n Size of the set from which subsets are selected. 314 * @param k Size of the subsets to be enumerated. 315 */ 316 LexicographicComparator(int n, 317 int k) { 318 this.n = n; 319 this.k = k; 320 } 321 322 /** 323 * {@inheritDoc} 324 * 325 * @throws IllegalArgumentException if the array lengths are not 326 * equal to {@code k}. 327 * @throws IllegalArgumentException if an element of the array is not 328 * within the interval [0, {@code n}). 329 */ 330 @Override 331 public int compare(int[] c1, 332 int[] c2) { 333 if (c1.length != k) { 334 throw new CombinatoricsException(CombinatoricsException.MISMATCH, k, c1.length); 335 } 336 if (c2.length != k) { 337 throw new CombinatoricsException(CombinatoricsException.MISMATCH, k, c2.length); 338 } 339 340 // Method "lexNorm" works with ordered arrays. 341 final int[] c1s = Arrays.copyOf(c1, k); 342 final int[] c2s = Arrays.copyOf(c2, k); 343 Arrays.sort(c1s); 344 Arrays.sort(c2s); 345 346 final long v1 = lexNorm(c1s); 347 final long v2 = lexNorm(c2s); 348 349 return Long.compare(v1, v2); 350 } 351 352 /** 353 * Computes the value (in base 10) represented by the digit 354 * (interpreted in base {@code n}) in the input array in reverse 355 * order. 356 * For example if {@code c} is {@code {3, 2, 1}}, and {@code n} 357 * is 3, the method will return 18. 358 * 359 * @param c Input array. 360 * @return the lexicographic norm. 361 * @throws IllegalArgumentException if an element of the array is not 362 * within the interval [0, {@code n}). 363 */ 364 private long lexNorm(int[] c) { 365 long ret = 0; 366 for (int i = 0; i < c.length; i++) { 367 final int digit = c[i]; 368 if (digit < 0 || 369 digit >= n) { 370 throw new CombinatoricsException(CombinatoricsException.OUT_OF_RANGE, digit, 0, n - 1); 371 } 372 373 ret += c[i] * ArithmeticUtils.pow((long) n, i); 374 } 375 return ret; 376 } 377 } 378}