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