/// <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); }
/// <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)); } } }