描述
设计一个算法,计算出n阶乘中尾部 0 的个数
样例
11! = 39916800,因此应该返回2
解题思路
首先考虑,如果 N! = K * 10M,且 K 不能被 10 整除,那么 N! 末尾有 M 个 0。考虑对 N!进行质因数分解,N! = 2x * 3y * 5z…,由于 10 = 2 * 5,所以 M 只和 x 与 z 有关,每一对 2 和 5 相乘可以得到一个 10,于是 M = min{x, z},因为能被 2 整除的数出现的频率比能被 5 整除的数高的多,所以 M = z。所以原问题可以转化为 1 ~ n 所有的数中,一共含有多少个质因子 5 。这里我们利用下面的公式求解:
z =
(这不会是一个无穷的运算,总存在一个 k,使得
)
理解起来就是 1 ~ n 中有
个数,这每个数都能贡献一个 5,然后 1 ~ n 中有
个数,这每个数又能贡献一个 5 …以此类推。
以上分析参考自《编程之美》。
解题代码
#include <iostream>
using namespace std;
class Solution {
public:
/*
* @param n: A long integer
* @return: An integer, denote the number of trailing zeros in n!
*/
long long trailingZeros(long long n) {
// write your code here, try to do it without arithmetic operators.
long long re = 0;
while(n){
re += n / 5;
n /= 5;
}
return re;
}
};
int main()
{
Solution solution;
long long n;
cin >> n;
cout << solution.trailingZeros(n) << endl;
return 0;
}
扩展进阶
求 n! 的二进制表示中最低位 1 的位置。(规定最右的位置为位置 0)
最低位的 1 在哪个位置上,取决于 1~n 的数中有多少个质因子 2,因为只要出现一个质因子 2,最低位的 1 就会向左位移一位,利用下面的公式求解:
质因子 2 的总个数 =
long long lowestOne(long long n) {
long long res = 0;
while(n){
n >>= 1;
res += n;
}
return res;
}
另外还可以通过下面这个规律求解:
n! 含有质因子 2 的个数还等于 n 减去 n 的二进制表示中 1 的数目。 下面来证明这个结论:
我们把 n 的二进制表达式中 1 的个数记为 m,也就是要证明
如果一个整数k正好为2的某次方(即
),那么求和公式
,
根据等比数列求和公式 s = (首项 - 末项 * 公比)/(1 - 公比),可以得到
如果在n的二进制表达中有 m 个 1,那么
,其中
,所以
即证!
long long lowestOne2(long long n) {
long long count = 0;
long long temp = n;
while(temp){
temp &= (temp - 1);
count++;
}
return n - count;
}