コード例 #1
0
            /// <summary>
            /// Computes the value (in base 10) represented by the digit
            /// (interpreted in base <c>n</c>) in the input array in reverse
            /// order.
            /// For example if <c>c</c> is <c>{3, 2, 1</c>}, and <c>n</c>
            /// is 3, the method will return 18.
            /// </summary>
            /// <param name="c">Input array.</param>
            /// <returns>the lexicographic norm.</returns>
            /// <exception cref="OutOfRangeException"> if an element of the array is not
            /// within the interval [0, <c>n</c>).</exception>
            private long lexNorm(int[] c)
            {
                long ret = 0;

                for (int i = 0; i < c.Length; i++)
                {
                    int digit = c[i];
                    if (digit < 0 ||
                        digit >= n)
                    {
                        throw new OutOfRangeException <Int32>(digit, 0, n - 1);
                    }
                    ret += c[i] * ArithmeticUtils.pow(n, i);
                }
                return(ret);
            }
コード例 #2
0
        /// <summary>
        /// Returns the <a
        /// href="http://mathworld.wolfram.com/StirlingNumberoftheSecondKind.html">
        /// Stirling number of the second kind</a>, "<c>S(n,k)</c>", the number of
        /// ways of partitioning an <c>n</c>-element set into <c>k</c> non-empty
        /// subsets.
        /// <para>
        /// The preconditions are <c>0 <= k <= n</c> (otherwise
        /// <c>NotPositiveException</c> is thrown)
        /// </para>
        /// </summary>
        /// <param name="n">the size of the set</param>
        /// <param name="k">the number of non-empty subsets</param>
        /// <returns><c>S(n,k)</c></returns>
        /// <exception cref="NotPositiveException"> if <c>k < 0</c>.</exception>
        /// <exception cref="NumberIsTooLargeException"> if <c>k > n</c>.</exception>
        /// <exception cref="MathArithmeticException"> if some overflow happens, typically
        /// for n exceeding 25 and k between 20 and n-2 (S(n,n-1) is handled specifically
        /// and does not overflow)</exception>
        public static long stirlingS2(int n, int k)
        {
            if (k < 0)
            {
                throw new NotPositiveException <Int32>(k);
            }
            if (k > n)
            {
                throw new NumberIsTooLargeException <Int32, Int32>(k, n, true);
            }

            long[][] stirlingS2;
            lock (STIRLING_S2)
            {
                stirlingS2 = STIRLING_S2;
                if (stirlingS2 == null)
                {
                    // the cache has never been initialized, compute the first numbers
                    // by direct recurrence relation

                    // as S(26,9) = 11201516780955125625 is larger than Long.MAX_VALUE
                    // we must stop computation at row 26
                    int maxIndex = 26;
                    stirlingS2    = new long[maxIndex][];
                    stirlingS2[0] = new long[] { 1L };
                    for (int i = 1; i < stirlingS2.Length; ++i)
                    {
                        stirlingS2[i]    = new long[i + 1];
                        stirlingS2[i][0] = 0;
                        stirlingS2[i][1] = 1;
                        stirlingS2[i][i] = 1;
                        for (int j = 2; j < i; ++j)
                        {
                            STIRLING_S2[i][j] = j * stirlingS2[i - 1][j] + stirlingS2[i - 1][j - 1];
                        }
                    }

                    // atomically save the cache, thread-safe
                    STIRLING_S2 = (long[][])stirlingS2.Clone();
                }
            }

            if (n < stirlingS2.Length)
            {
                // the number is in the small cache
                return(stirlingS2[n][k]);
            }
            else
            {
                // use explicit formula to compute the number without caching it
                if (k == 0)
                {
                    return(0);
                }
                else if (k == 1 || k == n)
                {
                    return(1);
                }
                else if (k == 2)
                {
                    return((1L << (n - 1)) - 1L);
                }
                else if (k == n - 1)
                {
                    return(binomialCoefficient(n, 2));
                }
                else
                {
                    // definition formula: note that this may trigger some overflow
                    long sum  = 0;
                    long sign = ((k & 0x1) == 0) ? 1 : -1;
                    for (int j = 1; j <= k; ++j)
                    {
                        sign = -sign;
                        sum += sign * binomialCoefficient(k, j) * ArithmeticUtils.pow(j, n);
                        if (sum < 0)
                        {
                            // there was an overflow somewhere
                            throw new MathArithmeticException(new LocalizedFormats("ARGUMENT_OUTSIDE_DOMAIN"), n, 0, stirlingS2.Length - 1);
                        }
                    }
                    return(sum / factorial(k));
                }
            }
        }