/// <summary> /// <para> /// Returns the least common multiple of the absolute value of two numbers, /// using the formula <c>lcm(a,b) = (a / gcd(a,b)) * b</c>. /// </para> /// Special cases: /// <list type="bullet"> /// <item>The invocations <c>lcm(Int64.MinValue, n)</c> and /// <c>lcm(n, Int64.MinValue)</c>, where <c>abs(n)</c> is a /// power of 2, throw an <c>ArithmeticException</c>, because the result /// would be 2^63, which is too large for an int value.</item> /// <item>The result of <c>lcm(0L, x)</c> and <c>lcm(x, 0L)</c> is /// <c>0L</c> for any <c>x</c>.</item> /// </list> /// </summary> /// <param name="a">Number.</param> /// <param name="b">Number.</param> /// <returns>the least common multiple, never negative.</returns> /// <exception cref="MathArithmeticException"> if the result cannot be represented /// as a non-negative <c>long</c> value.</exception> public static long lcm(long a, long b) { if (a == 0 || b == 0) { return(0); } long lcm = FastMath.abs(ArithmeticUtils.mulAndCheck(a / gcd(a, b), b)); if (lcm == Int64.MinValue) { throw new MathArithmeticException(new LocalizedFormats("LCM_OVERFLOW_64_BITS"), a, b); } return(lcm); }
/// <summary> /// <para> /// Returns the least common multiple of the absolute value of two numbers, /// using the formula <c>lcm(a,b) = (a / gcd(a,b)) * b</c>. /// </para> /// Special cases: /// <list type="bullet"> /// <item>The invocations <c>lcm(Int32.MinValue, n)</c> and /// <c>lcm(n, Int32.MinValue)</c>, where <c>abs(n)</c> is a /// power of 2, throw an <c>ArithmeticException</c>, because the result /// would be 2^31, which is too large for an int value.</item> /// <item>The result of <c>lcm(0, x)</c> and <c>lcm(x, 0)</c> is /// <c>0</c> for any <c>x</c>.</item> /// </list> /// </summary> /// <param name="a">Number.</param> /// <param name="b">Number.</param> /// <returns>the least common multiple, never negative.</returns> /// <exception cref="MathArithmeticException"> if the result cannot be represented as /// a non-negative <c>int</c> value.</exception> public static int lcm(int a, int b) { if (a == 0 || b == 0) { return(0); } int lcm = FastMath.abs(ArithmeticUtils.mulAndCheck(a / gcd(a, b), b)); if (lcm == Int32.MinValue) { throw new MathArithmeticException(new LocalizedFormats("LCM_OVERFLOW_32_BITS"), a, b); } return(lcm); }
/// <summary> /// Returns an exact representation of the <a /// href="http://mathworld.wolfram.com/BinomialCoefficient.html"> Binomial /// Coefficient</a>, "<c>n choose k</c>", the number of /// <c>k</c>-element subsets that can be selected from an /// <c>n</c>-element set. /// <para> /// Preconditions: /// <list type="bullet"> /// <item> <c>0 <= k <= n</c> (otherwise /// <c>MathIllegalArgumentException</c> is thrown)</item> /// <item> The result is small enough to fit into a <c>long</c>. The /// largest value of <c>n</c> for which all coefficients are /// <c> < Int64.MaxValue</c> is 66. If the computed value exceeds /// <c>Int64.MaxValue</c> an <c>ArithMeticException</c> is /// thrown.</item> /// </list></para> /// </summary> /// <param name="n">the size of the set</param> /// <param name="k">the size of the subsets to be counted</param> /// <returns><c>n choose k</c></returns> /// <exception cref="NotPositiveException"> if <c>n < 0</c>.</exception> /// <exception cref="NumberIsTooLargeException"> if <c>k > n</c>.</exception> /// <exception cref="MathArithmeticException"> if the result is too large to be /// represented by a long integer.</exception> public static long binomialCoefficient(int n, int k) { CombinatoricsUtils.checkBinomial(n, k); if ((n == k) || (k == 0)) { return(1); } if ((k == 1) || (k == n - 1)) { return(n); } // Use symmetry for large k if (k > n / 2) { return(binomialCoefficient(n, n - k)); } // We use the formula // (n choose k) = n! / (n-k)! / k! // (n choose k) == ((n-k+1)*...*n) / (1*...*k) // which could be written // (n choose k) == (n-1 choose k-1) * n / k long result = 1; if (n <= 61) { // For n <= 61, the naive implementation cannot overflow. int i = n - k + 1; for (int j = 1; j <= k; j++) { result = result * i / j; i++; } } else if (n <= 66) { // For n > 61 but n <= 66, the result cannot overflow, // but we must take care not to overflow intermediate values. int i = n - k + 1; for (int j = 1; j <= k; j++) { // We know that (result * i) is divisible by j, // but (result * i) may overflow, so we split j: // Filter out the gcd, d, so j/d and i/d are integer. // result is divisible by (j/d) because (j/d) // is relative prime to (i/d) and is a divisor of // result * (i/d). long d = ArithmeticUtils.gcd(i, j); result = (result / (j / d)) * (i / d); i++; } } else { // For n > 66, a result overflow might occur, so we check // the multiplication, taking care to not overflow // unnecessary. int i = n - k + 1; for (int j = 1; j <= k; j++) { long d = ArithmeticUtils.gcd(i, j); result = ArithmeticUtils.mulAndCheck(result / (j / d), i / d); i++; } } return(result); }
/// <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)); } } }