不要被阶乘吓到

编程之美有一道关于阶乘的题目:
1给定一个整数N,那么N的阶乘等于N!,末尾有多少个0呢,例如N=10,N!=3628800,N!的末尾有两个0
2求N!的二进制表示中最低位为1的位置。
阶乘定义:

n!={1n(n1)!n=0n>0nN

作者对于问题一的分析:对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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值