diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/math/MathUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/math/MathUtil.java
index bbcbe4c5c..15eaf731b 100644
--- a/hutool-core/src/main/java/org/dromara/hutool/core/math/MathUtil.java
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/math/MathUtil.java
@@ -18,6 +18,8 @@ import org.dromara.hutool.core.text.StrUtil;
import java.math.BigInteger;
import java.util.List;
+import static java.lang.Math.min;
+
/**
* 数学相关方法工具类
* 此工具类与{@link NumberUtil}属于一类工具,NumberUtil偏向于简单数学计算的封装,MathUtil偏向复杂数学计算
@@ -31,11 +33,12 @@ public class MathUtil {
* 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};
+ 1L, 1L, 2L, 6L, 24L, 120L, 720L, 5040L, 40320L, 362880L, 3628800L, 39916800L, 479001600L, 6227020800L,
+ 87178291200L, 1307674368000L, 20922789888000L, 355687428096000L, 6402373705728000L, 121645100408832000L,
+ 2432902008176640000L};
//--------------------------------------------------------------------------------------------- Arrangement
+
/**
* 计算排列数,即A(n, m) = n!/(n-m)!
*
@@ -61,7 +64,7 @@ public class MathUtil {
* 排列选择(从列表中选择n个排列)
*
* @param datas 待选列表
- * @param m 选择个数
+ * @param m 选择个数
* @return 所有排列列表
*/
public static List arrangementSelect(final String[] datas, final int m) {
@@ -79,6 +82,7 @@ public class MathUtil {
}
//--------------------------------------------------------------------------------------------- Combination
+
/**
* 计算组合数,即C(n, m) = n!/((n-m)!* m!)
*
@@ -94,7 +98,7 @@ public class MathUtil {
* 组合选择(从列表中选择n个组合)
*
* @param datas 待选列表
- * @param m 选择个数
+ * @param m 选择个数
* @return 所有组合列表
*/
public static List combinationSelect(final String[] datas, final int m) {
@@ -270,19 +274,57 @@ public class MathUtil {
}
/**
- * 最大公约数
+ * 最大公约数
+ * 见:https://stackoverflow.com/questions/4009198/java-get-greatest-common-divisor
+ * 来自Guava的IntMath.gcd
*
- * @param m 第一个值
- * @param n 第二个值
+ * @param a 第一个值
+ * @param b 第二个值
* @return 最大公约数
*/
- public static int divisor(int m, int n) {
- while (m % n != 0) {
- final int temp = m % n;
- m = n;
- n = temp;
+ public static int gcd(int a, int b) {
+ /*
+ * The reason we require both arguments to be >= 0 is because otherwise, what do you return on
+ * gcd(0, Integer.MIN_VALUE)? BigInteger.gcd would return positive 2^31, but positive 2^31
+ * isn't an int.
+ */
+ Assert.isTrue(a >= 0, "a must be >= 0");
+ Assert.isTrue(b >= 0, "b must be >= 0");
+ if (a == 0) {
+ // 0 % b == 0, so b divides a, but the converse doesn't hold.
+ // BigInteger.gcd is consistent with this decision.
+ return b;
+ } else if (b == 0) {
+ return a; // similar logic
}
- return n;
+ /*
+ * Uses the binary GCD algorithm; see http://en.wikipedia.org/wiki/Binary_GCD_algorithm.
+ * This is >40% faster than the Euclidean algorithm in benchmarks.
+ */
+ final int aTwos = Integer.numberOfTrailingZeros(a);
+ a >>= aTwos; // divide out all 2s
+ final int bTwos = Integer.numberOfTrailingZeros(b);
+ b >>= bTwos; // divide out all 2s
+ while (a != b) { // both a, b are odd
+ // The key to the binary GCD algorithm is as follows:
+ // Both a and b are odd. Assume a > b; then gcd(a - b, b) = gcd(a, b).
+ // But in gcd(a - b, b), a - b is even and b is odd, so we can divide out powers of two.
+
+ // We bend over backwards to avoid branching, adapting a technique from
+ // http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
+
+ final int delta = a - b; // can't overflow, since a and b are nonnegative
+
+ final int minDeltaOrZero = delta & (delta >> (Integer.SIZE - 1));
+ // equivalent to Math.min(delta, 0)
+
+ a = delta - minDeltaOrZero - minDeltaOrZero; // sets a to Math.abs(a - b)
+ // a is now nonnegative and even
+
+ b += minDeltaOrZero; // sets b to min(old a, b)
+ a >>= Integer.numberOfTrailingZeros(a); // divide out all 2s, since 2 doesn't divide b
+ }
+ return a << min(aTwos, bTwos);
}
/**
@@ -293,7 +335,7 @@ public class MathUtil {
* @return 最小公倍数
*/
public static int multiple(final int m, final int n) {
- return m * n / divisor(m, n);
+ return m * n / gcd(m, n);
}
private static int mathSubNode(final int selectNum, final int minNum) {