大整数的加法、减法、乘法,附完整代码

本文详细解析了大整数加法、减法及乘法的链栈实现方式,包括存储原理、运算流程与代码实现,适合对大整数运算感兴趣的技术人员阅读。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 本人萌新一个,要是有大佬发现bug还请一定要告诉我

一:大整数存储原理

用链栈的方式,每输入一个数字就压栈,同时用一个常量step记录数字长度放在栈顶方便后续使用,以“#”结束,分别将两个操作数压入S1和S2,结果放入S栈,完美实现任意长度的数字存储
(毕竟输入时是从左到右输入,计算时是从右到左计算,输出时又是从左往右,与栈的特点相符,经过尝试,加减运算全程确实完全符合这一规律)
二:加法
从数字个位开始即从栈顶开始,每次从S1和S2各弹出一个数,用k记录是否进位(0、1),进行一位数加法运算(S1+S2+k),将(S1+S2+k)%10压入S栈,循环执行下去,若其中一个栈已空,即直接把另一个栈加上进位k压入结果S栈,直到两个栈都为空。

三:减法
方法和加法类似,进位k在0和-1间选择
只是开始时先比较两个操作数的位数,若S1<S2则交换S1和S2位置,在输出答案前加上“-”;
bug:当两个数位数相同但实际数值S1<S2时,目前会输出“error”,得不出负数结果
还请大佬指点

四:乘法

乘法的计算思路和我们正常做竖式一样
操作数存储和上述一样(我觉得可以考虑改其它数据结构可能会更简便)
计算前根据栈顶存储的操作数长度,将更长的那个放在乘数位置(竖式的上面),另一个放下面,可以减少循环次数。因为乘数会被反复使用,所以直接申请一个数组a单独存放。
每次循环,被乘数栈弹出一个数,同时用time记录是第几次循环(关系到结果后面的补0个数),与a数组从右开始进行一位数的乘法运算,k记录进位,(S2*a[i]+k)%10压入辅助栈t,(S2*a[i]+k)/10给k。每得出一个结果便加到上一个结果上(调用上面加法的方法,本人单独写了一个jiafa_2函数(实现s+t),因为发现操作数是倒过来的,在最前面加了对栈的倒转(swap函数)对于这部分可以考虑把链栈结构改为链队列),最后得到的栈s即为乘法的结果。

 

完整可运行代码如下:

#include<stdio.h>
#include<stdlib.h>
typedef struct Snode{
	int data;                           //存放一位数字 
	struct Snode *next;
}Snode,*LinkStack;
void Push(LinkStack &S,int e)           //压栈 
{
	LinkStack p;
	p=new Snode;
	p->data=e;
	p->next=S;
	S=p;
}
int Pop(LinkStack &S)                   //出栈 
{
	int e;
	LinkStack p;
	if(S==NULL)return -1;
	e=S->data;
	p=S;
	S=S->next;
	delete p;
	return e;
}
void Putin(LinkStack &S1)                //录入大整数,以#结束 
{
	char A;
	int step=0;                          //step记录大整数位数 
	printf("put in num\n");
	while(1)
	{
		step++;
		scanf("%c",&A);
		if(A=='#')
			break;
		Push(S1,A-48);
	}
	Push(S1,step-1);                      //将step压在栈顶 
}
void jiafa()                              //大整数加法运算 
{
	LinkStack S1;
	S1=NULL;
	Putin(S1);                            //录入大整数S1 
	
	getchar();
	
	LinkStack S2;
	S2=NULL;
	Putin(S2);                            //录入大整数S2 
	
	LinkStack S;
	S=NULL;                               //栈S记录结果 
	int len1=Pop(S1);                     //len1记录S1长度 
	int len2=Pop(S2);                     //len2记录S2长度 
	int k=0;                              //记录是否进位 
	int c;                                //c作为中间量 
	while(S1!=NULL&&S2!=NULL)             //S1、S2都不为空 
	{
		c=Pop(S1)+Pop(S2)+k;              //c为两个栈的栈顶数字之和 
		if(c>=10)
		{
			c=c-10;
			k=1;
			Push(S,c);                    //将c的个位压入栈S 
		}else 
		{
			k=0;
			Push(S,c);
		}
	}
	if(S1==NULL&&S2==NULL&&k!=0)          //当两个栈同时为空,如果k!=0则将k压栈 
	{
		Push(S,k);                         
		k=0;
	}
	while(S1!=NULL)                       //当S1不为空,将S1和k相加后压入S栈 
	{
		c=Pop(S1)+k;
		if(c>=10)
		{
			k=1;
			c=c-10;
			Push(S,c);
		}
		else
		{
			k=0;
			Push(S,c);
		}	
	}
	while(S2!=NULL)                       //当S2不为空,将S1和k相加后压入S栈 
	{
		c=Pop(S2)+k;
		if(c>=10)
		{
			k=1;
			c=c-10;
			Push(S,c);
		}
		else
		{
			k=0;
			Push(S,c);
		}	
	}
	if(k!=0)Push(S,k);                     //当所有栈都为空时,看k!=0时将k压栈 
	while(S!=NULL)                         //针对类似99999+1的情况 
		printf("%d",Pop(S));
}

