[編程之美] 2.2 不要被階乘嚇倒

這一節給出了關於階乘的兩個問題,當然,並不是求階乘的值,而是求階乘的值的一些特性。

問題1:給定一個整數N,那麼N的階乘N! 末尾有多少個0?

如果將N! 的結果求出來,然後再計算末尾0的個數,那就要考慮是否會溢出,計算時間也要考慮。

這裏的思路:考慮哪些數相乘能得到10。將結果表示成N! = K * pow(10, M),然後對N! 進行因式分解,只有2 * 5會得到10,那麼N! 的末尾的0的個數就是因式分解後2和5的冪的最小值。由於整數中出現2的倍數的頻率比出現5的頻率低,因此,結果就可以直接等於5的冪。

解法一直接處理1到N所有的數,對於每個數,求出它的5的冪。

解法二就要細說了:

將結果表示爲[N/5] + [N/pow(5, 2)] + [N/pow(5, 3)] +...,也就是說,5的倍數貢獻一個5,pow(5, 2)的倍數再貢獻一個5,pow(5, 3)的倍數再貢獻一個5。

再來看看源代碼:

ret = 0;
while(N) {
	ret += N / 5;
	N /= 5;
}

N/5就能夠得到1到N中有多少個5的倍數,然後再N /= 5,也就是N / 25,即pow(5, 2)的倍數貢獻的5的個數。


問題2:求N! 的二進制表示中最低位1的位置。

要求階乘的二進制表示中最低位1的位置,其實就是求N! 的二進制表示中末尾有多少個0,也即N! 含有質因數2的個數。

對上面一句話的理解:如果N!含有n個2的乘積,那麼,N!必定可以通過右移n-1位使得最後一位不爲1,因此,就是尋找N!含有質因數2的個數。

由問題一有:[N/2] + [N/pow(2, 2)] + [N/pow(2, 3)] + ...。

對上面一句的理解:N/2得到的是1到N中2的倍數的個數(也就是2,4,6,8等等),N/pow(2, 2)得到的就是1到N中4的倍數的個數(也就是4,8,12,16等等),後面的依次類推。

那麼將上述代碼中的5換成2即可,還可以將除以2的操作換成位移。

問題二的解法二在書中只給了個小例子,沒有證明,就不多說了,不過如果採用這種方法倒是可以用上2.1中求二進制表示中1的數目。


相關題目

給定整數n,判斷它是否爲2的方冪。

如果n是2的方冪,那麼,n的二進制表示中只有一個1,使用2.1中的方法可以將最後一個1消去,此時,如果結果是0說明只有一個1:

n > 0 && (n & (n - 1)) == 0

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章