阶乘计算

 基础练习 阶乘计算  
时间限制:1.0s   内存限制:512.0MB
问题描述
  输入一个正整数n,输出n!的值。
  其中n!=1*2*3*…*n
算法描述
  n!可能很大,而计算机能表示的整数范围有限,需要使用高精度计算的方法。使用一个数组A来表示一个大整数aA[0]表示a的个位,A[1]表示a的十位,依次类推。
  将a乘以一个整数k变为将数组A的每一个元素都乘以k,请注意处理相应的进位。
  首先将a设为1,然后乘2,乘3,当乘到n时,即得到了n!的值。
输入格式
  输入包含一个正整数nn<=1000。
输出格式
  输出n!的准确值。
样例输入
10
样例输出
3628800

#include “stdio.h”
#define N 5000 
int main(void)
{
int n, i, j, s, up, f[N] = {0};
scanf("%d", &n);
f[0] = 1;
for (i = 2; i <= n; i++)//外循环控制阶乘的数
{
up=0;
for (j=0; j < N; j++) //内循环用于得出前一个数的阶乘结果乘以当前数i得出的阶乘结果
{
s = f[j] * i + up;
f[j] = s % 10;
up = s / 10;
}
}
i=N-1;
while(f[i]==0)//此循环用来消掉计算过程中多赋的值“0”
{
i--;
}
for (; i >= 0; i--) printf("%d", f[i]) ;
printf("\n");
return 0;
}

若是感觉不是很好明白,可以改成以下代码,可能就会好明白一点了:

 

#include <stdio.h>

#define N 5000 // modify it to hold larger number

int main(void)

{

int n, i, j, s, up, f[N] = {0};

scanf("%d", &n);

f[0] = 1;

for (i = 2; i <= n; i++)//外循环控制阶乘的数

{

up=0;

for (j=0; j < N; j++) //内循环用于得出前一个数的阶乘结果乘以当前数i得出的阶乘结果

{

s = f[j] * i + up;

f[j] = s % 10;

up = s / 10;

}

}

for (i = N-1; f[i] == 0; i--) ;

for (; i >= 0; i--) printf("%d", f[i]) ;

printf("\n");

return 0;

}

 

 

当我们第一次接触这个代码的时候,你会不会感到很无奈呢?是不是不明白为什么这么做啊?

下面说一说我对这串代码的理解:

首先,要想要理解这串代码是怎么回事儿,最好的方法就是先找一个数带进去,找一张好的白纸,好好写一写,画一画,从0写到5。从更本质上来说,只把5带进去就行,因为

0!*1=1!;

1!*2=2!;

2!*3=3!;

3!*4=4!;

4!*5=5!;

但是3!=6<10;4!=24刚刚大于10,取5就更具有普遍性。

下面就讨论当n=5时的情况。

因为外循环是从i=2开始的所以我们也从i=2开始讨论。

 

内循环中对于for(j=0,up=0;j<n;j++)

一:i=2时

1.

j=0时:s=f[j]*i+up;----->s=f[0]*2+0=1*2+0=2;

f[j]=s%10;----->f[0]=2%10=2;

up=s/10;----->up=2/10=0;

2.

j=1时:s=f[j]*i+up;----->s=f[1]*2+0=0*2+0=0;

f[j]=s%10;----->f[1]=0%10=0;

up=s/10;----->up=0/10=0;

当j大于等于2时的情况和“2.”时的情况相同,此时得到的结果为:f[0]=2;f[1]=0;f[2]=0;.....

二:i=3时

1.j=0时:s=f[j]*i+up;----->s=f[0]*3+0=2*3+0=6;

f[j]=s%10;----->f[0]=6%10=6;

up=s/10;----->up=6/10=0;

2.j=1时:s=f[j]*i+up;----->s=f[1]*3+0=0*3+0=0;

f[j]=s%10;----->f[1]=0%10=0;

up=s/10;----->up=0/10=0;

当j大于等于2时的情况和“2.”时的情况相同,此时得到的结果为:f[0]=6;f[1]=0;f[2]=0;......

三:i=4时

1.

j=0时:s=f[j]*i+up;----->s=f[0]*4+0=6*4+0=24;

f[j]=s%10;----->f[0]=24%10=4;

up=s/10;----->up=24/10=2;

2.

j=1时:s=f[j]*i+up;----->s=f[1]*4+2=0*4+2=2;

f[j]=s%10;----->f[1]=2%10=2;

up=s/10;----->up=2/10=0;

3.

j=2时:s=f[j]*i+up;----->s=f[2]*4+0=0*4+0=0;

f[j]=s%10;

 

----->f[2]=0%10=0;

up=s/10;----->up=0/10=0;

