[编程之美] PSet2.2 不要被阶乘吓倒

来源:互联网 发布:淘宝女装退货率多少 编辑:程序博客网 时间:2024/06/07 21:47


问题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的时候贡献了两个5int 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;


 



0 0
原创粉丝点击