Java tutorial
/** * @author Tadanori TERUYA <tadanori.teruya@gmail.com> (2012) * @license: The MIT license <http://opensource.org/licenses/MIT> */ /* * Copyright (c) 2012 Tadanori TERUYA (tell) <tadanori.teruya@gmail.com> * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * @license: The MIT license <http://opensource.org/licenses/MIT> */ package com.github.tell.mathematics.combinatorics; import org.apache.commons.lang3.ArrayUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.Array; import java.util.Arrays; import java.util.NoSuchElementException; /** * @author Tadanori TERUYA <tadanori.teruya@gmail.com> (2012) */ public final class Combination<T> implements Iterable<T[]> { private static final Logger logger = LoggerFactory.getLogger(Combination.class); private T[] elements; private int numberOfSelection; private Class<T> cls; public Combination(final T[] elements, final int numberOfSelection) { assert elements.length >= numberOfSelection; this.elements = elements; this.numberOfSelection = numberOfSelection; //noinspection unchecked cls = (Class<T>) this.elements.getClass().getComponentType(); } public T[][] makeCombination() { if (numberOfSelection == 0) { //noinspection unchecked return (T[][]) Array.newInstance(cls, 1, 0); } else if (numberOfSelection == 1) { @SuppressWarnings("unchecked") final T[][] result = (T[][]) Array.newInstance(cls, elements.length, 0); assert result.length == elements.length : String.format("result = %s, numberOfSelection = %s", Arrays.deepToString(result), numberOfSelection); for (int i = 0; i < elements.length; i++) { result[i] = Arrays.copyOfRange(elements, i, i + 1); } return result; } else if (numberOfSelection == elements.length) { @SuppressWarnings("unchecked") final T[][] result = (T[][]) Array.newInstance(cls, 1, 0); result[0] = ArrayUtils.clone(elements); return result; } else if (numberOfSelection == elements.length - 1) { @SuppressWarnings("unchecked") final T[][] result = (T[][]) Array.newInstance(cls, elements.length, 0); for (int i = elements.length - 1; i >= 0; i--) { final int j = elements.length - 1 - i; result[j] = Arrays.copyOf(elements, i); result[j] = ArrayUtils.addAll(result[j], ArrayUtils.subarray(elements, i + 1, elements.length)); logger.trace("generated array = {}", Arrays.deepToString(result[j])); } return result; } else { final T[] partialElements = Arrays.copyOf(elements, elements.length - 1); logger.trace("partialElements = {}, numberOfSelection = {}", Arrays.deepToString(partialElements), numberOfSelection); final Combination<T> leftHandSideGenerator = new Combination<T>(partialElements, numberOfSelection); final T[][] leftCombination = leftHandSideGenerator.makeCombination(); final Combination<T> rightHandSideGenerator = new Combination<T>(partialElements, numberOfSelection - 1); final T[][] rightCombination = rightHandSideGenerator.makeCombination(); for (int i = 0; i < rightCombination.length; i++) { rightCombination[i] = ArrayUtils.add(rightCombination[i], elements[elements.length - 1]); } //noinspection unchecked return ArrayUtils.addAll(leftCombination, rightCombination); } } public class NoSelectionIterator implements java.util.Iterator<T[]> { private final Class<T> cls; private boolean hasNext; private NoSelectionIterator(final Class<T> cls) { this.cls = cls; hasNext = true; } public boolean hasNext() { return hasNext; } public T[] next() { if (hasNext()) { hasNext = false; //noinspection unchecked return (T[]) Array.newInstance(cls, 0); } else { throw new NoSuchElementException("no more elements"); } } public void remove() { throw new UnsupportedOperationException("remove operation is not supported"); } } public class TrivialSelectionIterator implements java.util.Iterator<T[]> { private final T[] elements; private boolean hasNext; private TrivialSelectionIterator(final T[] elements) { this.elements = elements; hasNext = true; } public boolean hasNext() { return hasNext; } public T[] next() { if (hasNext()) { hasNext = false; return ArrayUtils.clone(elements); } else { throw new NoSuchElementException("no more elements"); } } public void remove() { throw new UnsupportedOperationException("remove operation is not supported"); } } public class SingleElementSelectionIterator implements java.util.Iterator<T[]> { private final T[][] currentBuffer; private final Class<T> cls; private int currentIndex; private SingleElementSelectionIterator(final T[] elements, final Class<T> cls) { this.cls = cls; //noinspection unchecked currentBuffer = (T[][]) Array.newInstance(this.cls, elements.length, 0); for (int i = 0; i < elements.length; i++) { currentBuffer[i] = Arrays.copyOfRange(elements, i, i + 1); } currentIndex = 0; } public boolean hasNext() { return currentIndex < currentBuffer.length; } public T[] next() { if (hasNext()) { return currentBuffer[currentIndex++]; } else { throw new NoSuchElementException("no more elements"); } } public void remove() { throw new UnsupportedOperationException("remove operation is not supported"); } } public class SingleElementRemovedSelectionIterator implements java.util.Iterator<T[]> { private T[][] currentBuffer; private Class<T> cls; private int currentIndex; private SingleElementRemovedSelectionIterator(final T[] elements, final Class<T> cls) { assert elements.length > 0; this.cls = cls; //noinspection unchecked currentBuffer = (T[][]) Array.newInstance(this.cls, elements.length, 0); for (int i = elements.length - 1; i >= 0; i--) { final int j = elements.length - 1 - i; currentBuffer[j] = Arrays.copyOf(elements, i); currentBuffer[j] = ArrayUtils.addAll(currentBuffer[j], ArrayUtils.subarray(elements, i + 1, elements.length)); } currentIndex = 0; } public boolean hasNext() { return currentIndex < currentBuffer.length; } public T[] next() { if (hasNext()) { return currentBuffer[currentIndex++]; } else { throw new NoSuchElementException("no more elements"); } } public void remove() { throw new UnsupportedOperationException("remove operation is not supported"); } } public class Iterator implements java.util.Iterator<T[]> { private T[] elements; private int numberOfSelection = -1; private Class<T> cls; private java.util.Iterator<T[]> recursiveIterator; private T pushedTailElement = null; private boolean hasNextCombinationWithTail = false; private Iterator(final T[] elements, final int numberOfSelection, final Class<T> cls) { assert elements.length >= numberOfSelection; this.elements = ArrayUtils.clone(elements); this.numberOfSelection = numberOfSelection; this.cls = cls; logger.trace("input elements = {}", Arrays.deepToString(this.elements)); logger.trace("# of selection = {}", this.numberOfSelection); if (this.numberOfSelection == 0) { recursiveIterator = new NoSelectionIterator(cls); hasNextCombinationWithTail = false; } else if (this.numberOfSelection == this.elements.length) { recursiveIterator = new TrivialSelectionIterator(this.elements); hasNextCombinationWithTail = false; } else if (this.numberOfSelection == 1) { recursiveIterator = new SingleElementSelectionIterator(this.elements, this.cls); hasNextCombinationWithTail = false; } else if (this.numberOfSelection == this.elements.length - 1) { recursiveIterator = new SingleElementRemovedSelectionIterator(this.elements, this.cls); hasNextCombinationWithTail = false; } else { pushedTailElement = this.elements[this.elements.length - 1]; this.elements = ArrayUtils.remove(this.elements, this.elements.length - 1); recursiveIterator = new Iterator(this.elements, this.numberOfSelection, this.cls); hasNextCombinationWithTail = true; } } public boolean hasNext() { return hasNextCombinationWithTail || recursiveIterator.hasNext(); } public T[] next() { if (hasNext()) { if (!recursiveIterator.hasNext()) { assert hasNextCombinationWithTail; assert pushedTailElement != null; recursiveIterator = new Iterator(elements, numberOfSelection - 1, cls); hasNextCombinationWithTail = false; } final T[] result = recursiveIterator.next(); if (hasNextCombinationWithTail || pushedTailElement == null) { return result; } else { return ArrayUtils.add(result, pushedTailElement); } } else { throw new NoSuchElementException("no more elements"); } } public void remove() { throw new UnsupportedOperationException("remove operation is not supported"); } } public Iterator iterator() { return new Iterator(elements, numberOfSelection, cls); } }