void jianfa()                              //大整数减法运算 
{
	LinkStack S1;                          //和加法一样的录入过程,默认S1为被减数,S2为减数 
	S1=NULL;
	Putin(S1);
	
	getchar();
	
	LinkStack S2;
	S2=NULL;
	Putin(S2);
	
	LinkStack S;
	S=NULL;
	int len1=Pop(S1);
	int len2=Pop(S2);
	int k=0;
	if(len1<len2)                          //当S1的位数<S2的位数, 
	{                                      // 将S1和S2交换位置,在输出结果时加-号
		S=S1;
		S1=S2;
		S2=S;
		S=NULL;
	}
	while(S1!=NULL&&S2!=NULL)              //S1、S2都不空则循环计算 
	{
		int a=Pop(S1);                     //a记录S1栈顶即减数 
		int b=Pop(S2);                     //b记录S2栈顶即被减数 
		if(a+k>=b)                         //a+k>=b即够减则直接将结果压栈 
		{
			Push(S,a+k-b);
			k=0;
		}
		else                               //不够减则将k置为-1,a加上10进行运算 
		{
			Push(S,a+k+10-b);
			k=-1;			
		}
	}
	while(S1!=NULL)                        //因为前面保证了S1长度大于等于S2,所以只要考虑S1是不是空 
	{
		int a=Pop(S1);
		if(a+k>=0)
		{
			Push(S,a+k);
			k=0;
		}
		else                                //a+k<0即还要向前借位,所以当前位置为9 ,k为-1 
		{
			k=-1;
			Push(S,9);
		}
	}
	if(k==-1)                               //S1、S2都为空,k仍为-1说明S1和S2位数相同但S1<S2,输出error报错 
	{
		printf("error\n");
		return;
	}
	if(k==0){                               //k==0说明S1-S2够减 
		int a=0;
		while((a=Pop(S))==0);               //将结果栈栈顶为0的部分弹出 
		if(a==-1)a=0;                       //如果全部为0则将a置0 
		Push(S,a);                          //将弹出的不为零的项压回去 
	};
	if(len1<len2)printf("-");
	while(S!=NULL)
		printf("%d",Pop(S));
	return;
}
void Swap(LinkStack &S)                     //将栈S首位倒转 
{
	LinkStack T;
	T=NULL;
	while(S!=NULL)
	{
		Push(T,Pop(S));
	}
	S=T;
}
void jiafa_2(LinkStack &S1,LinkStack &S2)    //针对乘法部分单独写的加法函数 
{                                            //将S1和S2相加,结果存入S1 
	Swap(S1);                                //这里获得的S1,S2都是上一次加法的结果,所以顺序正好相反,将S1倒转 
	Swap(S2);                                //将S2倒转 
	LinkStack S;
	S=NULL;
	int k=0;
	int c;
	while(S1!=NULL&&S2!=NULL)                //具体加法过程和上面相同 
	{
		c=Pop(S1)+Pop(S2)+k;
		if(c>=10)
		{
			c=c-10;
			k=1;
			Push(S,c);
		}else 
		{
			k=0;
			Push(S,c);
		}
	}
	if(S1==NULL&&S2==NULL&&k!=0)
	{
		Push(S,k);
		k=0;
	}
	while(S1!=NULL)
	{
		c=Pop(S1)+k;
		if(c>=10)
		{
			k=1;
			c=c-10;
			Push(S,c);
		}
		else
		{
			k=0;
			Push(S,c);
		}	
	}
	while(S2!=NULL)
	{
		c=Pop(S2)+k;
		if(c>=10)
		{
			k=1;
			c=c-10;
			Push(S,c);
		}
		else
		{
			k=0;
			Push(S,c);
		}	
	}
	if(k!=0)Push(S,k);
	S1=NULL;
	S1=S;                                  //把结果栈赋给S1 
}
void chenfa()                              //大整数乘法运算 
{
	LinkStack S1;                          //大整数录入过程和上面相同 
	S1=NULL;
	Putin(S1);
	
	getchar();
	
	LinkStack S2;
	S2=NULL;
	Putin(S2);
		
	LinkStack S;
	S=NULL;
	int c,k=0;
	int time=-1;                           //记录被乘数计算到第几位了 
	int len1=Pop(S1);
	int len2=Pop(S2);
	if(len1<len2)                          //比较S1\S2位数,将长的设为被乘数,即放在前面 
	{
		S=S1;
		S1=S2;
		S2=S;
		S=NULL;
		c=len1;
		len1=len2;
		len2=c; 
	}
	
	int *a=new int[len1];                   //因为被乘数会被循环使用 
	for(int i=len1-1;i>=0;i--)              //所以动态开辟数组将被乘数按位存入其中 
		a[i]=Pop(S1);
	while(S2!=NULL)                         //乘数不为空即要进行一次循环 
	{
		time++;
		c=Pop(S2);	                        //c记录乘数栈顶即最低位 
		if(time==0)                         
		{
			for(int i=len1-1;i>=0;i--)      // 从被乘数最低位开始循环 
			{
				Push(S,(c*a[i]+k)%10);      //将结果的各位压栈,十位给k 
				k=(c*a[i]+k)/10;
			}
			if(k!=0)                        //被乘数已经遍历完,k!=0则把k压栈 
				Push(S,k);
		}
		else
		{
			LinkStack t;
			t=NULL;
			for(int i=0;i<time;i++)         //从乘数的第二位开始,要为结果填充0 
				Push(t,0);
			k=0;
			for(int i=len1-1;i>=0;i--)
			{
				Push(t,(c*a[i]+k)%10);
				k=(c*a[i]+k)/10;
			}
			if(k!=0)
				Push(t,k);
			jiafa_2(S,t);
		}	
	}		
	while(S!=NULL)
		printf("%d",Pop(S));
	printf("\n");
	
}
int main()
{
	
	int n;
	printf("1.加法\n2.减法\n3.乘法\n0.退出\n ");
	printf("put in n\n");
	scanf("%d",&n);
	getchar();
	while(n!=0)
	{
		system("cls");
		switch(n)
		{
			case 1:
				{
					jiafa();
					break;
				}
			case 2:
				{
					jianfa();
					break;
				}
				case 3:
					{
						chenfa();
						break;
					}
		}
		printf("\n1.加法\n2.减法\n3.乘法\n0.退出\n");
		printf("put in n\n");
		scanf("%d",&n);
		getchar();
	}
}

 

