[英]What is the greatest factorial we can calculate using an int in JAVA 19?
我們可以使用 Java 19 中的整數計算出的最大階乘是多少?
我發現使用 int 存儲結果的計算直到Factorial(12)都是正確的。
這是我使用的遞歸代碼:
public static int Factorial(int n) {
if (n >= 13) {
System.out.println("Trop grand");
return -1;
}
if (n == 0) {
return 1;
} else {
return n * Factorial(n - 1);
}
}
你找到相同的結果嗎?
附屬問題:你將如何實現帶有 long 變量的 function?
我嘗試了一切,沒有期望,也沒有發生什么特別的事情。 (我必須填補這個空白。)
我沒有在 Java 中編碼,但是基於 C/C++ 的語言中的int
是有符號的 integer,這意味着它使用一位作為符號(是的,即使你使用補碼),所以如果你有 32/64 位整數,你可以僅使用數字(不包括):
2^31 = 2147483648
2^63 = 9223372036854775808
現在查看從Fast exact bigint factorial中獲取的前幾個階乘:
[ 0.001 ms ] 1! = 1
[ 0.000 ms ] 2! = 2
[ 0.000 ms ] 3! = 6
[ 0.000 ms ] 4! = 24
[ 0.006 ms ] 5! = 120
[ 0.006 ms ] 6! = 720
[ 0.007 ms ] 7! = 5040
[ 0.005 ms ] 8! = 40320
[ 0.006 ms ] 9! = 362880
[ 0.007 ms ] 10! = 3628800
[ 0.008 ms ] 11! = 39916800
[ 0.012 ms ] 12! = 479001600
2^31 = 2147483648 <-------------------------
[ 0.013 ms ] 13! = 6227020800
[ 0.014 ms ] 14! = 87178291200
[ 0.016 ms ] 15! = 1307674368000
[ 0.014 ms ] 16! = 20922789888000
[ 0.015 ms ] 17! = 355687428096000
[ 0.017 ms ] 18! = 6402373705728000
[ 0.019 ms ] 19! = 121645100408832000
[ 0.016 ms ] 20! = 2432902008176640000
2^63 = 9223372036854775808 <-------------------------
[ 0.017 ms ] 21! = 51090942171709440000
[ 0.019 ms ] 22! = 1124000727777607680000
所以我預計20!
應該仍然適合 64 位有符號整數。
如果你使用unsigned int
你可以計算兩倍的數字,但是在 64 位的情況下是21!
仍然太大...要計算更多,您需要更大的位寬或將結果分解為尾隨零(十進制或二進制)並將結果以兩個整數的形式出現,例如,如下所示:
void fact(int &man,int &exp,int n) // man * 10^exp = n!
{
man=1; exp=0;
if (n<=1) return;
int i,j;
for (i=2;i<=n;i++)
{
j=i;
while (j%10==0){j/=10; exp++; }
man*=j;
if (man<0){ man=0; exp=0; return; } // overflow
while (man%10==0){ man/=10; exp++; }
}
}
我在VCL和 32 位有符號整數中使用它,如下所示:
int i,m,e;
AnsiString s;
for (i=0;i<40;i++)
{
fact(m,e,i);
s=m; while (e){ s+="0"; e--; } // just print m to s and add e times "0" at the end
mm_log->Lines->Add(AnsiString().sprintf("%2i! = %s",i,s)); // output to memo
}
有了這個 output:
0! = 1
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880
10! = 3628800
11! = 39916800
12! = 479001600
13! = 6227020800
14! = 87178291200
15! = 19184179200 <- this one is overflowed too
16! = 0
17! = 0
18! = 0
19! = 0
20! = 0
21! = 0
22! = 0
23! = 0
24! = 0
25! = 0
26! = 0
27! = 0
28! = 0
29! = 0
30! = 0
31! = 0
32! = 0
33! = 0
34! = 0
35! = 0
36! = 0
37! = 0
38! = 0
39! = 0
如您所見,我最多可以計算14!
這樣而不是12!
...
此外,您正在使用遞歸計算階乘......迭代要好得多,除非您實現快速階乘,如果沒有大整數就沒有意義。
可以使用int
在 Java 中計算的最大階乘是 12. = 479001600, (12, = 479.001,600)。
這是一些代碼來測試:
public static void factorialTest () {
int i = 0, r = 1;
while (r > 0) {
r = factorial (i);
System.out.println ((i++) + "! = " + r);
}
}
public static int factorial(int n) {
if (n < 0) { return -1; }
if (n == 0 || n == 1) { return 1; }
int r = 1;
try {
for (int i = 1; i <= n; ++i) {
r = Math.multiplyExact (r, i);
}
} catch (ArithmeticException ex) {
return -2;
}
return r;
}
如果傳遞負數,方法public static int factorial(int n)
返回-1
,如果n
的值會導致計算的數字可能溢出int
,則返回-2
。
Java 中的 Integer 計算可以溢出而不產生異常。 如果計算結果太大,則結果“環繞”。 有時,需要檢測溢出。 為了支持這一點,Java Math
API有許多~Exact
方法。 當發生溢出時,它們會拋出ArithmeticException
。
所以,這里的想法是計算 integer 階乘,從 1 開始,遞增直到捕獲到溢出,使用try... catch
。
可以想象,這可以在不使用try... catch
的情況下通過測試乘法結果小於先前結果的結果來完成。 由於結果序列是嚴格遞增的,因此在結果序列中找到較小的值將表明發生了回繞。 然而,這是不可靠的:計算可能會回繞,但仍會產生比先前結果更大的不正確結果。
在 Java 中使用long
可以計算出的最大階乘是 20. = 2432902008176640000,即 20,= 2,432,902,008,176.640,000。 可以通過將上面代碼中的所有int
更改為long
來測試這一點。
注釋:雖然 Java 不允許聲明unsigned
integer 類型,但無符號 integer 計算是可能的。 對無符號計算的支持包括結果環繞的計算和無符號方法,例如toUnsignedString
。 然而,這似乎與本次討論無關,因為計算 13! 會溢出unsigned int
和 21 的計算! 會溢出unsigned long
。
評論:O/P 使用遞歸來計算階乘。 我使用了迭代。 雖然我是遞歸的粉絲,但當迭代解決方案很容易找到和實現時,我更喜歡使用迭代。 遞歸可以使用比迭代更多的開銷。
評論:優化編譯器有時會將使用遞歸的代碼更改為使用迭代。 O/P 的代碼中可能就是這種情況。
Output,使用long
:
0! = 1
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880
10! = 3628800
11! = 39916800
12! = 479001600
13! = 6227020800
14! = 87178291200
15! = 1307674368000
16! = 20922789888000
17! = 355687428096000
18! = 6402373705728000
19! = 121645100408832000
20! = 2432902008176640000
21! = -2
理論上,Java 最多支持 2^MAXINT-1 的階乘。 顯然,這是一個非常瘋狂的數字。 特定的 JVM,根據它們的實現,可能支持更大的值,但這不是語言規范所要求的。
這是您能夠裝入 BigInteger 的最高值。 您是否真的可以計算出那么高的數字,這顯然取決於您 go 如何進行該計算以及您手頭有多少時間來執行計算。
來自 JDK API 文檔:
當為任何輸入參數傳遞 null object 引用時,此 class 中的所有方法和構造函數都會拋出 NullPointerException。 BigInteger 必須支持 -2Integer.MAX_VALUE(不包括)到 +2Integer.MAX_VALUE(不包括)范圍內的值,並且可以支持該范圍之外的值。 當 BigInteger 構造函數或方法生成超出支持范圍的值時,將拋出 ArithmeticException。
請參閱 Java™ 語言規范:4.2.2 Integer 操作
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.