private void Test67Choose33() { // This function tests out the long binomial coefficien class for the case 67 choose 33. // This produces one of the largest number of combinations that will fit within a ulong: // 14,226,520,737,620,288,370 // // Test out the unsigned long version of GetBinCoeff. The int version overflows. int n = 67; int k = 33; string s; bool overflow; // Create the BinCoeff object that will be used for all the tests for the 50 choose 25 case. BCL = new BinCoeffL(n, k); // ulong numCombos50Choose25 = BCL.TotalCombos; if (BCL.TotalCombos != 14226520737620288370) { s = $"Test67Choose33: Error calculating # of combos - {BCL.TotalCombos}."; DisplayMsg(s); throw new ApplicationException(s); } uint loop; ulong rank = 0, highRank = BCL.TotalCombos - 1, rank2; int[] kIndexes = new int[k]; // Try getting the first and last 5 combinations and ranks for this case. try { for (loop = 0; loop < 5; loop++) { BCL.GetCombFromRank(rank, kIndexes); rank2 = BCL.GetRank(true, kIndexes, out overflow); if (rank != rank2) { s = $"Test67Choose33: Either rank or combos was not calculated correctly. rank = {rank}, rank2 = {rank2}"; DisplayMsg(s); throw new ApplicationException(s); } BCL.GetCombFromRank(highRank, kIndexes); rank2 = BCL.GetRank(true, kIndexes, out overflow); if (highRank != rank2) { s = $"Test67Choose33: Either rank or combos was not calculated correctly. highRank = {highRank}, rank2 = {rank2}"; DisplayMsg(s); throw new ApplicationException(s); } rank++; highRank--; } } catch (Exception e) { DisplayMsg($"Test67Choose33: Exception = {e.Message}."); return; } DisplayMsg("Test67Choose33: completed successfully."); }
private bool TestRankComboulong(int numItems, int groupSize) { // This method tests getting the rank and combination for the specified case. // If the test failed, then false is returned. Otherwise true is returned. string s; bool overflow; int n = numItems, k = groupSize; // Create the bin coeff object required to get all the combos for this n choose k combination. BinCoeffL bcl = new BinCoeffL(n, k); // The Kindexes array specifies the indexes for a combination. int[] kIndexes = new int[k]; ulong numCombos = bcl.TotalCombos, rankLoop; bcl.GetCombFromRank(numCombos - 1, kIndexes); uint numCombos2 = (uint)BinCoeffBase.GetRankAlt(kIndexes, n, k, out overflow) + 1; if (numCombos != numCombos2) { s = $"TestRankComboLong: {n} choose {k}: # of combos not the same with alternate method - {numCombos} .vs. {numCombos2}"; Console.WriteLine(s); return(false); } ulong rank, offset = 1; // Loop thru all the combinations for this n choose k case if # of combos <= 1000000. // Otherwise, choose a random rank between 100 & 200. if (numCombos > 1000000) { Random rand = new Random(-12345); offset = (ulong)rand.Next(100, 200); } for (rankLoop = 0; rankLoop < numCombos; rankLoop += offset) { // Get the k-indexes for this combination. bcl.GetCombFromRank(rankLoop, kIndexes); // Verify that the Kindexes returned can be used to retrieve the rank of the combination. rank = bcl.GetRank(true, kIndexes, out overflow, groupSize); if (rank != rankLoop) { s = $"TestRankComboLong: {n} choose {k}: GetRank or GetCombFromRank failed - value of {rank} != rank at {rankLoop}"; Console.WriteLine(s); return(false); } } s = $"TestRankComboLong: {n} choose {k} completed successfully."; Console.WriteLine(s); return(true); }
private void Test100Choose95SubCases() { // This method tests out the new int functionality of specifying a lower n choose k case without having to // create a new version of Pascal's Triangle to handle it. This version checks the 100 choose 95 case. // string s; bool overflow; int n = 100, k = 95, kLoop; BinCoeffL bcl = new BinCoeffL(n, k); ulong numCombos, numCombos2, rank, highRank; uint rankLoop; int[] kIndexes = new int[k]; // Test all subcases of 100 choose 95. for (kLoop = k; kLoop > 0; kLoop--) { numCombos = bcl.GetNumCombos(n, kLoop); numCombos2 = BinCoeffBase.GetCombosCount(n, kLoop); if (numCombos != numCombos2) { s = $"Test100Choose95SubCases: Error - {n} choose {k}: # of combos does not match."; Console.WriteLine(s); return; } // Test the lowest 10 ranks and the highest 10 ranks for each subcase test case. for (rankLoop = 1; rankLoop <= 10; rankLoop++) { bcl.GetCombFromRank(rankLoop, kIndexes, n, kLoop); rank = bcl.GetRank(true, kIndexes, out overflow, kLoop); if (rank != rankLoop) { s = $"Test100Choose95SubCases: Error - {n} choose {kLoop}: rank or combo is wrong."; Console.WriteLine(s); return; } highRank = numCombos - rankLoop; bcl.GetCombFromRank(highRank, kIndexes, n, kLoop); rank = bcl.GetRank(true, kIndexes, out overflow, kLoop); if (rank != highRank) { s = $"Test100Choose95SubCases: Error - {n} choose {kLoop}: high rank or combo is wrong."; Console.WriteLine(s); return; } } } }
private ulong BenchmarkBCRankAlt(int n, int k, out long ticks) { // This method benchmarks obtaining the rank for this n choose k case // using the Combination.GetRank method. // ulong rank; GC.Collect(); // Get the combination for a mid in kIndexes. var bc = new BinCoeffL(n, k); int[] kIndexes = new int[k]; bc.GetCombFromRank(bc.TotalCombos / 2, kIndexes); Stopwatch sw = Stopwatch.StartNew(); rank = Combination.GetRank(kIndexes, out bool overflow, n, k); // Some cases overflow. So, use big integer. if (rank == 0) { rank = (ulong)Combination.GetRankBigInt(kIndexes, n, k); } sw.Stop(); ticks = sw.ElapsedTicks; return(rank); }
private int[] BenchmarkBCGetComboFromRank(int n, int k, out long ticks) { // This method benchmarks obtaining the rank for this n choose k case. // GC.Collect(); var bc = new BinCoeffL(n, k); int[] kIndexes = new int[k]; ulong rank = bc.TotalCombos / 2; Stopwatch sw = Stopwatch.StartNew(); bc.GetCombFromRank(rank, kIndexes); sw.Stop(); ticks = sw.ElapsedTicks; return(kIndexes); }
private ulong BenchmarkBCRank(int n, int k, out long ticks) { // This method benchmarks obtaining the rank for this n choose k case. // ulong rank; GC.Collect(); var bc = new BinCoeffL(n, k); int[] kIndexes = new int[k]; bc.GetCombFromRank(bc.TotalCombos / 2, kIndexes); Stopwatch sw = Stopwatch.StartNew(); rank = bc.GetRank(true, kIndexes, out bool overflow); sw.Stop(); ticks = sw.ElapsedTicks; return(rank); }
private void BenchmarkBinCoeffMultiple(int[] numElemArray, int[] numGroupArray) { // This method benchmarks the n choose k tests for many subcases & ranks, and compares the performance against the Combination class methods. // The first benchmark compares the performance of single methods. This one attempts a more "real-life" analysis and calls GetRank and // GetComboFromRank 1,000 times for all the subcases for each test case. // int numItems, groupSize, caseLoop; int[] kIndexes = new int[40]; ulong rankSum = 0; int kLoop; bool overflow; BinCoeffL bcl; long ticks, ticksAlt, ticksSum = 0, ticksSumAlt = 0; ulong numCombos, rank, startRank, endRank, rankCount = 1000, rankLoop; double d; Stopwatch sw; // Benchmark all the subcases. for (caseLoop = 0; caseLoop < numElemArray.Length; caseLoop++) { GC.Collect(); sw = Stopwatch.StartNew(); numItems = numElemArray[caseLoop]; groupSize = numGroupArray[caseLoop]; bcl = new BinCoeffL(numItems, groupSize); for (kLoop = groupSize; kLoop > 0; kLoop--) { numCombos = bcl.GetNumCombos(numItems, kLoop); rankSum += numCombos; startRank = (numCombos / 2) - 500; endRank = startRank + rankCount; for (rankLoop = startRank; rankLoop < endRank; rankLoop++) { bcl.GetCombFromRank(rankLoop, kIndexes, numItems, kLoop); rank = bcl.GetRank(true, kIndexes, out overflow, kLoop); rankSum += rank; } } sw.Stop(); // Looking at the sum of the return values makes sure that the compiler does not optimize the code away. if (rankSum == 0) { Console.WriteLine("Error - rankSum = 0?"); } ticks = sw.ElapsedTicks; // // Benchmark the uint BinCoeff class subcases for 30 choose 15. rankSum = 0; sw = Stopwatch.StartNew(); for (kLoop = groupSize; kLoop > 4; kLoop--) { numCombos = Combination.GetNumCombos(numItems, kLoop); rankSum += numCombos; startRank = (numCombos / 2) - 500; endRank = startRank + rankCount; for (rankLoop = startRank; rankLoop < endRank; rankLoop++) { Combination.GetCombFromRankLong(rankLoop, kIndexes, numItems, kLoop); rank = Combination.GetRank(kIndexes, out overflow, numItems, kLoop); rankSum += rank; } } sw.Stop(); ticksAlt = sw.ElapsedTicks; // Display the results. d = (double)ticksAlt / (double)ticks; d = Math.Round(d, 2); Console.WriteLine($"Multiple methods benchmark for {numItems} choose {groupSize} & subcases .vs. Combination class ratio = {d} to 1."); ticksSum += ticks; ticksSumAlt += ticksAlt; } d = (double)ticksSumAlt / (double)ticksSum; d = Math.Round(d, 2); Console.WriteLine($"Average for all n choose k cases & subcases .vs. Combination class ratio = {d} to 1."); Console.WriteLine(); // Looking at the sum of the return values makes sure that the compiler does not optimize the code away. if (rankSum == 0) { Console.WriteLine("Error - rankSum = 0?"); } }
private void VerifyTranslastion50Choose25() { // This test function verifies that the BinCoeff class has been created correctly and that each // combination translates properly to the correct index in the sorted binomial coefficient table. // Testing for translating between the index and the KIndexes is also done here. // int k = 25; int[] kIndexes = new int[k]; int[,] verKIndexes = new int[5, 4] { { 24, 23, 22, 21 }, { 25, 23, 22, 21 }, { 25, 24, 22, 21 }, { 25, 24, 23, 21 }, { 25, 24, 23, 22 } }; uint verLoop; ulong loop, rank, rank2; // There are 126,410,606,437,752 unique combinations in this test case, far too many to go thru them all. // So, just check out the first 5 and the last 5. for (loop = 0; loop < 5; loop++) { BCL.GetCombFromRank(loop, kIndexes); // Verify that the KIndexes can be translated back to the proper index. rank = BCL.GetRank(true, kIndexes, out bool overflow); if (rank != loop) { DisplayMsg("BinCoeffL.GetKindex did not calculate the correct index."); return; } // Verify that the KIndexes were returned correctly. for (verLoop = 0; verLoop < k; verLoop++) { if (verLoop < 4) { if (kIndexes[verLoop] != verKIndexes[loop, verLoop]) { DisplayMsg("BinCoeffL.GetKindexes did not calculate the correct KIndexes."); return; } } else { if (kIndexes[verLoop] != k - 1 - verLoop) { DisplayMsg("BinCoeffL.GetKindexes did not calculate the correct last index in KIndexes."); return; } } } rank = 126410606437747 + loop; BCL.GetCombFromRank(rank, kIndexes); // Verify that the KIndexes can be translated back to the proper index. rank2 = BCL.GetRank(true, kIndexes, out overflow); if (overflow) { DisplayMsg("BinCoeffL.GetRank overflowed."); return; } if (rank != rank2) { DisplayMsg("BinCoeffL.GetRank did not calculate the correct index."); return; } for (verLoop = 0; verLoop < k; verLoop++) { if (verLoop != k - 1) { if (kIndexes[verLoop] != 49 - verLoop) { DisplayMsg("BinCoeffL.GetKindexes did not calculate the correct KIndexes."); return; } } else { if ((uint)kIndexes[verLoop] != 45 - verLoop + loop) { DisplayMsg("BinCoeffL.GetKindexes did not calculate the correct last index in KIndexes."); return; } } } } }
private TestResult TestLongSubCases(int n, int k) { // This method tests out the new int functionality of specifying a lower n choose k case without having to // create a new version of Pascal's Triangle to handle it. n & k specifies the main n choose k case. // string s; int nLoop, kLoop; BinCoeffL bcl = new BinCoeffL(n, k); ulong numCombos, numCombos2, rank, rankHigh, rank2, rankLoop; int[] kIndexes = new int[k]; // If the # of combos overflowed, then don't try this case with uint. if (bcl.TotalCombos == 0) { return(TestResult.IntOverflow); } // Try getting the rank and combination for all subcases of n choose k. for (kLoop = k; kLoop > 0; kLoop--) { for (nLoop = n; nLoop >= kLoop; nLoop--) { numCombos = bcl.GetNumCombos(nLoop, kLoop); numCombos2 = (uint)BinCoeffBase.GetCombosCount(nLoop, kLoop); if (numCombos != numCombos2) { s = $"TestIntSubCases: Error - {n} choose {k}: # of combos does not match."; Console.WriteLine(s); return(TestResult.Failed); } ulong comboCount = numCombos; // Loop thru all the combinations for this n choose k case if # of combos <= 1000. // Otherwise, just do the lowest ranked 500 and the highest ranked 500. if (numCombos > 1000) { comboCount = 500; } // Loop thru the combinations for this n choose k case. for (rankLoop = 0; rankLoop < comboCount; rankLoop++) { bcl.GetCombFromRank(rankLoop, kIndexes, nLoop, kLoop); rank = bcl.GetRank(true, kIndexes, out bool overflow, kLoop); if (rank != rankLoop) { s = $"TestIntSubCases: Error - {n} choose {k}: rank or combo is wrong."; Console.WriteLine(s); return(TestResult.Failed); } // If not doing all the combinations, then process the high ranks. if (numCombos > comboCount) { rankHigh = numCombos - rankLoop - 1; // Get the k-indexes for this combination. bcl.GetCombFromRank(rankHigh, kIndexes, nLoop, kLoop); // Verify that the Kindexes returned can be used to retrieve the rank of the combination. rank2 = bcl.GetRank(true, kIndexes, out overflow, kLoop); if (rank2 != rankHigh) { s = $"TestIntSubCases: Error - {n} choose {k}: GetRank or GetCombFromRank failed - {rank2} != rankHigh at {rankLoop}"; Console.WriteLine(s); return(TestResult.Failed); } } } } } return(TestResult.Passed); }