问题1:给定一个整数N,那么N的阶乘N!末尾有多少个0呢?例如:N=10,N!=3 628 800,N!的末尾有两个0。
问题2:求N!的二进制表示中最低位1的位置。
问题一思路:
解法一:给定一个整数N,那么N的阶乘N!末尾有多少个0的问题可以转换为N!乘式中可以分解出多少个5的问题.因为5和其前面的任何一个偶数相乘都会产生0,所以只需求出在由1到N的数中共可以分解出多少个5.例如25!,可以分解出5(1×5)、10(2×5),15(3×5),20(4×5)25(5×5),共可以分解出6个5,所以25!末尾有6个0;
由此可以推知15!最后有3个0…这事实上也用到了2出现的频次总是高于5出现的频次,所以可以不用管2这个质因数。(最早出现5是在5!,此时2出现频次为3,远高于5出现的频次1,随着N的增大,N!中2频次与5频次差距越来越大。)
那么如何得到N!因式分解的5的个数呢?
//求解阶乘结果中末尾0的个数
//考虑求解从1-N之间的数,能被5整除的次数
int findZeroNum(int N)
{
int temp = 0;
int num = 0;
for(int i=1 ; i<=N ; i++){
temp = i;
while(temp % 5 == 0){
num++;
temp = temp/5;
}
}
return num;
}
解法二:考虑区间贡献,1-5之间贡献了一个5;6-10之间贡献了一个5····也就是每隔5贡献一个5因子,每隔5^2贡献一个5^2因子···,因此原问题转化为按照N以5的幂次划分区间,能够得到多少个区间的问题。
//每隔5^i贡献1个5^i因子如0-5 5-10 10-15···都贡献了一个5,而到25的时候贡献了两个5
int findZeroNum(int N)
{
int num = 0;
while(N){
num+=N/5;
N = N/5;
}
}
问题二思路:
解法一:问题一解出的是十进制表示的情况,现在由于是二进制表示N!,最低位1表明后面位全部为0。考虑乘以一个2,二进制数左移一位,因此可以考虑2的质因数个数即可求得结果。
//如果要求N!中2的质因数个数
int findOnePos(int N)
{
int sum = 0;
while(N){
sum += N>>1;
N = N>>1;
}
return sum+1;
}
解法二:N!中2的质因数个数还等于N-N中二进制表示1的数目,这个规律怎么来的呢?
试看N=11011(二进制表示),那么N!有多少个2质因数呢?由解法一的分析可以得到:
sum=11011>>1 + 11011>>2 + 11011>>3 + 11011>>4 =1101 + 110 + 11 + 1,对该式进行拆分可以得到
sum=(1000+100+1) + (100+10) + (10+1) + 1
=(1000+100+10+1) + (100+10+1) + 1
=1111 + 111 + 1
=(10000-1) + (1000-1) + (10-1) + (1-1)
=11011-(N的二进制表示中1的个数)
于是解法二代码如下:
int numOfOne(int i)//i二进制表示中1的个数
{
int count = 0;
while(i){
i &=(i-1);
count++;
}
return count;
}
int findOnePos(int N)
{
return N-numOfOne(N);
}
相关题目:
给定整数n,判断它是否是n的方幂
if(n>0 && (n&(n-1)==0 )) return true;