public static OptimalType GetLeastTypeForAllSubcases(int numItems, int groupSize) { // This method returns the least type that will be able to handle all subcases without overflowing. // // n choose (n / 2) is the worst subcase that generates the most combinations. // So, if numItems is twice or more groupSize, then there is no subcase that will increase the number of combos. // BigInteger numCombos; OptimalType returnValue; if (numItems >= groupSize + groupSize) { numCombos = Combination.GetNumCombos(numItems, groupSize); } else { numCombos = Combination.GetNumCombosBigInt(numItems, numItems / 2); } // If overflowed ulong, then only big int will work with all subtypes. if (numCombos > ulong.MaxValue) { returnValue = OptimalType.BigInt; } else if (numCombos <= uint.MaxValue) { returnValue = OptimalType.UnsignedInt; } else { returnValue = OptimalType.UnsignedLong; } return(returnValue); }
public uint GetNumCombos(int numItems = -1, int groupSize = -1) { // This method gets the total number of combos for numItems choose groupSize from Pascal's triangle. // If Pascal's triangle has not been created, then an alternative method is called to get the # of combinations. // If either numItems or groupSize < 0, then NumItems or GroupSize, respectively is used instead. // // Both numItems and groupSize are optional parameters that provide a way to efficiently calculate the number of // combinations from Pascal's Triangle without having to re-create Pascal's Triangle for a different case. But, // both numItems and groupSize must be <= to the original NumItems and GroupSize, respectively, that were used to create the instance. // If either numItems or groupSize < 0, then NumItems or GroupSize, respectively, is used instead. // // Zero is returned if overflow occurred. // The expected runtime of this algorithm is O(1) when Pascal's Triangle is used. // string s; ulong numCombos = 0; // numItems = (numItems < 0) ? NumItems : numItems; groupSize = (groupSize < 0) ? GroupSize : groupSize; if ((numItems > NumItems) || (groupSize > GroupSize)) { s = "BinCoeff:GetNumCombos: numItems > NumItems or groupSize > GroupSize. Neither is allowed."; ApplicationException ae = new ApplicationException(s); throw ae; } if ((numItems == 0) || (groupSize == 0)) { s = "BinCoeff:GetNumCombos: numItems or groupSize equals zero. Neither is allowed."; ApplicationException ae = new ApplicationException(s); throw ae; } if ((groupSize == 1) || (numItems == groupSize + 1)) { return((uint)numItems); } if (groupSize == numItems) { return(1); } // if Pascal's Triangle has not been created, then use an alternate method to obtain the number of combos. if (PasTri == null) { numCombos = Combination.GetNumCombos(numItems, groupSize); if (numCombos > uint.MaxValue) { return(0); } return((uint)numCombos); } uint n = (uint)numItems - 1; int startIndex = GroupSize - groupSize; uint[] indexArray = PasTri[startIndex]; int endIndex = indexArray.Length - 1; if (groupSize == 2) { endIndex -= (NumItems - numItems); if ((indexArray.Length > endIndex) && (uint.MaxValue - indexArray[endIndex] > numItems - 1)) { numCombos = indexArray[endIndex] + (uint)numItems - 1; } } else { if (numItems == NumItems) { uint[] indexArrayPrev = PasTri[startIndex + 1]; int endIndexPrev = indexArrayPrev.Length - 1; if ((indexArray.Length > n) && (indexArrayPrev.Length > n) && (uint.MaxValue - indexArray[endIndex] > indexArrayPrev[endIndexPrev])) { numCombos = indexArray[endIndex] + indexArrayPrev[endIndexPrev]; } } else { endIndex = endIndex - (NumItems - numItems) + 1; if (indexArray.Length > endIndex) { return(indexArray[endIndex]); } } } return((uint)numCombos); }
public BigInteger GetNumCombos(int numItems = -1, int groupSize = -1) { // This method gets the total number of combos for numItems choose groupSize from Pascal's triangle. // If Pascal's triangle has not been created, then an alternative method is called to get the # of combinations. // If either numItems or groupSize < 0, then NumItems or GroupSize, respectively is used instead. // // Both numItems and groupSize are optional parameters that provide a way to efficiently calculate the number of // combinations from Pascal's Triangle without having to re-create Pascal's Triangle for a different case. But, // both numItems and groupSize must be <= to the original NumItems and GroupSize, respectively, that were used to create the instance. // If either numItems or groupSize < 0, then NumItems or GroupSize, respectively, is used instead. // // Zero is returned if overflow occurred. // The expected runtime of this algorithm is O(1) when Pascal's Triangle is used. // string s; BigInteger numCombos = 0; // numItems = (numItems == -1) ? NumItems : numItems; groupSize = (groupSize == -1) ? GroupSize : groupSize; if ((numItems > NumItems) || (groupSize > GroupSize)) { s = "BinCoeffBigInt:GetNumCombos: numItems > NumItems || groupSize > GroupSize. Neither is allowed. Create a new instance instead."; ApplicationException ae = new ApplicationException(s); throw ae; } if ((numItems == 0) || (groupSize == 0)) { s = "BinCoeffBigInt:GetNumCombos: numItems or groupSize equals zero. Neither is allowed."; ApplicationException ae = new ApplicationException(s); throw ae; } if ((groupSize == 1) || (numItems == groupSize + 1)) { return(numItems); } if (groupSize == numItems) { return(1); } // There are times when Pascal's triangle may not have been legitimately created. For example 5 choose 5. // If this method is called, for example, with 5 choose 3 after being created with a 5 choose 5 case, then this is handled here. if (PasTri == null) { numCombos = Combination.GetNumCombos(numItems, groupSize); return(numCombos); } uint n = (uint)numItems - 1; int startIndex = GroupSize - groupSize; BigInteger[] indexArray = PasTri[startIndex]; int endIndex = indexArray.Length - 1; if (groupSize == 2) { if (numItems != NumItems) { endIndex -= (NumItems - numItems); } numCombos = indexArray[endIndex] + (uint)numItems - 1; } else { if (numItems == NumItems) { BigInteger[] indexArrayPrev = PasTri[startIndex + 1]; int endIndexPrev = indexArrayPrev.Length - 1; numCombos = indexArray[endIndex] + indexArrayPrev[endIndexPrev]; } else { endIndex = endIndex - (NumItems - numItems) + 1; return(indexArray[endIndex]); } } return(numCombos); }