密码学分为两类密码:对称密码和非对称密码。对称密码主要用于数据的加/解密,而非对称密码则主要用于认证、数字签名等场合。非对称密码在加密和解密时,是把加密的数据当作一个大的正整数来处理,这样就涉及到大整数的加、减、乘、除和指数运算等,同时,还需要对大整数进行输出。请采用相应的数据结构实现大整数的加、减、乘、除和指数运算,以及大整数的输入和输出。 【基本要求】 1.要求采用链表来实现大整数的存储和运算,不允许使用标准模板类的链表类(list)和函数。 同时要求可以从键盘输入大整数,也可以文件输入大整数大整数可以输出至显示器,也可以输出至文件。大整数的存储、运算和显示,可以同时支持二进制和十进制,但至少要支持十进制。大整数输出显示时,必须能清楚地表达出整数的位数。测试时,各种情况都需要测试,并上测试截图;要求测试例子要比较详尽,各种极限情况也要考虑到,测试的输出信息要详细易懂,表明各个功能的执行正确。 2.要求大整数的长度可以不受限制,即大整数的十进制位数不受限制,可以为十几位的整数,也可以为500多位的整数,甚至更长;大整数运算和显示时,只需要考虑正的大整数。如果可能的话,请以秒为单位显示每次大整数运算的时间。 3.要求采用类的设计思路,不允许出现类以外的函数定义,但允许友元函数。主函数中只能出现类的成员函数的调用,不允许出现对其它函数的调用。 4.要求采用多文件方式:.h文件存储类的声明,.cpp文件存储类的实现,主函数main存储在另外一个单独的cpp文件中。如果采用类模板,则类的声明和实现都放在.h文件中。 5.不强制要求采用类模板,也不要求采用可视化窗口;要求源程序中有相应注释。 6.要求采用Visual C++ 6.0及以上版本进行调试。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橙子树下

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值