All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hfg.math.SetPartitionIterator Maven / Gradle / Ivy

There is a newer version: 20240423
Show newest version
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()));
   }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy