千万不要被阶乘吓倒

阶乘(Factorial)是个很有意思的函数,但是不少人都比较怕它,我们来看看两个与阶乘相关的问题:
1、 给定一个整数N,那么N的阶乘N!末尾有多少个0呢?例如:N=10,N!=3 628 800,N!的末尾有两个0。
2、求N!的二进制表示中最低位1的位置。
有些人碰到这样的题目会想:是不是要完整计算出N!的值?如果溢出怎么办?事实上,如果我们从"哪些数相乘能得到10"这个角度来考虑,问题就变得简单了。
首先考虑,如果N!= K×10^M,且K不能被10整除,那么N!末尾有M个0。再考虑对N!进行质因数分解,N!=(2^x)×(3^y)×(5^z)…,由于10 = 2×5,所以M只跟X和Z相关,每一对2和5相乘可以得到一个10,于是M = min(X, Z)。不难看出X大于等于Z,因为能被2整除的数出现的频率比能被5整除的数高得多,所以把公式简化为M = Z。
根据上面的分析,只要计算出Z的值,就可以得到N!末尾0的个数。
【问题1的解法一】
要计算Z,最直接的方法,就是计算i(i =1, 2, …, N)的因式分解中5的指数,然后求和:


代码如下:

ret = 0;
for(i = 1; i <= N; i++)
{
 j = i;
 while(j % 5 ==0)
 {
  ret++;     //统计N的阶乘中那些能够被5整除的因子的个数
  j /= 5;
 }
}

【问题1的解法二】
公式:Z = [N/5] +[N/5^2] +[N/5^3] + …(不用担心这会是一个无穷的运算,因为总存在一个K,使得5^K > N,[N/5^K]=0。)
公式中,[N/5]表示不大于N的数中5的倍数贡献一个5,[N/5^2]表示不大于N的数中5^2的倍数再贡献一个5,……代码如下:


代码如下:

ret = 0;
while(N)
{
 ret += N / 5;
 N /= 5;
}

问题2要求的是N!的二进制表示中最低位1的位置。给定一个整数N,求N!二进制表示的最低位1在第几位?例如:给定N = 3,N!= 6,那么N!的二进制表示(1 010)的最低位1在第二位。
为了得到更好的解法,首先要对题目进行一下转化。
首先来看一下一个二进制数除以2的计算过程和结果是怎样的。
把一个二进制数除以2,实际过程如下:
判断最后一个二进制位是否为0,若为0,则将此二进制数右移一位,即为商值(为什么);反之,若为1,则说明这个二进制数是奇数,无法被2整除(这又是为什么)。
所以,这个问题实际上等同于求N!含有质因数2的个数+1。即答案等于N!含有质因数2的个数加1。 实际上N!都为偶数,因为质因数里面都有一个2,除了1以外,因为1的阶乘是1,是个奇数,其他数的阶乘都是偶数。。
【问题2的解法一】
由于N! 中含有质因数2的个数,等于 N/2 + N/4 + N/8 + N/16 + …[1],
根据上述分析,得到具体算法,如下所示:


代码如下:

/*
可以先求出N!中2的个数(因为每存在一个2,则在数的
最低位多1个0)。因此求1的最低位的位置即为N!中2的个数+1;
*/
int lowestOnePos(int n)
{
    int ret = 0;     //统计n!中含有质因数2的个数
    while(n)
    {
        n >>= 1;
        ret += n;
    }
    return ret+1;
}

【问题2的解法二】
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的个数

小结
任意一个长度为m的二进制数N可以表示为N = b[1] + b[2] * 2 + b[3] * 22 + … + b[m] * 2(m-1),其中b [ i ]表示此二进制数第i位上的数字(1或0)。所以,若最低位b[1]为1,则说明N为奇数;反之为偶数,将其除以2,即等于将整个二进制数向低位移一位。
相关题目
给定整数n,判断它是否为2的方幂(解答提示:n>0&&((n&(n-1))==0))。
--------------------------------------------------------------------------------
[1] 这个规律请读者自己证明(提示N/k,等于1, 2, 3, …, N中能被k整除的数的个数)。

