关于这道题,见过两种问法:
- 给定参数n(n为正整数),请计算n的阶乘n!末尾所含“0”的个数。
- (n!%(10^k)) == 0。已知n,求能使上式成立的K的最大值。
问题分析:
显然,对于阶乘这个大数,我们不可能将其结果计算出来,在统计其末尾所含有的“0”的个数。所以必须从其数字特征进行分析。
首先考虑一般的情形。对于任意一个正整数,若对其进行因式分解,那么其末尾的“0”必可以分解为2*5。这里每一个“0”必然和一个“2”与“5”对应。这里是阶乘,含有2的分解比含有5的分解多很多。所以这道题的是问:正整数n因式分解中“5”的个数。例如6的阶乘:
6! = 6 * 5 * 4 * 3 * 2 * 1
分解后
6! = (3*2)*5*(2*2)*3*2*1 = 5*3*3*2*2*2*2*1
从上面的例子可以看出,分解之后6的阶乘含有一个5,四个2,2的个数5多很多。
对于n<5,因为不含分解因子“5”,所以末尾没有0。
但是对于n>=5的情况呢?我们可以把含有分解因子为“5”的给提出来,把n!变为
n! = [5m*5(m-1)*....*10*5]*a,a是一个不含因子“5”的整数,其中n = 5m + r (0<= r <= 4)
例如26的阶乘:
26! = [25*20*15*10*5]*26*24*23*22*21*19*......*1
26! = [(5*5)*(5*4)*(5*3)*(5*2)*5]*26*24*23*22*21*19*......*1
26! = [(5*5)*(5*4)*(5*3)*(5*2)*5]*a(a代表后面所有数相乘)
通过上面的分解我们很容易看出26的阶乘一共有6个分解因子“5”。但是特别大的正整数呢?我们可不能手写出来然后数个数,所以我们还要进一步分析。
当 m>=5 时,m本身也可以分解出“5”,所以我们需要在原有的基础上再加m分解后的“5”的个数,现在n的阶乘可以写为:
n! = 5^m * m! * a
推导过程是:
n! = [5m*5(m-1)*....*10*5]*a = [5*5*5....*5*m*m-1*m-2*.....*1]*a
整合之后得到
n! = 5^m * m! * a
通过上面的推导,我们可以得出最终的公式,令f(x)表示正整数x末尾所含有的“0”的个数, g(x)表示正整数x的因式分解中因子“5”的个数:
f(n!) = g(n!) = g(5^k * k! * a) = k + g(k!) = k + f(k!)
所以,最终的计算公式为:
当0 < n < 5时,f(n!) = 0;
当n >= 5时,f(n!) = k + f(k!), 其中 k = n / 5(取整)
到这里代码就写了,这不是递归调用嘛,所以代码写为:
int func(const int x)
{
if ( x < 5)
{
return 0;
}
int y = x / 5;
return y + func(y);
}
但是递归调用涉及到函数的调用开销,效率并不高,我们既然有公式了,可以直接带入公式:
int func2(int x)
{
int y = 0;
while (x > 4)
{
x = x / 5;
y += x;
}
return y;
}
还有一种写法,我觉着比较难理解:
int func3(const int x)
{
int y = 0;
for (int i = 5; i <= x; i *= 5)
{
y += x / i;
}
return y;
}
感谢大家,我是假装很努力的YoungYangD(小羊)。
参考资料:
https://www.cnblogs.com/hutonm/p/5624996.html