我的老师给我们布置的R语言作业中,有一道题目是这样的↓
要解决该问题,首先应当了解这一问题的数学算法。对于此题的数学解法,我借鉴了如下两位前辈的解答。
1.100的阶乘末尾有几个0
2.阶乘N!的末尾有多少个0
如何计算N!末尾有多少个0,其实可以简单化为求N!中可以提取出多少个5。
思路如下:
首先,如果提取出一个因子“2”和一个因子“5”,就可以得到一个“10”,末尾就会多一个0,所以原问题可化为,N!中可以提取出多少个2和5的组合
其次,以100的阶乘末尾有几个0这个问题为例,12345,出现一次5,这里就出现了两次2;12345678910,十个数,出现两次5,五次2;我们可以发现,2出现的次数是远多于5的,所以可以忽略2出现的次数,只专注于数“5”这个因子出现的次数。
那么如何计算“5”这个因子出现的次数呢?
这里再次以100的阶乘末尾有几个0这个问题为例,
以100的阶乘为例,在100内,因子是5的数有5, 10, 15, 20, 25…
总共有20个。也就是说,100的阶乘当中,以上20个数可以提取出“5”这个因子。但是25, 50, 75,100都包含了2个5作为因子(25=55, 50=25*5),对于这些数,还能再提取出一个“5”因子,所以5因子总共有100/5 +
100/(5^2) + 100/(5^3) + … = 24 (必须是整数)
这句话是这么理解的,计算100!也就是从1乘到100,在1~100这些数字中,5, 10, 15, 20, 25…都含有因子“5”,总共有100/5=20个“5”;并且在这20个数字中,25, 50, 75,100,含有两个“5”因子,他们都是25的倍数,所以这四个含有两个“5”的数还能为100!再带来4个零。
通过上述推理分析,我们就知道了计算“5”这个因子出现的次数的公式了:100/5 +
100/(5^2) + 100/(5^3) + … = 24 (必须是整数)
可以类比推理出N!含有“5”因子个数的公式:N/5+N/5^2 +…+N/5^i
知道了这个规律以后,我们就有思路了:
设一个变量i来判定这个N能不能除得掉5^i
设一个变量来记录含有的“5”因子个数,直接用我们上面得到的公式求
把上述内容封装成一个函数,大功告成!
以上思路本人借鉴了“长弓Smile”前辈的Java代码阶乘N!的末尾有多少个0,感谢这位前辈。我认可他的思路,但是不认可他的算法,他在做i值判断的时候用的代码是
while(N/(Math.pow(5, i))>0){
count+=N/(Math.pow(5, i));
i++;
}
我们会发现,其实这时候不管i值怎么增大,分母变成一个多大的数,都不会停止计算,因为永远是正数,所以这一步判断是有误的。
所以我在设计R语言代码的时候,将其改为
while(n/(5^i) >= 1){
count = count + floor(n/(5^i))
i = i+1
}
用java的代码表示就是
while(N/(Math.pow(5, i))>=1){
count+=N/(Math.pow(5, i));
i++;
}
该段R语言代码有两个需要说明的地方
1.必须要大于等于1而不是0.
如果结果是一个不大于1的数字,说明i不能再大了,从而得到正确的倍数。
2.必须要用floor函数或与floor函数作用相同的函数框定n/(5^i)
否则结果会得到一个小数,所以需要floor函数向下取整。这个很好理解,就拿前面的100为例,100/(5^3)显然是一个小数,这个小数并不能表示提出“5”这个因子,该部分是我们所不需要的。
(java代码不需要是因为作者定义其为整型int)
fac <- function(n){
i <- 1
count <- 0
while(n/(5^i) >= 1){
count = count + floor(n/(5^i))
#count = count + n/(5^i)
i = i+1
}
return(count)
}
fac(2700)
最后附上我用R语言解决问题“求任意整数阶层N!的尾数有多少个零?(以2700!为例) ”的代码,运行
fac(2700)
的结果为
673
可以在fac()的括号中自由更改数字。如果本人表述有误,或您认为该问题有更好改进方法,欢迎批评指正。