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