当j大于等于2时的情况和“3.”时的情况相同,此时得到的结果为:f[0]=4;f[1]=2;f[2]=0;f[3]=0;......

四:i=5时

1.

j=0时:s=f[j]*i+up;----->s=f[0]*5+0=20;

f[j]=s%10;----->f[0]=s%10=20%10=0;

up=s/10;----->up=20/10=2;

2.

j=1时:s=f[j]*i+up;----->s=f[1]*5+2=2*5+2=12;

f[j]=s%10;----->f[1]=12%10=2;

up=s/10;----->up=12/10=1;

3.

j=2时:s=f[j]*i+up;----->s=f[2]*5+1=0*5+1=1;

f[j]=s%10;----->f[2]=1%10=1;

up=s/10;----->up=1/10=0;

4.

j=3时:s=f[j]*i+up;----->s=f[3]*5+up=0*5+0=0;

f[j]=s%10;----->f[3]=0%10=0;

up=s/10;----->up=0/10=0;

当j大于等于4时的情况和"3."时的情况相同,此时得到的结果是:f[0]=0;f[1]=2;f[2]=1;f[3]=0;f[4]=0;......

 

到这个时候,我们已经把n=5时的情况带入程序并完全分解开写了出来,通过这个展开式我们能够发现什么呢?在这个问题得到解决以前,我们先看一看以下乘法:

 

24*5=120,若是我们在小学,一定会这么算:

运算1:

2 4

* 5

————————

1 2 0

我们先用4乘以5得到20然后写下0进2;然后让2乘以5得到10,用10加上2得到12,我们应该写下2进1;因为24前一位是0,我们可以认为是用0乘以5加上1得到1,此时写下1,24*5的乘法运算结束。

算到这里你是不是有什么启发呢?再看下一个乘法运算的例子,或许你就会有一些灵感了:

运算2:

1 2 2

* 4 5

——————————————————

6 1 0

4 8 8

——————————————————

5 4 9 0

这是我们在小学的时候就用的乘法运算,但是我们还能够将上面的那个乘法过程加以变形,然后得到另一个类似的乘法运算:

运算3:

1 2 2

* ( 4 5)//在本次运算中把45视为一个整体

______________________________________________

先用122中的最高位2乘以45得到90,写下0进9;在让122中的次高位的2乘以45得到90,加上进位的9,得到99,写下最高位的9,进最低位的9;再让122中的最低位1乘以45得到45,用45加上进位的9,得到54,写下4,进5;因为122可以认为是0122,所以我们再用0乘以45得到0,用0加上进位的5,得到5,写下5,我们仍然能够得

 

到5490这个答案,实际上,运算3可以等效为下面的运算4:

运算4:

4 5

* 1 2 2

————————————————

9 0

9 0

4 5

————————————————

5 4 9 0

我们发现运算3和运算4实际上是一个运算,因为它们的运算过程完全相同,但是因为我们一般都是用运算2的运算方法运算两个数的乘积,就连运算4的方法人们都很少用,更何况运算3了。虽然说运算3的方法很少用,但是,实际上,在编程的时候,特别是在阶乘的高精度运算中,用的正是这个方法。

在运算3中122*45的过程相当于内循环中的整个循环,122相当于前44(外循环中的i-1)个数的乘积(假设是,实际上不是,这个乘积相当于i-1的阶乘),45相当于外循环中的i;

在运算的时候,程序把45当成一个整体来运算。我们来看看到底程序是怎样用数组来保存大整数的。还是用122*45这个例子。

实际上数组中的每一个元素都保存有f[i]>=0&&f[i]<=9的元素。在122*45这个乘法运算中,则有

1 2 2

| | |

f[2] f[1] f[0]

所以有

运算3:

1 2 2

* (4 5)

—————————————————

 

 

等价于

运算5:

f[3] f[1] f[0]

* i

____________________________________________

我们把运算3的运算过程带入运算5就能发现,这个过程和我们将n=5时的情况带入程序时的分析过程完全相同,到此,我们已经完全证明了,程序代码中的内循环就是i-1的阶乘结果乘以i的过程,而且这个过程用的方法就是我们在运算3中用的方法。

到这里,我们已经能够完全明白百度百科中所提供的程序代码的运行过程了,外循环控制阶乘数,正如:

0!*1=1!;

1!*2=2!;

2!*3=3!;

3!*4=4!;

4!*5=5!;

内循环控制i-1!和i的乘积求积的过程,如4!*5的过程,其中4!在上个内循环已经求出并保存在了数组中。

分析过程到此结束!

posted on 2017-02-09 17:36  凌云朗  阅读(1103)  评论(0)    收藏  举报

导航