大整数乘法 求阶乘

题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=1042


N!

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 24886    Accepted Submission(s): 6897


Problem Description
Given an integer N(0 ≤ N ≤ 10000), your task is to calculate N!
 

Input
One N in one line, process to the end of file.
 

Output
For each N, output N! in one line.
 

Sample Input
  
  
1 2 3
 

Sample Output
  
  
1 2 6
 


解题思路 :  算法来源于一篇博客的介绍---“列表法”。http://blog.csdn.net/amossavez/article/details/4312399



      也就是说我们在计算生成这个二维表时,不必一位一位地乘,而可以三位三位地乘;在累加时也是满1000进位。这样,我们在计算 m位整数乘以 n位整数,只需要进行 m x n / 9次乘法运算,再进行约(m + n) / 3次加法运算和(m + n) /3 次取模运算。总体看来,效率约是前一种算法的 9倍。 
有人可能会想:既然能够三位三位地乘,为什么不4位 4位甚至5位5位地乘呢?那不是可以提高 16 乃至 25 倍效率吗?听我解来:本算法在累加表中斜线间的数字时,如果用无符号长整数(范围 0至~4294967295)作为累加变量,在最不利的情况下(两个乘数的所有数字均是 9),能够累加约4294967295/(999*999)=4300 次,也就是能够准确计算任意两个均不超过 12900(每次累加的结果"值"三位,故 4300*3=12900)位的整数相乘。如果 4 位 4 位地乘,在最不利的情况下,能够累加约4294967295/(9999*9999)=43 次,仅能够确保任意两个不超过 172 位的整数相乘,没有什么实用价值,更不要说5位了。


倒序存储:如res[]={312,658,7}  two[]={564,2}

代码如下:

#include <iostream>
#include <iomanip>
using namespace std;

unsigned int *res,*temp;
int two[2];
int digit;

void mult(int x)
{
	int twoN;
	unsigned int *p=0;
	if(x>999){two[0]=x%1000;two[1]=x/1000;twoN = 2;}
	else{two[0]=x;twoN=1;}
	int c=0;
	int i,j,maxj;
	//cout<<"two: "<<two[0]<<","<<two[1]<<" --"<<twoN<<endl;
	for(i=0;i<digit+twoN-1;i++)
	{
		if(i-digit+1>0)j=i-digit+1;
		else j=0;
		maxj = i+1 > twoN ? twoN:i+1;
		for(;j<maxj;j++)
		{
			c = c + two[j]*res[i-j];
		}
	
		temp[i]=c%1000;
		c = c/1000; 
		//cout<<">>>> i:"<<i<<"  temp[i]:"<<temp[i]<<endl;
	}
	if(c){temp[i]=c;digit=i+1;}
	else digit=i;
	p=res;
	res=temp;
	temp=p;
}

int main()
{
	//freopen("out.txt","w",stdout);
	int n,i;
	res=(unsigned int *)malloc(sizeof(int)*15000);
	temp=(unsigned int *)malloc(sizeof(int)*15000);
	while(cin>>n)
	{
		if(n==0){cout<<1<<endl;continue;}
		memset(res,0,sizeof(int)*15000);
		if(n>999){res[0]=n%1000;res[1]=n/1000;digit=2;}
		else {res[0] = n;digit = 1;}
		for(i=n-1;i>1;i--)
		{
			mult(i);
		}
		//cout<<"digit:"<<digit<<endl;
		cout<<res[digit-1];
		for(i=digit-2;i>=0;i--)
		{	cout<<setfill('0')<<setw(3)<<res[i];	}
		cout<<endl;
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一、 课题内容和要 1、系统的基本功能及要 (1)写一个类BigInteger,并有如下成员函数operator+/ operator-/ operator*/ operator\,即整数的四则运算并重载运算符。 (2)用该大整数计算n的阶乘并显示出来。(n∈[1,100]) 注:为了简化,可不考虑负数,则遇上形如“2-4”这样的表达式需要报错。 2、程序执行过程 (1)系统从键盘读入自然数n,直到输入了合法的n为止。 (2)输出运算的结果,为便于观察结果,每输出四位中间插入空格,格式如下(例如,n=12): 12!= 4790 0160 0 (3)询问用户是否继续进行,用户选择“是”,直到输入合法为止,转(1),否则退出程序。 3、算法及提示 (1)因为n较大时,n!的结果将超出长整形的保存范围,因此结果不能用long int型的变量来保存。本算法用链表来存储。 (2)链表的每一个节点存储结果的一位数字,因此结果的输出实际上是链表的遍历问题,同时要先考虑用多少位来表示大整数。 4、其他要 (1)输入时具备一定的容错性判断,如输入的不是数字,或输入的数超过范围等等。 (2)变量、函数命名符合规范。 (3)注释详细:每个变量都要有注释说明用途;函数有注释说明功能,对参数、返回值也要以注释的形式说明用途;关键的语句段要有注释解释。 (4)程序的层次清晰,可读性强。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值