/// <summary> /// Make a new <see cref="Combination"/> from the supplied /// <em>choices</em> and <em>picks</em> of <see cref="Rank"/> 0. /// </summary> /// <param name="choices">Number of values to pick from.</param> /// <param name="picks">Number of elements in the sequence.</param> /// <example> /// <code source="Examples\Combination\CnExample01\CnExample01.cs" lang="cs" /> /// </example> /// <exception cref="ArgumentOutOfRangeException"> /// When negative value supplied; when <em>picks</em> greater than <em>choices</em>. /// </exception> /// <exception cref="OverflowException">When the numbers are just too big.</exception> public Combination(int choices, int picks) { if (choices < 0) { throw new ArgumentOutOfRangeException("choices", "Value is less than zero."); } if (picks < 0) { throw new ArgumentOutOfRangeException("picks", "Value is less than zero."); } if (picks > choices) { throw new ArgumentOutOfRangeException("picks", "Value is greater than choices."); } _data = new int[picks]; for (var ki = 0; ki < picks; ++ki) { _data[ki] = ki; } _choices = choices; _rowCount = picks == 0 ? 0 : Combinatoric.BinomialCoefficient(choices, picks); _rank = 0; }
/// <summary> /// Iterate thru all rows of all <see cref="Combination"/> tables for every /// pick in the range (1..<see cref="Picks"/>). /// </summary> /// <returns>An iterator for a series of <see cref="Combination"/> tables.</returns> /// <example> /// <code source="Examples\Combination\CnExample02\CnExample02.cs" lang="cs" /> /// </example> public IEnumerable <Combination> GetRowsForAllPicks() { for (var k = 1; k <= Picks; ++k) { var current = (Combination)MemberwiseClone(); current._data = new int[k]; for (var ei = 0; ei < current._data.Length; ++ei) { current._data[ei] = ei; } current._rowCount = Combinatoric.BinomialCoefficient(_choices, k); current._rank = 0; for (;;) { yield return(current); current.Rank = current.Rank + 1; if (current.Rank == 0) { break; } } } }
// On entry: elements & choices assumed valid, picks = elements.Length. private static long CalcRank(int[] elements, int choices) { long result = 0; var isUsed = new bool[choices]; // // Perform ranking: // for (var ei1 = 0; ei1 < elements.Length; ++ei1) { isUsed[elements[ei1]] = true; var digit = 0; for (var ei2 = 0; ei2 < elements[ei1]; ++ei2) { if (!isUsed[ei2]) { ++digit; } } result += digit * Combinatoric.Factorial(choices - ei1 - 1); } if (elements.Length < choices) { result = result / Combinatoric.Factorial(choices - elements.Length); } return(result); }
/// <summary> /// Make a new <see cref="Combination"/> from the supplied elements. /// </summary> /// <param name="choices">Number of values to pick from.</param> /// <param name="source">Array of integers.</param> /// <example> /// <code source="Examples\Combination\CnExample04\CnExample04.cs" lang="cs" /> /// </example> /// <exception cref="ArgumentNullException">When <em>source</em> is <b>null</b>.</exception> /// <exception cref="ArgumentOutOfRangeException"> /// When length of <em>source</em> is greater than <em>picks</em>; /// when <em>source</em> contains invalid data. /// </exception> public Combination(int choices, int[] source) { if (source == null) { throw new ArgumentNullException("source"); } if (choices < 0) { throw new ArgumentOutOfRangeException("choices", "Value is less than zero."); } if (choices < source.Length) { throw new ArgumentOutOfRangeException("choices", "Value is less than picks."); } _data = new int[source.Length]; source.CopyTo(_data, 0); Array.Sort(_data); _choices = choices; _rowCount = Picks == 0 ? 0 : Combinatoric.BinomialCoefficient(choices, Picks); for (var ki = 0; ki < Picks; ++ki) { if (_data[ki] < 0 || _data[ki] >= choices) { throw new ArgumentOutOfRangeException("source", "Element is out of range."); } if (ki > 0) { if (_data[ki] == _data[ki - 1]) { throw new ArgumentOutOfRangeException("source", "Elements must be unique."); } } } // // Perform ranking: // _rank = 0; var ji = 0; for (var ki = 0; ki < Picks; ++ki) { for (; ji < _data[ki]; ++ji) { _rank += Combinatoric.BinomialCoefficient(Choices - ji - 1, Picks - ki - 1); } ji = _data[ki] + 1; } }
/// <summary> /// Make a new <see cref="Multicombination"/> from the supplied /// <em>choices</em> of the same <em>Picks</em>. /// </summary> /// <param name="choices">Number of elements in the sequence.</param> /// <exception cref="ArgumentOutOfRangeException"> /// When <em>choices</em> less than 0. /// </exception> public Multicombination(int choices) { if (choices < 0) { throw new ArgumentOutOfRangeException("choices", "Value is less than zero."); } _data = new int[choices]; _choices = choices; _rowCount = choices == 0 ? 0 : Combinatoric.BinomialCoefficient(Picks + choices - 1, Picks); _rank = 0; }
// On entry: elements allocated for choices. // On exit: result will be in elements. private static void CalcPlainUnrank(int[] elements, long plainRank) { elements[0] = 0; for (var ei = 1; ei < elements.Length; ++ei) { var yd = (int)(Combinatoric.Factorial(elements.Length) / Combinatoric.Factorial(ei + 1)); var yi = (int)((plainRank / yd) % ((ei + 1) * 2)); var ip = yi <= ei ? ei - yi : yi - ei - 1; for (var si = ei; si > ip; --si) { elements[si] = elements[si - 1]; } elements[ip] = ei; } }
// On entry: choices, picks assumed valid. private static long CalcCount(int choices, int picks) { if (picks == 0) { return(0); } var result = Combinatoric.Factorial(choices); if (picks < choices) { result = result / Combinatoric.Factorial(choices - picks); } return(result); }
/// <summary> /// Make a new <see cref="Combination"/> from the supplied /// <em>choices</em> and <em>picks</em> of the supplied <em>rank</em>. /// </summary> /// <remarks> /// If the supplied <em>rank</em> is out of the range (0..<see cref="RowCount"/>-1), /// it will be normalized to the valid range. For example, a value of -1 will /// produce the last row in the ordered table. /// </remarks> /// <param name="choices">Number of values to pick from.</param> /// <param name="picks">Number of elements in the sequence.</param> /// <param name="rank">Initial row index in the ordered <see cref="Combination"/> table.</param> /// <example> /// <code source="Examples\Combination\CnExample05\CnExample05.cs" lang="cs" /> /// </example> /// <exception cref="ArgumentOutOfRangeException"> /// When negative value supplied; when <em>picks</em> greater than <em>choices</em>. /// </exception> /// <exception cref="OverflowException">When too many <em>choices</em>.</exception> public Combination(int choices, int picks, long rank) { if (choices < 0) { throw new ArgumentOutOfRangeException("choices", "Value is less than zero."); } if (picks < 0) { throw new ArgumentOutOfRangeException("picks", "Value is less than zero."); } if (picks > choices) { throw new ArgumentOutOfRangeException("picks", "Value is greater than choices."); } _data = new int[picks]; _choices = choices; _rowCount = picks == 0 ? 0 : Combinatoric.BinomialCoefficient(choices, picks); Rank = rank; }
/// <summary> /// Make a new <see cref="Multicombination"/> from the supplied /// <em>choices</em> and <em>picks</em> of the supplied <em>rank</em>. /// </summary> /// <remarks> /// If the supplied <em>rank</em> is out of the range (0..<see cref="RowCount"/>-1), /// it will be normalized to the valid range. For example, a value of -1 will /// produce the last row in the ordered table. /// </remarks> /// <param name="choices">Number of values to pick from.</param> /// <param name="picks">Number of elements in the sequence.</param> /// <param name="rank">Initial row index in the ordered <see cref="Multicombination"/> table.</param> /// <example> /// <code source="Examples\Multicombination\McExample05\McExample05.cs" lang="cs" /> /// </example> /// <exception cref="ArgumentOutOfRangeException"> /// When negative value supplied; when <em>choices</em> is 0 and <em>picks</em> is not 0. /// </exception> /// <exception cref="OverflowException">When too many <em>choices</em>.</exception> public Multicombination(int choices, int picks, long rank) { if (choices < 0) { throw new ArgumentOutOfRangeException("choices", "Value is less than zero."); } if (picks < 0) { throw new ArgumentOutOfRangeException("picks", "Value is less than zero."); } if (choices == 0 && picks > 0) { throw new ArgumentOutOfRangeException("choices", "Value is zero and picks is nonzero."); } _data = new int[picks]; _choices = choices; _rowCount = picks == 0 ? 0 : Combinatoric.BinomialCoefficient(picks + choices - 1, picks); Rank = rank; }
/// <summary> /// Iterate thru all rows of all <see cref="Multicombination"/> tables for every /// pick in the range (<em>startPicks</em>..<em>stopPicks</em>). /// </summary> /// <returns>An iterator for a series of <see cref="Multicombination"/> tables.</returns> /// <remarks> /// Unlike <see cref="Picks"/>, <see cref="Choices"/> may exceed <see cref="Choices"/>. /// </remarks> /// <param name="startPicks">Number of picks for first table.</param> /// <param name="stopPicks">Number of picks for last table.</param> /// <example> /// <code source="Examples\Multicombination\McExample02\McExample02.cs" lang="cs" /> /// </example> /// <exception cref="Combination"> /// When <em>startPicks</em> is less than 0 or greater than <em>stopPicks</em>. /// </exception> public IEnumerable <Multicombination> GetRowsForPicks(int startPicks, int stopPicks) { if (startPicks < 0 || startPicks > stopPicks) { throw new ArgumentOutOfRangeException("startPicks", "Pick range is not valid."); } if (Choices == 0) { yield break; } if (startPicks == 0) { startPicks = 1; } for (var k = startPicks; k <= stopPicks; ++k) { var current = (Multicombination)MemberwiseClone(); current._data = new int[k]; current._rowCount = Combinatoric.BinomialCoefficient(k + _choices - 1, k); current._rank = 0; for (;;) { yield return(current); current.Rank = current.Rank + 1; if (current.Rank == 0) { break; } } } }
/// <summary> /// Make a new <see cref="Multicombination"/> from the supplied elements. /// </summary> /// <param name="choices">Number of values to pick from.</param> /// <param name="source">Array of integers.</param> /// <example> /// <code source="Examples\Multicombination\McExample04\McExample04.cs" lang="cs" /> /// </example> /// <exception cref="ArgumentNullException">When <em>source</em> is <b>null</b>.</exception> /// <exception cref="ArgumentOutOfRangeException"> /// When <em>source</em> contains invalid data; /// when <em>choices</em> is 0 and <em>source</em> is not empty. /// </exception> public Multicombination(int choices, int[] source) { if (source == null) { throw new ArgumentNullException("source"); } if (choices < 0) { throw new ArgumentOutOfRangeException("choices", "Value is less than zero."); } if (choices == 0 && source.Length > 0) { throw new ArgumentOutOfRangeException("choices", "Value is zero and picks is nonzero."); } _data = new int[source.Length]; source.CopyTo(_data, 0); Array.Sort(_data); _choices = choices; _rowCount = Picks == 0 ? 0 : Combinatoric.BinomialCoefficient(Picks + choices - 1, Picks); for (var ki = 0; ki < Picks; ++ki) { if (_data[ki] < 0 || _data[ki] >= choices) { throw new ArgumentOutOfRangeException("source", "Element is out of range."); } } // // Perform ranking: // _rank = 0; if (RowCount == 0) { return; } var comboElement = _data[0]; var ji = 0; for (var ki = 0;;) { for (; ji < comboElement; ++ji) { _rank += Combinatoric.BinomialCoefficient(Choices + Picks - ji - 2, Picks - ki - 1); } ++ki; if (ki >= Picks) { break; } ji = comboElement + 1; comboElement = _data[ki] - _data[ki - 1] + ji; } }