求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!的前三、四…位数字,所用方法与上面描述的方法类似。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值