求n!最后一位非零数

引子:求n!末尾0的个数

n!末尾的0来源只有2,5两个质数相乘.所以只需要考察n!中包含多少个2和多少个5.然后取其较小值即为所求.即ans=min(cnt(2),cnt(5)).而转念一想,cnt(2)怎么可能比cnt(5)少呢?所以最终答案改为ans=cnt(5).
那么问题来了,如何求n!中5的个数呢?cnt(5)=n/5+n/25+n/125......+n/5^log5(n).式中一切浮点运算皆向下取整.

原题

以17为例,把1,2,3,...n分成5个一份.

1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17

每行的最后一个都包含一个5,而这个5最终只会变成0,所以真正有用的是这个数除以5.于是上面数阵等价于

1 2 3 4 1
6 7 8 9 2
1 2 3 4 3
6 7 

这样一来规律太明显了.假设这个数阵有m整行,也就是n/5的下界,最后剩余k个,也就是n%5.

1*2*3*4=24
6*7*8*9=3024
最后一位都是4,也就是每行相乘(除去每行最后一个数)最后一位都是4,在算上每行最后略去的5,从4中拿出一个2来给5,于是每行最后变成2.
于是答案呼之欲出了.对于前面的整行m行,结果为
2^m*m!
最后再把最后一行的几个数乘上去就可以了.

这个结论还不够简明,表达上不够清晰.
附上一份林教主的代码

#include<stdio.h>
int a[] = {1, 2, 4, 3};//2^k % 5
int b[] = {1, 1, 2, 6, 4};//n! % 5
int solve(int n){//求n!的最右非零数 % 5
    if(n < 5)
        return b[n];
    int t = n%5, k = n/5;
    return a[k%4]*solve(t)%5*solve(k)%5;
}
int main(){
    int ans = solve(2016);
    if(ans&1) ans =(ans+5)%10;
    printf("%d\n", ans);
    return 0;
} 

留个课后思考题,n!的倒数第二位非0数是几?

上一篇:PHP 高级编程(1/5) - 编码规范及文档编写


下一篇:复合文档的二进制存储格式研究[ole存储结构](word,xls,ppt...)[转]