com.hfg.math.SetPartitionIterator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of com_hfg Show documentation
Show all versions of com_hfg Show documentation
com.hfg xml, html, svg, and bioinformatics utility library
package com.hfg.math;
import com.hfg.util.collection.OrderedSet;
import java.math.BigInteger;
import java.util.*;
//------------------------------------------------------------------------------
/**
* Iterator for enumerating the possible set partitions of a specified set.
* @author J. Alex Taylor, hairyfatguy.com
*/
//------------------------------------------------------------------------------
// com.hfg Library
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
// [email protected]
//------------------------------------------------------------------------------
public class SetPartitionIterator implements Iterator>>
{
public static enum Flag
{
GROUP_ORDER_MATTERS
}
private Set mFlags = new HashSet<>(4);
private OrderedSet mSetToPartition;
private Integer mNumGroups;
private Counter[] mPermutationIndices;
//##########################################################################
// CONSTRUCTORS
//##########################################################################
//--------------------------------------------------------------------------
public SetPartitionIterator(Set inSetToPartition, Integer inNumGroups, Flag... inFlags)
{
mSetToPartition = new OrderedSet(inSetToPartition);
mNumGroups = inNumGroups;
if (inFlags != null)
{
for (int i = 0; i < inFlags.length; i++)
{
mFlags.add(inFlags[i]);
}
}
init();
}
//##########################################################################
// PUBLIC FUNCTIONS
//##########################################################################
//--------------------------------------------------------------------------
public boolean hasNext()
{
return iterate();
}
//--------------------------------------------------------------------------
@Override
public List> next()
{
int size = numOfGroupsInCurrentReading();
List> partitionedSetCombination = new ArrayList>(size);
for (int i = 0; i < size; i++)
{
partitionedSetCombination.add(new OrderedSet(mSetToPartition.size()));
}
int i = 0;
for (T obj : mSetToPartition)
{
partitionedSetCombination.get(mPermutationIndices[i].intValue()).add(obj);
i++;
}
return partitionedSetCombination;
}
//##########################################################################
// PRIVATE FUNCTIONS
//##########################################################################
//--------------------------------------------------------------------------
private void init()
{
mPermutationIndices = new Counter[mSetToPartition.size()];
for (int i = 0; i < mSetToPartition.size(); i++)
{
mPermutationIndices[i] = new Counter();
}
}
//--------------------------------------------------------------------------
private boolean iterate()
{
boolean result = true;
boolean acceptable = false;
while (result && ! acceptable)
{
// Uses the permutation indices like an odometer to generate the permutations
for (int idx = mPermutationIndices.length - 1; idx >= 0; idx--)
{
Counter counter = mPermutationIndices[idx];
counter.increment();
if (mNumGroups != null)
{
if (counter.intValue() < mNumGroups)
{
// All is good.
break;
}
}
else if (counter.intValue() < mSetToPartition.size())
{
// All is good.
break;
}
if (0 == idx)
{
// End of the last wheel
result = false;
break;
}
// 'roll over' this wheel of the odometer
counter.reset();
}
if (result)
{
acceptable = true;
if (mNumGroups != null
&& !mNumGroups.equals(numOfGroupsInCurrentReading()))
{
// Skip this result and keep going
acceptable = false;
}
if (acceptable
&& !mFlags.contains(Flag.GROUP_ORDER_MATTERS)
&& inverseOfCurrentReadingHasAlreadyBeenEnumerated())
{
// Keep going if the inverse of the odometer reading has been previously seen
acceptable = false;
}
}
}
return result;
}
//--------------------------------------------------------------------------
private int numOfGroupsInCurrentReading()
{
Set groups = new HashSet<>(mSetToPartition.size());
for (Counter counter : mPermutationIndices)
{
groups.add(counter.intValue());
}
return groups.size();
}
//--------------------------------------------------------------------------
private boolean inverseOfCurrentReadingHasAlreadyBeenEnumerated()
{
StringBuilder buffer = new StringBuilder();
StringBuilder inverseBuffer = new StringBuilder();
int maxGroupIdx = numOfGroupsInCurrentReading() - 1;
for (Counter counter : mPermutationIndices)
{
buffer.append(counter.toString());
inverseBuffer.append((maxGroupIdx - counter.intValue()) + "");
}
// value greater than the inverse?
return 1 == new BigInteger(buffer.toString()).compareTo(new BigInteger(inverseBuffer.toString()));
}
}