求N!阶乘中结尾0的个数,或结果的二进制表示中最后一个1所在的位置

本文探讨了两个编程问题:1) 阶乘N!末尾0的个数,通过分析5的倍数及其幂次出现的次数来计算;2) 找出二进制表示中最后一个1的位置,与阶乘中0的个数问题类似,都是关于2的倍数出现频率的问题。通过类比和分析,给出了相应的程序解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题1:阶乘结尾0的个数

要想阶乘的结尾是0,那么这个0已经通过5的倍数乘以另一个数获得,例如:5,10,15,20,25...,同时可以注意到这样一个规律,5,10,15,20,30,35,40与某个数相乘后可以贡献1个0,而25,50,75,100...与某个数相乘可以贡献2个0,5^3, 2* 5^3, 3*5^3与某个数相乘时贡献了3个0,依次类推。同时我们注意到,在统计5,10,15,20,25...时已经统计了一次25,50,75, 100,5^3, 2* 5^3, 3* 5^3一次了,而在统计25、50、75...时也统计了一次5^3, 2* 5^3...一次了,这样最终0的个数就是计算5的倍数出现了多少次+ 25的倍数出现了多少次 + 5^3出现了多少次,知道5^x > N,据此分析程序如下,如NumberOfZero1所示,

#include <stdio.h>
int NumberOfZero(int var) {
  int num = 0;
  int j;
  for (int i = 1; i <= var; ++i) {
    j = i;
    while (j % 5 == 0) {
      num++;
      j /= 5;
    }
  }
  return num;
}
int NumberOfZero1(int var) {
  int num = 0;
  int base = 1;
  int i = 1;
  while (true) {
    base = base * 5;
    if (base > var) {
      break;
    }
    i = 1;
    while (base * i <= var) {
      num++;
      i++;
    }
  }
  return num;
}
int main(int argc, char** argv) {
  printf("method1: num = %d\n", NumberOfZero(14000));
  printf("method2: num = %d\n", NumberOfZero1(14000));
}

NumberOfZero是编程之美中给出了另一种求5、25、 5^3、个数的另一中求法,更加简洁一些。

问题2:二进制表示中最后一个1所在的位置

根据二进制数的性质,如果最后一位是1,那么该数为奇数,因为如果结尾为0,那么这个二进制数一定为偶数,因为结尾为0的数可以看作某个结尾为1的数,左移1位得到,即乘2后得到的数,因此一定是偶数,那么这个偶数再加1后,一定为奇数。

相似的分析,如果倒数第2位为1一定是2的倍数

如果倒数第3位是1一定是4的倍数

...

如果与问题1类比,如果N!有多少个2的倍数,相当于左移了多少位,相当于问题1的有多少个0,有多少个4的倍数,相当多少个左移2位,相当于问题1的贡献2个0...

同时注意到除了1!外,没有一个数的阶层是奇数,因为2以上的阶乘至少含有一个2这个因子。

因此问题2其实和问题1是相同的问题。

程序如下:

#include <stdio.h>
int PositionOf1(int var) {
  int pos = 0;
  while (var) {
    var>>=1;
    pos += var;
  }
  return pos;
}
int PositionOf1_1(int var) {
  int pos = 0;
  int base = 1;
  int i = 0;
  while (true) {
    base *=2;
    if (base > var) {
      break;
    }
    i = 1;
    while (base * i <= var) {
      pos++;
      i++;          
    }
  }
  return pos;
}
int main(int argc, char** argv) {
  printf("method1: num = %d\n", PositionOf1_1(14));
  printf("method2: num = %d\n", PositionOf1(14));
}

参考文献

编程之美p125 2.2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值