PermutationsIterator.java
/*
* Licensed to the Hipparchus project under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The Hipparchus project licenses this file to You 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
*
* https://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.hipparchus.util;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
/** Iterator for generating permutations.
* <p>
* This class implements the Steinhaus–Johnson–Trotter algorithm
* with Even's speedup
* <a href="https://en.wikipedia.org/wiki/Steinhaus%E2%80%93Johnson%E2%80%93Trotter_algorithm">Steinhaus–Johnson–Trotter algorithm</a>
* </p>
* @param <T> type of the elements
* @since 2.2
*/
class PermutationsIterator<T> implements Iterator<List<T>> {
/** Current permuted list. */
private final List<T> permuted;
/** Value markers. */
private final int[] value;
/** Directions markers. */
private final int[] direction;
/** Indicator for exhausted partitions. */
private boolean exhausted;
/** Simple constructor.
* @param list list to permute (will not be touched)
*/
PermutationsIterator(final List<T> list) {
this.permuted = new ArrayList<>(list);
this.value = new int[list.size()];
this.direction = new int[list.size()];
for (int i = 0; i < value.length; ++i) {
value[i] = i;
direction[i] = i == 0 ? 0 : -1;
}
exhausted = false;
}
/** {@inheritDoc} */
@Override
public boolean hasNext() {
return !exhausted;
}
/** {@inheritDoc} */
@Override
public List<T> next() {
if (exhausted) {
throw new NoSuchElementException();
}
// the value that will be returned at the end
final List<T> current = new ArrayList<>(permuted);
// select element to swap for next permutation
int selectedIndex = -1;
int selectedValue = -1;
int selectedDirection = -1;
for (int i = 0; i < value.length; ++i) {
if (direction[i] != 0 && value[i] > selectedValue) {
selectedIndex = i;
selectedValue = value[i];
selectedDirection = direction[i];
}
}
if (selectedIndex < 0) {
exhausted = true;
} else {
// prepare next permutation
// swap selected and peer elements
final int selectedPeer = selectedIndex + selectedDirection;
final T tmp = permuted.get(selectedIndex);
permuted.set(selectedIndex, permuted.get(selectedPeer));
permuted.set(selectedPeer, tmp);
value[selectedIndex] = value[selectedPeer];
value[selectedPeer] = selectedValue;
direction[selectedIndex] = direction[selectedPeer];
if (selectedPeer == 0 || selectedPeer == permuted.size() - 1 ||
value[selectedPeer + selectedDirection] > selectedValue) {
// we cannot move anymore
direction[selectedPeer] = 0;
} else {
// we will continue moving in the same direction
direction[selectedPeer] = selectedDirection;
}
// enable motion again for greater elements, towards selected one
for (int i = 0; i < selectedIndex; ++i) {
if (value[i] > selectedValue) {
direction[i] = +1;
}
}
for (int i = selectedIndex + 1; i < value.length; ++i) {
if (value[i] > selectedValue) {
direction[i] = -1;
}
}
}
return current;
}
}