Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 14440 | Accepted: 4530 |
Description
Input
Output
Sample Input
2 10 20
Sample Output
7 19
Source
题目要求n!有多少位,由于n最大可以到10^7,直接计算根本没有合适的东西存储结果,使用以下公式:
n!=n*(n-1)*(n-2)*…..*1
N的位数=[lg(N)]+1;
所以:n!的位数=[lg(n*(n-1)*(n-2)*…..*1)]+1=[lgn+lg(n-1)+lg[n-2]+….+lg1]+1;
如果对每个数字都重新计算,是超时的,因此,可以先对数字进行排序,然后从小的开始计算,对大的数,可以直接接着前面小的数来计算,比如:10和20,先计算10,那么log10(10)+......+log10(1)就都计算出来了,那么对20来说,只需要用10的结果加上log(20)+......+log(11)即可,类似动态规划中的备忘录。
其中,v是进行排序的结果;old保存的是初始的数据顺序,用来输出时使用;map用来保存结果。
#include<iostream>
#include<math.h>
#include<map>
#include<algorithm>
#include<vector>
using namespace std;
vector<double> old;
vector<double> v;
map<double, double>m;
int solve(vector<double> &v ,map<double, double> &m, int i)
{
double pre_value=1;
double head=1;
if(i>0)
{
head=v[i-1]+1;
pre_value=m[v[i-1]];
}
double sum;
sum=pre_value;
for(double j=head;j<=v[i];j++)
{
sum += log10(j);
}
m[v[i]]=sum;
return 0;
}
int main()
{
int n;
cin>>n;
while(n--)
{
double num;
cin>>num;
v.push_back(num);
old.push_back(num);
}
sort(v.begin(),v.end()); //排序
for(int i=0;i<v.size();i++)
{
solve(v,m,i);
}
for(int i=0;i<old.size();i++)
{
double sum=m[old[i]];
cout<<(int)sum<<endl;
}
system("pause");
return 0;
}
还可以使用Stirling公式
Stirling公式的意义在于:当n足够大之后n!计算起来十分困难,虽然有很多关于n!的不等式,但并不能很好的对阶乘结果进行估计,尤其是n很大之后,误差将会非常大.但利用Stirling公式可以将阶乘转化成幂函数,使得阶乘的结果得以更好的估计.而且n越大,估计得就越准确。
补充:
用Stirling公式计算n!结果的位数时,可以两边取对数,得:
log10(n!) = log10(2*PI*n)/2+n*log10(n/E);
故n!的位数为 log10(2*PI*n)/2+n*log10(n/E)+1(注意:当n=1时,算得的结果为0)
n的位数为[lg10(n)]+1
n!的位数为[lg10(n*(n-1)*(n-2)*…..*1)]+1=[lg10(n)+lg10(n-1)+lg10(n-2)+….+lg10(1)]+1
高德纳的《计算机程序设计艺术》中,
n! = sqrt(2*π*n) * ((n/e)^n) * (1 + 1/(12*n) + 1/(288*n*n) + O(1/n^3))
代码如下:
#include<iostream>
#include<cmath>
int main( void )
{
int m , n ;
double pi = 3.1415926;
double e = 2.71828182;
double r;
freopen("1423.txt" , "r" , stdin );
std::cin >> n;
while( n -- )
{
r = 0.0;
std::cin >> m;
if( m > 3 )
r = log10( 2*pi*m )/2 + m*log10(m/e);
m = (int)r + 1;
std::cout << m << std::endl;
}
return 0;
}