编程之美有一道关于阶乘的题目:
1给定一个整数N,那么N的阶乘等于N!,末尾有多少个0呢,例如N=10,N!=3628800,N!的末尾有两个0
2求N!的二进制表示中最低位为1的位置。
阶乘定义:
作者对于问题一的分析:对N!进行质因数分解: N!=2X*3Y*5Z…,因为10=2*5,所以M与2和5的个数即X、Z有关。每一对2和5都可以得到10,故M=min(X,Z)。因为能被2整除的数出现的频率要比能被5整除的数出现的频率高,所以M=Z。
解法一:
直接计算因式分解中5的指数,然后求和.
代码实现:
#include <iostream>
using namespace std;
int findZero(int N)
{
int ret=0;
for(int i=1;i<=N;i++)
{
int j=i;
//计算每个阶乘相乘数字分解因式5的个数
while(j%5==0)
{
ret++;
j/=5;
}
}
return ret;
}
解法二:
作者根据[N/k]等于1,2,3 ,…,N中能被k整除的数的个数规律,得出下面公式:
Z=[N/5]+[N/
52
]+[N/
53
]+….(总存在一个K,使得
5k
>N,[N/
5k
]=0)
公式中,[N/5]表示不大于N的数中5的倍数贡献一个5,[N/
52
]表示不大于N的数中
52
的倍数再贡献一个5…。
代码实现:
#include <iostream>
using namespace std;
int findZero(int N)
{
int ret=0;
while(N)
{
//计算1,2,3...k能被当前5的指数倍数整除个数
ret+=N/5;
//累计5的指数
N/=5;
}
return ret;
}
对于问题二,确实不好想到那方面去,我们都知道在计算机二进制里,有一个规律,那就是如果一个数是偶数,那么该数的最后一个二进制必定是0,如果是奇数,那么该数的最后一个二进制必定是1,这里作者根据这样的规律给了两种解法。
解法一:
判断最后一个二进制位是否为0,若为0,则将此二进制数右移一位,即为商值;反之,若为1,则说明这个二进制数是奇数,无法被2整除。
所以,这个问题实际上等同于求N!含有质因数2的个数。即答案等于N!含有质因数2的个数加1。
代码实现:
#include <iostream>
using namespace std;
int lowestOne(int N)
{
int ret=0;
//统计被2整除的个数
while(N)
{
N>>=1;
ret+=N;
}
return ++ret;
}
解法二:
N!含有质因数2的个数,还等于N减去N的二进制表示中1的数目。我们还可以通过这个规律来求解。
下面对这个规律进行举例说明,假设 N = 11011,那么N!中含有质因数2的个数为 N/2 + N/4 + N/8 + N/16 + …
即: 1101 + 110 + 11 + 1
=(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的个数
代码实现:
#include <iostream>
using namespace std;
int lowestOne(int N)
{
int ret=0;
int temp=N;
//统计被2整除的个数
while(temp)
{
ret+=temp&1;
temp>>=1;
}
ret=N-ret;
return ++ret;
}