编程之美读书笔记之阶乘

题目:

        看到题目,很容易冒出一种想法:计算出N!的结果,并对结果除以10取余。直到余数不为0为止。

int Factorial(int n)
{
	int a = 0, sum = n;
	while(--n)
	{
		sum *= n;
	}
	while(sum % 10 == 0)
	{
		sum /= 10;
		a ++;
	}
	return a;
}


但是仔细想想这里会牵涉到两个问题:

  • 1.效率问题,当N很大的时候,执行效率不高。
  • 2.溢出问题,毕竟计算机操作数的容量是有限的。大一个数达到足以溢出这个容器,该如何处理?


        反思一下,在这里真的有必要算出N!的结果吗?如果N!能被10整除,那么N! = K *10的M次方(K不能被10整除)。如果我们把这个问题转化为寻找哪些数相乘得到10。是不是简单很多。既然是阶乘,那么当N>=3的时候,其结果一定是个合数,我们对N!进行质因数分解 N! = 2的x次方 * 3的y次方 * 5的z次方 * 7的n次方....依次类推。每一对2和5相乘得到10。所以这里M只与x和z相关。并且M=MIN(Z,X),由于在阶乘中能2的幂的个数总是比5的幂的个数多。所以X>=Z。那么要得到M,只需计算出z即可。

解法1:

int Factorial1(int n)
{
	int i, j = 0, sum = 0;
	for (i = 1; i < = n; i++)
	{
		j = i;
		while(j % 5 == 0)
		{
			sum++;
			j = j / 5;
		}

	}
}


        解法1把求末尾0的个数转化为求质因数分解中5的指数。那么我们有不有更好的方法来计算5的指数。我们以25!为例:

25!= 25 * 24 * 23 *... * 20 * 19 * ... * 15 *... * 10 * ... * 5 * ... * 1 这里面出现了5个5的倍数,他们都贡献了一个5,但是25特殊一点,因为它是5的平方,所以它还额外贡献了一个5。所以25!中5的出现次数为6,Z = 6,说明末尾有6个0。

        归纳一下:质因数分解中5的指数Z = [N/5] + [N/5的平方] + [N/5的3次方] +... + [N/5的K次方]。[N/5]表示不大于N的数中,每个5的倍数贡献的一个5,[N/5的平方]表示不大于N的数中,每个5的平方的倍数再额外贡献一个5,依次类推。得出代码如下。

解法2:

int Factorial2(int n)
{
	int sum;
	while(n)
	{
		sum += n / 5;//依次寻找5的i次方贡献的5的个数,
		n /= 5;
	}
	return sum;
}

        以上解法2同样可以应用于问题2,求N!的2进制表示中最低位1的位置。只不过由贡献5的个数到贡献2的个数转换一下就好了。

int Factorial3(int n)
{
	int sum;
	while(n)
	{
		n = n >> 1;
		sum += n;
	}
	return sum;
}

拓展问题:

        


        我们来看看2的方幂的2进制数表现形式,2的5次方:00100000 ,2的4次方:00010000 不难看出方幂的2进制表示有且只有一个一。在上篇求2进制中1的个数 。提到了如何证明2进制表示中有且仅有一个1。         


bool Judgement(int n)
{
	bool result;
	if (n > 0 && (n & (n-1)) == 0)
	{
		return true;
	}
	return false;
}





        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值