【编程之美】不要被阶乘吓到

阶乘(Factorial)是个很有意思的函数,但是不少人都比较怕它,我们来看看两个与阶乘相关的问题。

1) 给定一个整数N,那么N的阶乘N!末尾有多少个0呢?例如:N=10,N!=3628800,N!的末尾有两个0。

2) 求N!的二进制表示中最低位1的位置。

【分析】

对于问题1有些人碰到这样的题目会想:是不是要完整计算出N!的值?如果溢出怎么办?事实上,如果我们从"哪些数相乘能得到10"这个角度来考虑,问题就变得简单了。

首先考虑,如果N!=K*10M,且K不能被10整除,那么N!末尾有M个0。再考虑对N!进行质因数分解,N!=2X*3Y*5Z*...,由于10=2*5,所以M只跟X和Z有关,每一对2和5相乘可以得到一个10,于是M=min(X,Z)。不难看出,X大于等于Z,因为能被2整除的数出现的频率比能被5整除的数高得多,所以把公式简化为M=Z。

对于问题2,要求的是N!中二进制表示中最低位1的位置。例如:给定N=3,N!=6,那么N!的二进制表示(1010)的最低位1在第二位。

判断最后一个二进制位是否为0:若为0,则将此二进制数右移一位,即为商值;反之,若为1,则说明这个二进制数是奇数,无法被2整除。

所以,这个问题实际上等同于求N!含有质因数2的个数。即答案等于N!含有质因数2的个数加1。

1. 问题1的解法一

要计算Z,最直接的办法,就是计算i(i=1,2,3,...,N)的因式分解中5的指数,然后求和,代码如下:

 1 #include <iostream>
 2 using namespace std;
 3
 4 int numZero(int n)
 5 {
 6     int ret = 0;
 7     for (int i = 1; i <= n; i++)
 8     {
 9         int j = i;
10         while (j % 5 == 0)
11         {
12             ret++;
13             j /= 5;
14         }
15     }
16     return ret;
17 }
18
19 int main(int argc, char *argv[])
20 {
21     int N;
22     while (cin >> N)
23     {
24         cout << numZero(N) << endl;
25     }
26     return 0;
27 }

2. 问题1的解法二

公式:Z = [N/5] + [N/52] + [N/53] + ... (不用担心这会是一个无穷的运算,因为总存在一个K,使得5K>N,[N/5K] = 0。)

公式中,[N/5]表示不大于5的数中5倍数贡献一个5,[N/52]表示不大于N的数中52的倍数再贡献一个5……参考代码如下:

 1 #include <iostream>
 2 using namespace std;
 3
 4 int numZero(int n)
 5 {
 6     int ret = 0;
 7     while (n)
 8     {
 9         ret += n / 5;
10         n /= 5;
11     }
12     return ret;
13 }
14
15 int main(int argc, char *argv[])
16 {
17     int N;
18     while (cin >> N)
19     {
20         cout << numZero(N) << endl;
21     }
22     return 0;
23 }

3. 问题2的解法一

由于N!中含有质因数2的个数,等于[N/2] + [N/4] + [N/8] + [N/16] + …… 则参考代码如下:

 1 #include <iostream>
 2 using namespace std;
 3
 4 int lowestOne(int n)
 5 {
 6     int ret = 1;
 7     while (n)
 8     {
 9         n >>= 1;
10         ret += n;
11     }
12     return ret;
13 }
14
15 int main(int argc, char *argv[])
16 {
17     int N;
18     while (cin >> N)
19     {
20         cout << lowestOne(N) << endl;
21     }
22     return 0;
23 }

4. 问题2的解法二

N!含有质因数2的个数,还等于N减去N的二进制表示中1的数目。我们还可以通过这个规律来求解,代码略。

相关题目

给定整数n,判断它是否为2的方幂。

【解答】参考答案如下:

1 n > 0 && (n & (n-1) == 0)

时间: 2024-11-04 09:14:23

【编程之美】不要被阶乘吓到的相关文章

编程之美----不要被阶乘吓到

任一个正整数都能分解成质数的连乘,因此求N!末尾有多少个0,等于质因数分解之后5的个数,而求5的个数可以用如下代码实现: ret =0; while(N) { ret += N/5; N/=5; } 5的倍数贡献一个5,5的平方的倍数再贡献一个5,如此继续下去.... 问题二:求N!的二进制表示中最低位1的位置,等价于求N! 含有质因数2的个数加1,因为如果将N!表示成2的多少次幂乘以一个数,那么就可以表示成2的多少次幂+2的更多的次幂.于是将上面的代码稍微修改就可以用来求此问题,另外N!含有质

编程之美-分层遍历二叉树

问题:给定一个二叉树,要求按分层遍历该二叉树,即从上到下按层次访问该二叉树(每一层将单独输出一行),每一层要求访问的顺序为从左到右,并将节点依次编号.那么分层遍历如图的二叉树,正确的输出应该为: <span style="font-size:14px;">1 2 3 4 5 6 7 8</span> 书中还给出了问题2:打印二叉树中的某层次的节点(从左到右),其中根结点为第0层,成功返回true,失败返回false 分析与解法 关于二叉树的问题,由于其本身固有的

