/*
 * Copyright (c) 2012-2015, Peter Abeles. All Rights Reserved.
 *
 * This file is part of DDogleg (http://ddogleg.org).
 *
 * 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.
 */

package org.ddogleg.combinatorics;

import java.util.ArrayList;
import java.util.List;

/**
 * 

* Computes the combinations of size k given a set S of size N. This can be done in the forward or reverse direction. * A combination is defined is a unique subset of a list in which order does not mater. This is different from * a permutation where order does matter. *

* *

* The bucket is used to refer to the output set of size k that is the current combination. Elements outside the * bucket are all elements not currently in the bucket. *

* *

Below is an example of a combination.

*
 * List = 012345
 * k = 3;
 *
 * 012
 * 013
 * 014
 * 015
 * 023
 * 024
 * 025
 * 034
 * 035
 * 045
 * 123
 * 124
 * 125
 * 134
 * 135
 * 145
 * 234
 * 235
 * 245
 * 345
 * 
**/ public class Combinations< T > { protected List a; // the original unmolested list protected int N; // the total number of elements in "a" protected int k; // the number of elements in the active bucket protected int []bins; // which element in "a" is in which position in the list protected int c; // the last bin in "bins" // indicates if it is 0=at beginning, 1=middle, 2=end // 0 or 2 indicate that it is actually one past the extreme, which needs to be taken into account protected int state; /** * Constructor where the list and combinations is specified * * @param a List of symbols * @param bucketSize Size of the bucket */ public Combinations(List a, int bucketSize) { init( a , bucketSize ); } public Combinations(){ } /** * Initialize with a new list and bucket size * * @param list List which is to be symbols * @param bucketSize Size of the bucket */ public void init( List list , int bucketSize ) { if( list.size() < bucketSize ) { throw new RuntimeException("There needs to be more than or equal to elements in the 'list' that there are in the bucket"); } this.k = bucketSize; this.c = bucketSize - 1; N = list.size(); bins = new int[ bucketSize ]; for( int i = 0; i < bins.length; i++ ) { bins[i] = i; } this.a = list; state = 1; } /** * This will shuffle the elements in and out of the * bins. When all combinations have been exhausted * an ExhaustedException will be thrown. * * @return true if the next combination was successfully found or false is it has been exhausted */ public boolean next() { if( state == 2 ) return false; else state = 1; bins[c]++; if( bins[c] >= N ) { boolean allgood = false; for( int i = c-1; i >= 0; i-- ) { bins[i]++; if( bins[i] <= (N-(k-i)) ) { allgood = true; for( int j = i + 1; j < k; j++ ) { bins[j] = bins[j-1] + 1; } break; } } if( !allgood ) { state = 2; // put it back into the last combination for( int j = 0; j < bins.length; j++ ) { bins[j] = j+N-bins.length; } } return allgood; } return true; } /** * Undoes the previous combination computed by {@link #next()}. * * @return true if the combination was successfully undone or false is is back to the original state */ public boolean previous() { // see if it is back to the start already if( state == 0 ) return false; else state = 1; for( int i = c; i >= 0; i-- ) { bins[i]--; if( i == 0 ) { if( bins[0] < 0 ) { state = 0; // put it back into its first combination for( int j = 0; j < bins.length; j++ ) { bins[j] = j; } return false; } break; } else if( bins[i] <= bins[i-1] ) { bins[i] = N-1-(c-i); } else { break; } } return true; } /** * The number of combinations is, n!/(k!*(n-k)!, where n is number of elements, k is the number of bins, and * ! is factorial. * * @return Total number */ public long computeTotalCombinations() { long numerator = a.size(); long denominator = k; for( int i = 1; i < k; i++ ) { numerator *= a.size()-i; denominator *= k-i; } return numerator/denominator; } /** * Returns the size of the bucket/output set * * @return Size of bucket */ public int getBucketSize() { return k; } /** * Returns element 'i' in the bucket. * * @param i which element * @return the element */ public T get( int i ) { return a.get( bins[i] ); } /** * Extracts the entire bucket. Will add elements to the provided list or create a new one * * @param storage Optional storage. If null a list is created. clear() is automatically called. * @return List containing the bucket */ public List getBucket(List storage) { if( storage == null ) storage = new ArrayList(); else storage.clear(); for( int i = 0; i < bins.length; i++ ) { storage.add( a.get( bins[i] ) ); } return storage; } /** * This returns all the items that are not currently inside the bucket * * @param storage Optional storage. If null a list is created. clear() is automatically called. * @return List containing the bucket */ public List getOutside( List storage ) { if( storage == null ) { storage = new ArrayList(); } else { storage.clear(); } storage.addAll(a); // bins specifies elements in order of first in 'a' to last for( int i = bins.length-1; i >= 0; i-- ) { storage.remove(bins[i]); } return storage; } }

Ads help maintain this website.