前言
首先基於一個事實:我們不可能真的把 n! 的結果計算出來,再去數結果的末尾有幾個0;n 很小還好,如果n很大,甚至趨近於無窮大,我們是不可能這樣做的。原因主要有二:
- 一般計算機的計算能力和存儲能力也有限,是計算不出那麼大的數的。
- 即使計算機能算出來,這樣做也很耗時,可能要算很久。
連計算機都算不出來,那我們怎麼辦呢?別慌,雖然我們不能直接算出結果,但我們可以把問題一步步拆解。
拆解思路
首先,我們想什麼情況下會產生一個0?
誒,一個數乘以 10,在末尾就會多出一個 0。而 10 = 5 * 2。
一組數相乘的結果末尾有幾個0,取決於這組數因式分解後有**幾對 **5 和 2 的因子。
針對於 n! 這個題目,有這樣一個事實:把相乘的數因式分解後,2 的個數肯定大於 5 的個數。
所以,這個問題可以拆解爲:只要求出因式分解後有幾個 5 的因子即可,5的個數即是末尾出現的0的個數。
解法一:直接法
這種解法的思路是:直接將 n! 中的每個數,按照 5 來因式分解,最後把出現的 5 的個數加起來。
public int calculateZeroInFactorial(int n) {
int count = 0;
// 循環判斷所有的乘數
for (int i = n; i > 0; i++) {
if (i % 5 == 0) {
// 如果這個乘數可以對 5 進行因式分解,再看這個乘數可以分解出幾個5
int a = i;
while(a % 5 == 0) {
a = a / 5;
count++;
}
}
}
return count;
}
但是這種算法的時間複雜度爲 O(nlog(n)),那有沒有更快的算法呢?
解法二: log(n) 解法
分析:
- n! 這些乘數中,每隔 5 個數,肯定會有一個數至少能拆出一個 5 因子。所以 n / 5 = 至少會出現的 5 的個數。
- 上面說至少,因爲 n / 5 並不能完全算出 5 因子的個數,比如若某個數 25 = 5 * 5,分解後得到的 5 也算一個,所以能被 25 因式分解相當於會出現 2 個 5 因子,而第一步中除以 5 算個數的時候已經算了一個了,所以相當於比之前會多一個 5 因子。
- 依此類推,能被 25 * 5 = 125 因式分解的相當於比之前按 25 因式分解的時候又多出一個 5 因子。能被 125 * 5 = 625 因式分解的相當於比按 125 因式分解時又多出一個 5 因子。還有 625 * 5 ……
所以,n! 的結果可以拆分爲多少個 5 因子呢?
n/5 + n/25 + n /125 + n/625 + ….
比如 128!的階乘的結果末尾有幾個0呢?
128/5 +128/25 + 128/125 = 25+5+1 = 31 個
又如:1247! 的階乘的結果末尾有幾個0呢?
1247/5 + 1247/25 + 1247/125 + 1247/625 = 249+49+9+1 = 308 個
public int calculateZeroInLogN(int n) {
int count = 0;
while (n > 0) {
count += n / 5;
n /= 5;
}
return count;
}
這種算法的時間複雜度爲 O(log(n)),效率會高很多,而且僅需幾行代碼。