阶乘的计算

最近看到一条题目,计算n!,阶乘由于它的特殊性,需要涉及到大数运算,本文在32位机器下描述一个简单的计算方法。

        网上有很多种方法,本文就用数组的形式来保存计算结果,首先可以知道unsigned的数据范围是0-2^32-1,因此常规的想法就是将进制提升到2^32,从而用一个数组来保存计算结果,下面给出具体代码:

int bigNum( int x, unsigned *p, int len )
{
	if ( 13 > x || p == NULL || len <= 0 )
		return -1;
	p[0] = 479001600;
	int c = 1;
	__int64 base = 0x100000000;
	for ( int i=13; i<=x; i++ )
	{
		int addbit = 0;
		__int64 num = 0;
		for ( int j=0; j<c; j++ )
		{
			num = p[j];
			num = num*i + addbit;
			if ( num <= base-1 )
			{
				p[j] = (unsigned)num;
				addbit = 0;
			}
			else
			{
				p[j] = num%base;
				addbit = num/base;
			}
		}
		if ( addbit != 0 )
			p[c++] = addbit;
	}
	return c;
}
首先,根据计算可知,12!=479001600,没有超过unsigned的范围,上述代码计算13及以上的阶乘,计算过程就是将当前数i依次与结果数组p中的每个数依次相乘,然后加上上一轮的进位,在计算过程中用到了64位num来辅助计算进位,从而防止发生溢出,这个程序本身没有问题,但是调用以后的结果是这样的:

unsigned buf[200] = {0};
int c = bigNum( 13, buf, 200 );
for(int i=0; i<c; i++)
     cout<<buf[i]<<"  ";
输出:

1932053504 1
这样的结果太不直观,可想而知,当计算较大的数的时候,得出的是一堆高低反序,并且以2^32为进制的数组数据,想要直观的输出的十进制数据,貌似好像比较麻烦,我在这里也踌躇了一会儿,转换起来太麻烦,后来仔细一想我可以以2^32为进制,那么同样我也可以以10^x为进制,上述代码我们采用的是unsigned数组来保存最后结果,而unsigned最多可以包含10位10进制数,一次我们可以将进制定为10^9,因此只要将上述代码中的的base修改一下即可:

_int64 base = 1000000000;

然后运行上述测试代码,输出

227020800 6
反过来输出即可得到正确结果6227020800,此处还有需要注意的地方,就是数组中可能省略了前面的0,因此可以将输出部分修改如下:

printf("%d",buf[c-1]);
for(int i=c-2;i>=0;i--)
     printf("%09d",buf[i]);
需要反序输出,另外出最后一个外,其余需要补齐中间的0

void CalcNN(int n, char *pOut)
{
	if ( n <= 0 || pOut == NULL )
		return;

	if ( n<=12 )
	{
		unsigned num = 1;
		for ( int i=1; i<=n; i++ )
			num *= i;
		sprintf( pOut, "%d", num );
		return;
	}
	unsigned buf[300] = {0};
	int c = bigNum(n, buf, 300);
	sprintf( pOut, "%d", buf[c-1] );
	for ( int i=c-2; i>=0; i-- )
	{
		int len = strlen(pOut);
		sprintf( pOut+len, "%09d", buf[i] );
	}
}
上述代码将最后的结果以字符串的形式保存在pOut所指向的缓存空间中。

       总结:代码本身不复杂,主要的是一个思想,我们可以利用一个较大数据范围类型的数组,来模拟一个较小的进制,本题中base也可以取100、1000........






你可能感兴趣的:(阶乘的计算)