001package com.hfg.math; 002 003import com.hfg.util.collection.OrderedSet; 004 005import java.math.BigInteger; 006import java.util.*; 007 008//------------------------------------------------------------------------------ 009/** 010 * Iterator for enumerating the possible set partitions of a specified set. 011 * @author J. Alex Taylor, hairyfatguy.com 012 */ 013//------------------------------------------------------------------------------ 014// com.hfg Library 015// 016// This library is free software; you can redistribute it and/or 017// modify it under the terms of the GNU Lesser General Public 018// License as published by the Free Software Foundation; either 019// version 2.1 of the License, or (at your option) any later version. 020// 021// This library is distributed in the hope that it will be useful, 022// but WITHOUT ANY WARRANTY; without even the implied warranty of 023// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 024// Lesser General Public License for more details. 025// 026// You should have received a copy of the GNU Lesser General Public 027// License along with this library; if not, write to the Free Software 028// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 029// 030// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com 031// jataylor@hairyfatguy.com 032//------------------------------------------------------------------------------ 033 034public class SetPartitionIterator<T> implements Iterator<List<Set<T>>> 035{ 036 public static enum Flag 037 { 038 GROUP_ORDER_MATTERS 039 } 040 041 private Set<Flag> mFlags = new HashSet<>(4); 042 043 private OrderedSet<T> mSetToPartition; 044 private Integer mNumGroups; 045 private Counter[] mPermutationIndices; 046 047 //########################################################################## 048 // CONSTRUCTORS 049 //########################################################################## 050 051 //-------------------------------------------------------------------------- 052 public SetPartitionIterator(Set<T> inSetToPartition, Integer inNumGroups, Flag... inFlags) 053 { 054 mSetToPartition = new OrderedSet<T>(inSetToPartition); 055 mNumGroups = inNumGroups; 056 057 if (inFlags != null) 058 { 059 for (int i = 0; i < inFlags.length; i++) 060 { 061 mFlags.add(inFlags[i]); 062 } 063 } 064 065 init(); 066 } 067 068 //########################################################################## 069 // PUBLIC FUNCTIONS 070 //########################################################################## 071 072 //-------------------------------------------------------------------------- 073 public boolean hasNext() 074 { 075 return iterate(); 076 } 077 078 //-------------------------------------------------------------------------- 079 @Override 080 public List<Set<T>> next() 081 { 082 int size = numOfGroupsInCurrentReading(); 083 List<Set<T>> partitionedSetCombination = new ArrayList<Set<T>>(size); 084 for (int i = 0; i < size; i++) 085 { 086 partitionedSetCombination.add(new OrderedSet<T>(mSetToPartition.size())); 087 } 088 089 int i = 0; 090 for (T obj : mSetToPartition) 091 { 092 partitionedSetCombination.get(mPermutationIndices[i].intValue()).add(obj); 093 i++; 094 } 095 096 return partitionedSetCombination; 097 } 098 099 100 //########################################################################## 101 // PRIVATE FUNCTIONS 102 //########################################################################## 103 104 //-------------------------------------------------------------------------- 105 private void init() 106 { 107 mPermutationIndices = new Counter[mSetToPartition.size()]; 108 for (int i = 0; i < mSetToPartition.size(); i++) 109 { 110 mPermutationIndices[i] = new Counter(); 111 } 112 } 113 114 //-------------------------------------------------------------------------- 115 private boolean iterate() 116 { 117 boolean result = true; 118 boolean acceptable = false; 119 120 while (result && ! acceptable) 121 { 122 // Uses the permutation indices like an odometer to generate the permutations 123 for (int idx = mPermutationIndices.length - 1; idx >= 0; idx--) 124 { 125 Counter counter = mPermutationIndices[idx]; 126 counter.increment(); 127 128 if (mNumGroups != null) 129 { 130 if (counter.intValue() < mNumGroups) 131 { 132 // All is good. 133 break; 134 } 135 } 136 else if (counter.intValue() < mSetToPartition.size()) 137 { 138 // All is good. 139 break; 140 } 141 142 if (0 == idx) 143 { 144 // End of the last wheel 145 result = false; 146 break; 147 } 148 149 // 'roll over' this wheel of the odometer 150 counter.reset(); 151 } 152 153 if (result) 154 { 155 acceptable = true; 156 157 if (mNumGroups != null 158 && !mNumGroups.equals(numOfGroupsInCurrentReading())) 159 { 160 // Skip this result and keep going 161 acceptable = false; 162 } 163 164 if (acceptable 165 && !mFlags.contains(Flag.GROUP_ORDER_MATTERS) 166 && inverseOfCurrentReadingHasAlreadyBeenEnumerated()) 167 { 168 // Keep going if the inverse of the odometer reading has been previously seen 169 acceptable = false; 170 } 171 } 172 } 173 174 return result; 175 } 176 177 //-------------------------------------------------------------------------- 178 private int numOfGroupsInCurrentReading() 179 { 180 Set<Integer> groups = new HashSet<>(mSetToPartition.size()); 181 182 for (Counter counter : mPermutationIndices) 183 { 184 groups.add(counter.intValue()); 185 } 186 187 return groups.size(); 188 } 189 190 //-------------------------------------------------------------------------- 191 private boolean inverseOfCurrentReadingHasAlreadyBeenEnumerated() 192 { 193 StringBuilder buffer = new StringBuilder(); 194 StringBuilder inverseBuffer = new StringBuilder(); 195 196 int maxGroupIdx = numOfGroupsInCurrentReading() - 1; 197 for (Counter counter : mPermutationIndices) 198 { 199 buffer.append(counter.toString()); 200 inverseBuffer.append((maxGroupIdx - counter.intValue()) + ""); 201 } 202 203 // value greater than the inverse? 204 return 1 == new BigInteger(buffer.toString()).compareTo(new BigInteger(inverseBuffer.toString())); 205 } 206}