题目:给定一个整数N,请问N!这个数末尾有多少个0?
解决方案1:计算N!,并搜索其末尾,判断0的个数。N!可能过大,数会溢出,如果用该办法,需自己完成一个大数乘法的类,才会好用!
解决方案2:对N!进行质因数分解,可知N!=pow(2,x)*pow(3,y)*pow(5,z)...,能让末尾有零的必然是2*5。可以想见,2的个数远多于5的个数,即 x>z ,所以,末尾应该有z个0。
解决方案2的代码1:
public static int calZero(int a){
int ret=0;
for(int i=1;i<=a;++i){
if(i%5==0){
int j=i;
while(j%5==0){
ret++;
j=j/5;
}
}
}
return ret;
}
对代码1的分析:
事实上,while语句中就具备了if语句判断的功能,多写一个if语句是毫无意义。所以应修改为:
代码2:
public static int calZero(int a){
int ret=0;
for(int i=1;i<=a;++i){
int j=i;
while(j%5==0){
ret++;
j=j/5;
}
}
return ret;
}
对于这个问题,网上有更简短的代码:
代码3:
public static int calZero(int a){
int ret=0;
while(a>0){
ret+=a/5;
a/=5;
}
return ret;
}
对于代码3的分析:
从思想的角度来看,代码2和代码3都是同样的思考方式,即计算N!中质因数的个数。在代码3中,计算方法是 Z=[N/5]+[N/25]+[N/125]+... ,可理解为,[N/5]不大于N的数中,5的倍数贡献一个5,[N/25]表示不大于N的数中25的倍数再次贡献一个5,以此类推。如N=25时,5的倍数有5,10,15,20,25,其中5、10、15、20均贡献1个5,25贡献2个5,因为25=5*5!
============================================================
题目:给定整数N,求N!的二进制标识中最低位1的位置
解决方法1:依旧是求出N! 然后转换二进制,并最终得结果。缺点:复杂,需考虑数的溢出问题。
解决方法2:判断一个二进制数的最低位1的位置,可如下判断:检查当前是否为1,不是则将该二进制数右移一位。循环直至当前末位为1。所以,欲求N! 二进制表示中最低位1的位置,即可将N!的二进制表示右移,直至末位为1。由于右移在算术中是除2的意思,所以该问题的实质就是求N!中含质因数2的个数!最后的答案为N!中含质因数2的个数加1。
代码:
public static int loc(int N){
int ret=0;
while(N!=0){
N>>=1;
ret+=N;
}
return ret+1;
}
代码分析:
代码思想是计算N!中质因数为2的个数,即为 [N/2]+[N/4]+[N/8]+[N/16]+... 。
总结:
质因数的分解是解决有关数字的算法问题的一个思考方向,其中的思想还蛮巧妙。