初始化
dongbinhui authored at 2023-02-23 22:25:53
5.34 KiB
KwCombinatorics.Core
using System;
using System.Collections.Generic;

namespace Kw.Combinatorics
{
    /// <summary>
    /// Provides static methods for combinatorics.
    /// </summary>
    /// <remarks>
    /// This class cannot be instantiated.
    /// </remarks>
    public static class Combinatoric
    {
        #region Private static fields

        static internal readonly long[] factorial = new long[]
        {
            1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 
            479001600, 6227020800, 87178291200, 1307674368000, 20922789888000,
            355687428096000, 6402373705728000, 121645100408832000, 2432902008176640000
        };

        static private List<long[]> pascalsTriangle = null;
        static private int pascalsTriangleMaxN = -1;

        #endregion

        #region Private static methods

        // Returns as many complete rows of Pascal's triangle as possible.
        static private List<long[]> BuildPascalsTriangle ()
        {
            var result = new List<long[]> { new long[] { 1 } };

            try
            {
                for (int n = 1; ; ++n)
                {
                    var row = new long[n+1];
                    row[0] = 1;
                    for (int k = 1; k <= n-1; ++k)
                        row[k] = checked (result[n-1][k-1] + result[n-1][k]);
                    row[n] = 1;
                    result.Add (row);
                }
            }
            catch (OverflowException)
            { }

            return result;
        }

        #endregion

        #region Public static methods

        /// <summary>
        /// Returns the binomial coefficient of the supplied values.
        /// </summary>
        /// <param name="n">Number of choices.</param>
        /// <param name="k">Number of picks.</param>
        /// <returns>
        /// The binomial coefficient of <em>n</em> choose <em>k</em>.
        /// </returns>
        /// <remarks>
        /// The result is equal to row <em>n</em>, column <em>k</em> of Pascal's triangle
        /// with counting starting at 0.
        /// </remarks>
        /// <example>
        /// <para>
        /// The number of rows in a <see cref="Combination"/> table of <em>k</em> picks
        /// from <em>n</em> choices is:<br />
        /// <br />
        /// <c>Combinatoric.BinomialCoefficient (n, k)</c>
        /// </para>
        /// <para>
        /// The number of rows in a <see cref="Multicombination"/> table of <em>k</em> picks
        /// from <em>n</em> choices is:<br />
        /// <br />
        /// <c>Combinatoric.BinomialCoefficient (k+n-1, k)</c>
        /// </para>
        /// <para>
        /// An exception to the above formulas is the special case where the numer of elements
        /// is 0. While mathematics treats this result as 1 row containing the empty product,
        /// this library returns 0 rows.
        /// </para>
        /// </example>
        /// <exception cref="OverflowException">When the numbers are just too big.</exception>
        static public long BinomialCoefficient (int n, int k)
        {
            if (k < 0 || k > n)
                return 0;

            if (n <= pascalsTriangleMaxN)
                return pascalsTriangle[n][k];

            if (pascalsTriangle == null)
            {
                pascalsTriangle = BuildPascalsTriangle ();
                pascalsTriangleMaxN = pascalsTriangle.Count - 1;

                if (n < pascalsTriangleMaxN)
                    return pascalsTriangle[n][k];
            }

            // Row is beyond precalculated table so fall back to multiplicative formula:

            if (k > n - k)
                k = n - k;

            int factor = n - k;
            long bc = 1;

            for (int ki = 1; ki <= k; ++ki)
            {
                ++factor;
                bc = checked (bc * factor) / ki;
            }

            return bc;
        }


        /// <summary>Returns the factorial of the supplied value.</summary>
        /// <param name="value">Non-negative integer.</param>
        /// <returns>
        /// For increasing values starting at 0, returns
        /// 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 
        /// 479001600, 6227020800, 87178291200, 1307674368000, 20922789888000,
        /// 355687428096000, 6402373705728000, 121645100408832000, 2432902008176640000.
        /// </returns>
        /// <example>
        /// <para>
        /// The number of rows in a <see cref="Permutation"/> table of <em>n</em> choices is:<br />
        /// <br />
        /// <c>Combinatoric.Factorial (n)</c><br />
        /// <br />
        /// The number of rows in a <see cref="Permutation"/> table of <em>k</em> picks
        /// from <em>n</em> choices is:<br />
        /// <br />
        /// <c>Combinatoric.Factorial (n) / Combinatoric.Factorial (n-k)</c>
        /// </para>
        /// <para>
        /// An exception to the above formulas is the special case where the number of elements
        /// in the permutation is 0. While mathematics treats this result as 1 row containing the
        /// empty product, this library returns 0 rows.
        /// </para>
        /// </example>
        /// <exception cref="IndexOutOfRangeException">
        /// When <em>value</em> not in range (0..20).
        /// </exception>
        static public long Factorial (int value)
        {
            return factorial[value];
        }

        #endregion
    }
}