读书问题之《编程之美》 -----12061161 赵梓皓

我阅读的书是<编程之美> 刚开始的时候阅读序,就觉得控制cpu利用率这个问题很好玩,所以重点看了这部分和解决办法,问题也都大部分是这部分的.那么问题就来了(挖掘机技术xxx?中国山东找蓝翔) 咳咳,问题在下面: 1.关于问题的提出.(也是一点点建议) 本书的主要内容是告诉读者如何思考问题和解决问题.但是提出问题也是很重要的,正如爱因斯坦所说“提出一个问题往往比解决一个问题更重要”,很多面试题(比如井盖为啥是圆的)我觉得正常人很少会想到.所以,这个问题是怎么想出来的...我很好奇.也希望作者能够

《编程之美》3.6判断链表是否相交之扩展:链表找环方法证明

先看看原题:<编程之美>3.6编程判断两个链表是否相交,原题假设两个链表不带环. 为了防止剧透使得没看过原题目的读者丧失思考的乐趣,我把最好的解法隐藏起来.由于这个问题本身的解答并不是本文的重点,扩展问题也采用这种形式呈现. 注:位于(*)符号之间的文字出自于:http://blog.csdn.net/v_july_v/article/details/6447013,作者v_JULY_v. 用指针p1.p2分别指向两个链表头,不断后移:最后到达各自表尾时,若p1==p2,那么两个链表必相交 用

编程之美之买票找零

题目:假设有2N个人在排队买票,其中有N个人手持50元的钞票,另外有N个人手持100元的钞票,假设开始售票时,售票处没有零钱,问这2N个人有多少种排队方式,不至使售票处出现找不开钱的局面? 分析:队伍的序号标为0,1,...,2n-1,并把50元看作左括号,100元看作右括号,合法序列即括号能完成配对的序列.对于一个合法的序列,第0个一定是左括号,它必然与某个右括号配对,记其位置为k.那么从1到k-1.k+1到2n-1也分别是两个合法序列.那么,k必然是奇数(1到k-1一共有偶数个),设k=2i

【编程之美】java实现重建二叉树

package com.cn.binarytree.utils; /** * @author 刘利娟 [email protected] * @version 创建时间:2014年7月20日 下午2:03:30 类说明: */ class Node { Node left; Node right; char chValue; Node(char chValue) { left = null; right = null; this.chValue = chValue; } } public cla

编程之美之字符串移位包含问题

[题目] 给定两个字符串s1和s2,要求判断s2是否能够被通过s1做循环移位(rotate)得到的字符串包含.例如,S1=AABCD和s2=CDAA,返回true:给定s1=ABCD和s2=ACBD,返回false. [分析] [思路一] 从题目中可以看出,我们可以使用最直接的方法对S1进行循环移动,再进行字符串包含的判断,从而遍历其所有的可能性. 字符串循环移动,时间复杂度为O(n),字符串包含判断,采用普通的方法,时间复杂度为O(n*m),总体复杂度为O(n*n*m). 字符串包含判断,若采

读《编程之美》读后感

读完<编程之美>后,我觉得这并不是简简单单的一本有关于编程的书,本书强调的不仅仅是程序或者考题本身,而是思维.这就和小学时看到数学奥林匹克竞赛的题一样,重要的不是套路或者定式,而在于独立思考时被自己激活的无数脑细胞.对于程序,也是一样,思维,才是程序的精髓所在.正如作者所言,书中展现的题目和分析,犹如海滩上美丽的石子和漂亮的贝壳那样,反映出造化之美,编程之美.该书的目的不仅在于揭开微软面试的神秘面纱,更是鼓励更多的编程爱好者从中收获自己的想法,开阔自己的眼界.作者更希望让面试者和被面试者都能够

求二进制数中1的个数(编程之美)

求二进制数中1的个数 继京东618店庆时买的<编程之美>这本书,翻了翻,发现里面的题还是挺有意思的,看起来我们觉得很简单的题目,解法却有很多很多种,真是一个比一个巧妙,于是,决定记录一下. 书中的题目如下 对于一个字节(8bit)的无符号数,求其二进制表示中"1"的个数,要求算法的执行效率尽可能高. 就像书中给我们说的一样,我们一般人可能想到的解决方法如下 int countOne(int n){ int count=0; while(n){ if(n%2==1){ cou

求子数组之和的最大值——编程之美 2.14 扩展问题 正确实现

使用动态规划求最大子数字和: s[i]表示data[i~n-1]以元素i开始的最大子数组和,a[i]表示data[i~n-1]中的最大子数组和 : s[i]=max(s[i+1]+data[i], data[i]); a[i]=max(a[i+1], s[i]); 由于数组s,a递推的时候,都只用到数组的前一个变量,所以可以用滚动数组节省空间. 扩展问题: 1) 如果数组首尾相连,即允许找到一组数字(A[i],···,A[n-1], A[0],···, A[j]),请使其和最大,怎么办?(书中答