(0)

相关推荐

  • ASP.NET递归法求阶乘解决思路

    前台: 复制代码 代码如下: <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox> !<asp:Button ID="Button1" runat="server" onclick="Button1_Click" Text="=" /> <asp:TextBox ID="Tex

  • C# 实现阶乘 (递归,非递归) 实现代码

    //Main: 复制代码 代码如下: using System;using System.Collections.Generic;using System.Linq;using System.Text; namespace Factorial{    class Program    {        static void Main(string[] args)        {            Function obj = new Function();            Cons

  • 求1000阶乘的结果末尾有多少个0

    素数是个科学计算中很重要的一个概念,素数也叫质数.素数就是最纯净的数,没有任何其它成分的数,其它的数都可以说是由素数相乘出来的. 所以,理解好素数,对于数学和程序来说,有重要的意义. 题目是:求1000!的结果末尾有多少个0 复制代码 代码如下: 1000! = 1×2×3×4×5×...×999×1000 复制代码 代码如下: public static void main(String[] args) { /*1000的阶乘已经是天文数字了,所以不可能计算出来,再看有多少个0*/ /* 解题

  • C++求阶乘的两种方法

    1.使用静态局部变量static静态局部变量在函数调用结束之后不消失而保留原值,即其占用的存储单元不释放,在下一次该函数调用时,该变量保留上一次函数调用结束时的值. 静态局部变量赋初值实在编译时进行的,即只赋初值一次,在程序运行时它已有初值. code: 复制代码 代码如下: #include<iostream>using namespace std;int fac(int n){ static int f=1; f=f*n; return f;}int main(){ int i; for(

  • 算阶乘的vbs小程序

    dim n,ans,i n=inputbox("n(0<=n<=170)=") ans=1 for i=1 to n ans=ans*i next msgbox(ans)

  • 从阶乘函数对比Javascript和C#的异同

    JavaScript代码块 复制代码 代码如下: <script type="text/javascript"> function factorial(num) { if (num <= 1) { return 1; } else { return num * arguments.callee(num - 1); } } var trueFactorial = factorial; factorial = function () { return 0; } alert

  • C语言 实现N阶乘的程序代码

    代码如下所示: 复制代码 代码如下: #include <stdio.h>#include <stdlib.h>#define N 10 //算N的阶乘int main(){       //数组   1位 1!    int ary[N] = {1, 1};    int i, j;    for (i = 2; i <= N; i++)    {        //各个下标的阶乘,第0位下标是位数,所以从第1位开始        for (j = 1; j <= a

  • c++大数阶乘的实现方法

    C++代码如下: #include <algorithm>#include <vector>#include <cstdio>using namespace std;typedef  unsigned int Type;enum{ BASE_DATA = 10000, MAX_NUM = 100000 , MAX_SIZE = MAX_NUM+1000};struct MulOpt {Type data1;MulOpt(Type x):data1(x){}inline

  • 用VBS精确计算100的阶乘的实现代码

    今天又无聊的逛了一下搜搜问问,找到这样一个问题: 有谁能提供vbs里20的阶乘的详细程序 下面有人给出了这样的答案: 复制代码 代码如下: function jx(x) j=1 for i=2 to x j=j*i next jx=j end function msgbox jx(20) 运行一下上面的程序,输出2.43290200817664E+18.笑而不语,再次证明了我之前的结论,在这种网站上回答问题的,大部分水平都不咋地. 其实之前在<用VBS精确计算2的100次方>我已经写过了VBS

  • java阶乘计算获得结果末尾0的个数代码实现

    看到题目后,分析了下, 10的阶乘就已经很大了.计算出来再得到这个末尾的0的个数,完全不现实,即使实现了也是很麻烦的. 后来想某个数的阶乘中乘积有5结尾的数字的时候就应该在结果的末尾产生一个0. 付诸实现,测试了几个, 没出错. 贴出来, 大家看看, 有问题了及时指教: 复制代码 代码如下: /**     * 求1000~10000之间的数n的阶层并计算所得的数n!末尾有多少个0?     */    public static void test2(int number){        i

随机推荐