N!这个题目在算法竞赛入门经典 出现过,过程相当于模拟手算的过程(我的大数乘法也是这样的)。假使我们的现在知道(n-1)! ,那么n!=(n-1)!*n.这个也是显而易见的。
但是n!的结果十分的大,10000!差不多需要45000位数字。所以我们不能简单的使用一个基础类型去进行存储,所以我们想到了另一个东西--------数组,对,没错就是数组,我们可以模拟我们手算的方式来进行计算。
假设我们要求解7!,已知6!,手算过程如下
6!=720.
7 2 0
* 7
--------------------------------
0
1 4
4 9
------------------------------------
5 0 4 0
这个过程相当简单,我们也就是拿7和6!的每一位相乘,然后按照位置加下来就可以了。当然写程序一般我们不会那么做,我们先将乘数与被乘数的第i位相乘得到的结果和上一次的进位结果加起来与10取模(既是求得当前位),放在存储结果的数组的第i位中,记录这次运算的进位(结果除以10就是进位结果),这样循环即可。
result[0]=1;//result是存储结果的数组
//我们存放的顺序是逆序的,以便于我们进位,例如6!=720,你们result存放的是 result[0]=0,result[1]=2,result[2]=7;
for(int i=2;i<=n;i++)
{
int t=0;//进位结果,初始化进位为0
for(int j=0;j<MAXN;j++)
{
int temp=result[j]*i+t;//被乘数的第i位与乘数相乘加上进位的结果
result[j]=temp%10;//实际上存储的第i位的大小
t=temp/10000;//取得这次运算的进位结果
}
}
//输出的时候逆序输出即可,记得处理前导零
这样做是没有问题的。但是面对hdu1042需要求10000!这么搞?我一开始使用上面的方法做,果断的TLE(超时),看了别人的一些资料后发现,还可以对上面的代码进行改进。我们可以观察,我们的result数组只存一位,那如果我们的result存的不是一位而是几位是不是会使我们的计算次数变少?怎么做?其实也就是简简单单改一下代码中mod的数和除的数。
result[0]=1;
for(int i=2;i<=n;i++)
{
int t=0;
int k=0;
for(int j=0;j<MAXN;j++)
{
int temp=result[j]*i+t;
result[j]=temp%10000;//改了这儿
t=temp/10000;//改了这儿
}
}
我们这样做为什么是正确的?其实我们也就是把一个数分成了几块来运算而已 ,第一段代码我们使用一块一位,现在我们是一块4位进行计算,进位也是一样。其实是进制的转换,我们从10进制的计算到了万进制的转换。
Q:上面提到的是4位一块,那能不能在提高欸例如6位7位?
A:可以的,分几块基于基础的数据类型,假设你使用int定义一个块,那么必须保证两个块的最大值相乘能够被一个int装起来,不会出现溢出。所以int可以装5位,如果6位和6位会超,可以选用64位整形存储。位数可达9位。
注意:注意多位存储的时候注意0的输出
贴下我的代码:
#include <iostream>
#include <stdio.h>
#include <cstring>
using namespace std;
const int MAXN=20000;
int result[MAXN];
void fac(int n)
{
memset(result,0,sizeof(result));
result[0]=1;
for(int i=2;i<=n;i++)
{
int t=0;
int k=0;
for(int j=0;j<MAXN;j++)
{
int temp=result[j]*i+t;
result[j]=temp%10000;
t=temp/10000;
}
}
int i=MAXN-1;
for(;!result[i];i--);
cout<<result[i];
for(i-=1;i>=0;i--)
{
if(result[i]<10){
cout<<"000";
}
else if(result[i]<100){
cout<<"00";
}
else if(result[i]<1000){
cout<<"0";
}
cout<<result[i];
}
cout<<endl;
}
int main()
{
int n;
while(cin>>n)
{
fac(n);
}
return 0;
}
已经ac。
如果我的这篇文中出现什么问题,欢迎批评指正。