Merge pull request #1134 from akiyamaneko/factorial_improved

优化阶乘相关的逻辑
This commit is contained in:
Golden Looly 2020-09-28 19:00:53 +08:00 committed by GitHub
commit d775ce9cca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 323 additions and 288 deletions

View File

@ -43,6 +43,14 @@ public class NumberUtil {
*/
private static final int DEFAUT_DIV_SCALE = 10;
/**
* 0-20对应的阶乘超过20的阶乘会超过Long.MAX_VALUE
*/
private static final long[] FACTORIALS = new long[]{
1L, 1L, 2L, 6L, 24L, 120L, 720L, 5040L, 40320L, 362880L, 3628800L, 39916800L, 479001600L, 6227020800L,
87178291200L, 1307674368000L, 20922789888000L, 355687428096000L, 6402373705728000L, 121645100408832000L,
2432902008176640000L};
/**
* 提供精确的加法运算
*
@ -1423,13 +1431,31 @@ public class NumberUtil {
* @since 4.1.0
*/
public static long factorial(long start, long end) {
// 负数没有阶乘
if(start < 0 || end < 0) {
throw new IllegalArgumentException(String.format("Factorial start and end both must be >= 0, " +
"but got start=%d, end=%d", start, end));
}
if (0L == start || start == end) {
return 1L;
}
if (start < end) {
return 0L;
}
return start * factorial(start - 1, end);
return factorialMultiplyAndCheck(start, factorial(start - 1, end));
}
/**
* 计算范围阶乘中校验中间的计算是否存在溢出factorial提前做了负数和0的校验因此这里没有校验数字的正负
* @param a 乘数
* @param b 被乘数
* @return 如果 a * b的结果没有溢出直接返回否则抛出异常
*/
private static long factorialMultiplyAndCheck(long a, long b) {
if (a <= Long.MAX_VALUE / b) {
return a * b;
}
throw new IllegalArgumentException(String.format("Overflow in multiplication: {%d} * {%d}", a, b));
}
/**
@ -1442,7 +1468,11 @@ public class NumberUtil {
* @return 结果
*/
public static long factorial(long n) {
return factorial(n, 1);
if (n < 0 || n > 20) {
throw new IllegalArgumentException(String.format("Factorial must have n >= 0 and n <= 20 for n!, " +
"but got n = %d", n));
}
return FACTORIALS[(int) n];
}
/**

View File

@ -241,12 +241,17 @@ public class NumberUtilTest {
long factorial = NumberUtil.factorial(0);
Assert.assertEquals(1, factorial);
Assert.assertEquals(1L, NumberUtil.factorial(1));
Assert.assertEquals(1307674368000L, NumberUtil.factorial(15));
Assert.assertEquals(2432902008176640000L, NumberUtil.factorial(20));
factorial = NumberUtil.factorial(5, 0);
Assert.assertEquals(120, factorial);
factorial = NumberUtil.factorial(5, 1);
Assert.assertEquals(120, factorial);
Assert.assertEquals(5, NumberUtil.factorial(5, 4));
Assert.assertEquals(2432902008176640000L, NumberUtil.factorial(20, 0));
}
@Test