1. 程式人生 > >【LintCode】2、尾部的零

【LintCode】2、尾部的零

1、題目

描述:
設計一個演算法,計算出n階乘中尾部零的個數

樣例:
11! = 39916800,因此應該返回 2

2、分析

假如你把1 × 2 ×3× 4 ×……×N中每一個因數分解質因數,結果就像: 1 × 2 × 3 × (2 × 2) × 5 × (2 × 3) × 7 × (2 × 2 ×2) ×…… 10進位制數結尾的每一個0都表示有一個因數10存在——任何進位制都一樣,對於一個M進位制的數,讓結尾多一個0就等價於乘以M。10可以分解為2 × 5——因此只有質數2和5相乘能產生0,別的任何兩個質數相乘都不能產生0,而且2,5相乘只產生一個0。 所以,分解後的整個因數式中有多少對(2, 5),結果中就有多少個0,而分解的結果中,2的個數顯然是多於5的,因此,有多少個5,就有多少個(2, 5)對。 所以,討論1000的階乘結尾有幾個0的問題,就被轉換成了1到1000所有這些數的質因數分解式有多少個5的問題。

假設有序列1、2、3、4、5、6、7、8、9、10、11、…

分析上面的數列可知,每5個數中會出現一個可以產生結果中0的數字。把這些數字抽取出來是:

…、5、…、10、…、15、…、20、…、25、…

這些數字其實是都能滿足5*k的數字,是5的倍數。統計一下他們的數量:n1=N/5。比如如果是101,則101之前應該是5,10,15,20,…,95,100共101/5=20個數字滿足要求。

整除操作滿足上面的數量統計要求。

將1中的這些數字化成5*(1、2、3、4、5、…)的形式,內部的1、2、3、4、5、…又滿足上面的分析:每5個數字有一個是5的倍數。抽取為:

…、25、…、50、…、75、…、100、…、125、…

而這些數字都是25的倍數(5的2次冪的倍數),自然也都滿足5*k的要求。
這些數字是25、50、75、100、125、…=5*(5、10、15、20、25、…)=5*5*(1、2、3、4、5、…),內部的1、2、3、4、5、…又滿足上面的分析,因此後續的操作重複上述步驟即可。

統計一下第二次中滿足條件的數字數量:n2=N/5/5,101/25=(101/5)/5=4。

因為25、50、75、100、125、…它們都滿足相乘後產生至少兩個0,在第一次5*k分析中已經統計過一次。對於N=101,是20。因此此處的5*5*k只要統計一次4即可,不需要根據25是5的二次冪統計兩次。

後面的125,250,…等乘積為1000的可以為結果貢獻3個0的數字,只要在5*5*k的基礎上再統計一次n3=((N/5)/5)/5即可。

核心點:尾部的零有5的個數決定,單5(因式分解後有1個5)與某偶數相乘有單0,多5(因式分解後有多個5)與某偶數相乘有多個0。

3、AC程式碼

public class Solution {
    /*
     * @param n: An integer
     * @return: An integer, denote the number of trailing zeros in n!
     */
    public long trailingZeros(long n) {
        // write your code here, try to do it without arithmetic operators.
        long sum = 0;
        while (n > 0) {
            sum += n / 5;
            n /= 5;
        }
        return sum;
    }
}