求n!的最高位数字

CSDN上某网友,提出以下问题:

Time Limit:1000MSMemory Limit:30000KB


Description

求n的阶乘的最高位数。

例如:5! = 120,所以最高位为1, 10!=3628800,所以最高位为3

 

Input

每个数据包含一行,每行有一个整数N(0<=N<=10000000)

 

Output

对于每个测试数据,输出n!的最高位数字

 

Sample Input

5

10

 

Sample Output

1

3

 

很显然,不能先计算出阶乘,然后再去阶乘的最高位数字,因为当n(=13)很小的时候,n!在32-bit的机器上就会溢出,即使在64-bit的机器上,要溢出也是很容易的事情(当n = 21时)。

 

第一种方法:

很自然地,想到Sterling公式:

, 也就是说当n比较大的时候有:,且n越大,结果越精确,两边同时取常用对数:

 ,则其整数部分加1,为n!的位数,比如,其整数部分为2,加1则等于3,这就说说明123是一个3位数。如果我们把整数部分去掉,那么就相当于125除以100,因为,因此可以得知:

,也就是说,对1.25向下取整,就可以很容易得到125这个数字的最高位数字1。

 

对于求n!的最高位数字,我们可以用同样的方法:

n!的最高位 = ,其中[]表示向下取整。

 

由于Sterling公式在n较小的时候存在一定的误差,所以,在写代码时候,对于较小的n的阶乘的最高位数字,可以采用直接输出的方式,具体代码如下:

#include<iostream>

#include<math.h>

using namespacestd;

 

const double PI =3.14159265358979;

const double E =2.71828459045;

int main(int argc,char *argv[])

{

    int n = 0;

    int firstnumber = 0;

    double log_n_factorial = 0.0;

    do

    {

        cin >> n;

   

        log_n_factorial = 0.5 * log(2 * PI *(double)n) / log(10.0) + (double)n * log((double)n / E) / log(10.0);

        log_n_factorial -=(int)log_n_factorial;

 

        firstnumber = exp(log_n_factorial *log(10.0));

 

        // 1. 用sterling公式计算较小数字的阶乘的时候,存在比较大的误差,随着数字的增大误差越来越小。

        // 2. 用switch...case...处理几个比较小的数字,其他的(见default)就均可以使用Sterling公式计算出来的结果了。

        switch(n)

        {

        case 0:

            cout << "1"<< endl;

            break;

        case 1:

            cout << "1"<< endl;

            break;

        case 2:

            cout << "2"<< endl;

            break;

        case 3:

            cout << "6"<< endl;

            break;

        case 7:

            cout << "5"<< endl;

            break;

        case 8:

            cout << "4"<< endl;

            break;

        default:

            cout << firstnumber <<endl;

        }

    }while(n != 0);    // n = 0时退出循环

 

    return 0;

}

 

第二种方法:

不使用Sterling公式,也可以求解。我们知道:

,两边也取常用对数:


其他的做法就和第一种方案类似了。很显然,用这种方法求得的n!的对数是精确值,而不是用Sterling公式算出来的近似值,因此不需要像第一种方法那样在计算较小的n的阶乘时,直接返回结果。

 

代码如下:

#include<iostream>

#include<math.h>

using namespacestd;

 

int main(int argc,char *argv[])

{

    int n = 0, firstnumber = 0;

    double log_n_factorial = 0.0;

         do

         {

                   cin >> n;

   

                   for(int i = 1; i <= n;++i)

                   {

                            log_n_factorial +=log((double)i) / log(10.0);

                   }

 

                   log_n_factorial -=(int)log_n_factorial;  

 

                   firstnumber =exp(log_n_factorial * log(10.0));

 

                   cout << firstnumber<< endl;

 

                   log_n_factorial = 0.0;

 

         }while(n != 0);    // n = 0时退出循环

 

    return 0;

}

 

结论分析:

1.       第二种方法的计算性能尽管也很高,但第一种的计算性能要比第二种高很多,数字越大的时候越是如此。第一种算法的复杂度是一个常量,第二种算法的复杂度是线性的;

2.       第一种算法的结果是近似值,第二种算法的结果是相对精确值(仅受限于计算机的精度)

3.       由于C语言中没有求常用对数的函数,因此用到了换底公式:

4.      推而广之,如果要计算n!的前两位数字,那么仅需要将

          log_n_factorial -= (int)log_n_factorial;这行代码改为:

          log_n_factorial -= ((int)log_n_factorial - 1); 即可,当然首先要确定n!阶乘的位数至少要有两项。

          如果要计算n!的前三、四…位数字,所用方法与上面描述的方法类似。

 


  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值