大数运算之加减乘除

原文1链接:https://blog.csdn.net/lisp1995/article/category/6384570
原文2链接:https://www.cnblogs.com/wuqianling/p/5387099.html
原文3链接:https://www.nowcoder.com/questionTerminal/0337e32b1e5543a19fa380e36d9343d7?f=discussion

一.大数加法

两个大数我们可以用数组来保存,在数组中逐位进行相加,再判断该位相加后是否需要进位。
为了方便计算,我们将数字的低位放在数组的前面,高位放在后面。

/*
  大数加法 
  参数: 
  num1为第一个大数,用字符数组保存
  num2为第二个大数
  sum数组保存相加的结果  即:num1+num2=sum
  返回值:返回数组sum的有效长度,即计算结果的位数 
 */
int Addition(char num1[], char num2[], int sum[])
{
    int i, j, len;
    int n2[MAX] = {0};
    int len1 = strlen (num1); // 计算数组num1的长度,即大数的位数 
    int len2 = strlen (num2); // 计算数组num2的长度,即大数的位数 

    len = len1>len2 ? len1 : len2; // 获取较大的位数
    //将num1字符数组的数字字符转换为整型数字,且逆向保存在整型数组sum中,即低位在前,高位在后
    for (i = len1-1, j = 0; i >= 0; i--, j++) 
        sum[j] = num1[i] - '0';
    // 转换第二个数 
    for (i = len2-1, j = 0; i >= 0; i--, j++)
        n2[j] = num2[i] - '0';
    // 将两个大数相加 
    for (i = 0; i <= len; i++)
    {
        sum[i] += n2[i];  // 两个数从低位开始相加 
        if (sum[i] > 9)   // 判断是否有进位 
        {   // 进位 
            sum[i] -= 10;
            sum[i+1]++;
        }
    }
    if(sum[len] != 0)  // 判断最高位是否有进位 
        len++;
    return len;   // 返回和的位数 
}

二.大数减法

相减算法也是从低位开始减的。先要判断被减数和减数哪一个位数长,若被减数位数长是正常的减法;若减数位数长,则用被减数减去减数,最后还要加上负号;当两数位数长度相等时,最好比较哪一个数字大,否则负号处理会很繁琐;处理每一项时要,如果前一位相减有借位,就先减去上一位的借位,无则不减,再去判断是否能够减开被减数,如果减不开,就要借位后再去减,同时置借位为1,否则置借位为0。

#include<stdio.h>
#include<string.h>

#define MAX 1000    // 大数的最大位数 


/*
  大数减法
  参数:
  num1为被减数,用字符数组保存
  num2为减数
  sum数组保存相减的结果   即:num1-num2=sum
  返回值:返回数组sum的有效长度,即计算结果的位数
 */
int Subtraction(char num1[], char num2[], int sum[]) {
	int i, j, len, blag;
	char *temp;
	int n2[MAX] = {0};
	int len1 = strlen(num1); // 计算数组num1的长度,即大数的位数
	int len2 = strlen(num2); // 计算数组num2的长度,即大数的位数

	// 在进行减法之前要进行一些预处理
	blag = 0; // 为0表示结果是正整数,为1表示结果是负整数
	if(len1 < len2) { // 如果被减数位数小于减数
		blag = 1; // 标记结果为负数
		// 交换两个数,便于计算
		temp = num1;
		num1 = num2;
		num2 = temp;
		len = len1;
		len1 = len2;
		len2 = len;
	} else if(len1 ==len2) { // 如果被减数的位数等于减数的位数
		// 判断哪个数大
		for(i = 0; i < len1; i++) {
			if(num1[i] == num2[i])
				continue;
			if(num1[i] > num2[i]) {
				blag = 0; // 标记结果为正数
				break;
			} else {
				blag = 1; // 标记结果为负数
				// 交换两个数,便于计算
				temp = num1;
				num1 = num2;
				num2 = temp;
				break;
			}
		}
	}
	len = len1>len2 ? len1 : len2; // 获取较大的位数
	//将num1字符数组的数字转换为整型数且逆向保存在整型数组sum中,即低位在前,高位在后
	for (i = len1-1, j = 0; i >= 0; i--, j++)
		sum[j] = num1[i] - '0';
	// 转换第二个数
	for (i = len2-1, j = 0; i >= 0; i--, j++)
		n2[j] = num2[i] - '0';
	// 将两个大数相减
	for (i = 0; i <= len; i++) {
		sum[i] = sum[i] - n2[i]; // 两个数从低位开始相减
		if (sum[i] < 0) { // 判断是否有借位
			// 借位
			sum[i] += 10;
			sum[i+1]--;
		}
	}
	// 计算结果长度
	for (i = len1-1; i>=0 && sum[i] == 0; i--)
		;
	len = i+1;
	if(blag==1) {
		sum[len] = -1;  // 在高位添加一个-1表示负数
		len++;
	}
	return len;   // 返回结果的位数
}

int main() {
	int i, len;
	int sum[MAX] = {0}; // 存放计算的结果,低位在前,高位在后,即sum[0]是低位
	char num1[] = "987654321987654321"; // 第一个大数
	char num2[] = "123456789123456789"; // 第二个大数
	len = Subtraction(num1, num2, sum);    // 两数相减
	// 输出结果
	printf("%s\n  -\n%s\n  =\n", num1, num2);
	if(sum[i=len-1] < 0) { // 根据高位是否是-1判断是否是负数
		printf("-"); // 输出负号
		i--;
	}
	for (; i >= 0; i--)
		printf("%d", sum[i]);
	printf("\n");
	return 0;
}

三.大数乘法

首先说一下乘法计算的算法,从低位向高位乘,在竖式计算中,我们是将乘数第一位与被乘数的每一位相乘,记录结果,之后,用第二位相乘,记录结果并且左移一位,以此类推,直到计算完最后一位,再将各项结果相加,得出最后结果。

计算的过程基本上和小学生列竖式做乘法相同。为了编程方便,并不急于处理进位,而是将进位问题留待最后统一处理。

总结一个规律: 即一个数的第i 位和另一个数的第j 位相乘所得的数,一定是要累加到结果的第i+j 位上。这里i, j 都是从右往左,从0 开始数。
ans[i+j] = a[i]*b[j];

另外注意进位时要处理,当前的值加上进位的值再看本位数字是否又有进位;前导清零。

#include<stdio.h>
#include<string.h>
#define MAX 1000    // 大数的最大位数 

// 注:
// 本代码在以下博客代码中进行修改:
// http://www.cnblogs.com/javawebsoa/archive/2013/08/01/3231078.html
//


/*
  函数SubStract功能:
  用长度为len1的大整数p1减去长度为len2的大整数p2
  结果存在p1中,返回值代表结果的长度
  不够减:返回-1 , 正好够:返回0
*/
int SubStract(int *p1, int len1, int *p2, int len2) {
	int i;
	if(len1 < len2)
		return -1;
	if(len1 == len2 ) {
		// 判断p1 > p2
		for(i = len1-1; i >= 0; i--) {
			if(p1[i] > p2[i])   // 若大,则满足条件,可做减法
				break;
			else if(p1[i] < p2[i]) // 否则返回-1
				return -1;
		}
	}
	for(i = 0; i <= len1-1; i++) { // 从低位开始做减法
		p1[i] -= p2[i];         // 相减
		if(p1[i] < 0) {         // 若是否需要借位
			// 借位
			p1[i] += 10;
			p1[i+1]--;
		}
	}
	for(i = len1-1; i >= 0; i--) { // 查找结果的最高位
		if( p1[i] )             //最高位第一个不为0
			return (i+1);       //得到位数并返回
	}
	return 0;                   //两数相等的时候返回0
}


/*
  大数除法---结果不包括小数点
  num1 被除数
  num2 除数
  sum  商,存放计算的结果,即:num1/num2=sum
  返回数组sum的有效长度,即商的位数
*/
int Division(char num1[], char num2[], char sum[]) {
	int k, i, j;
	int len1, len2, len=0;     //大数位数
	int dValue;                //两大数相差位数
	int nTemp;                 //Subtract函数返回值
	int num_a[MAX] = {0};      //被除数
	int num_b[MAX] = {0};      //除数
	int num_c[MAX] = {0};      //商

	len1 = strlen(num1);       //获得大数的位数
	len2 = strlen(num2);

	//将数字字符转换成整型数,且翻转保存在整型数组中
	for( j = 0, i = len1-1; i >= 0; j++, i-- )
		num_a[j] = num1[i] - '0';
	for( j = 0, i = len2-1; i >= 0; j++, i-- )
		num_b[j] = num2[i] - '0';

	if( len1 < len2 ) {        //如果被除数小于除数,直接返回-1,表示结果为0
		return -1;
	}
	dValue = len1 - len2;      //相差位数
	for (i = len1-1; i >= 0; i--) {  //将除数扩大,使得除数和被除数位数相等
		if (i >= dValue)
			num_b[i] = num_b[i-dValue];
		else                         //低位置0
			num_b[i] = 0;
	}
	len2 = len1;
	for(j = 0; j <= dValue; j++ ) {  //重复调用,同时记录减成功的次数,即为商
		while((nTemp = SubStract(num_a, len1, num_b+j, len2-j)) >= 0) {
			len1 = nTemp;            //结果长度
			num_c[dValue-j]++;       //每成功减一次,将商的相应位加1
		}
	}
	// 计算商的位数,并将商放在sum字符数组中
	for(i = MAX-1; num_c[i] == 0 && i >= 0; i-- );  //跳过高位0,获取商的位数
	if(i >= 0)
		len = i + 1; // 保存位数
	for(j = 0; i >= 0; i--, j++)     // 将结果复制到sum数组中
		sum[j] = num_c[i] + '0';
	sum[j] = '\0';   // sum字符数组结尾置0
	return len;      // 返回商的位数
}


int main() {
	int i;
	int len;                // 商的位数
	char num1[MAX] = "1234567899876543210";   // 第一个大数
	char num2[MAX] = "20160415123025";              // 第二个大数
	char sum[MAX] = {0};    // 计算结果

	//scanf("%s", num1);      //以字符串形式读入大数
	//scanf("%s", num2);

	len = Division(num1, num2, sum);

	//输出结果
	printf("%s\n  ÷\n%s\n  =\n", num1, num2);
	if( len>=0 ) {
		for(i = 0; i < len; i++ )
			printf("%c", sum[i]);
	} else {
		printf("0");
	}
	printf("\n");

	return 0;
}

四.大数除法

#include<stdio.h>
#include<string.h>
char a[100],b[100];//用两个字符串用来输入两个大数
int x[100],y[100],z[100],m[100];//被除数  除数  商  余数
int digit;//大数的位数
void sub(int x[],int y[],int len1,int len2) { //大数减法
	int i;
	for(i=0; i<len1; i++) {
		if(x[i]<y[i]) {
			x[i]=x[i]+10-y[i];
			x[i+1]--;
		} else
			x[i]=x[i]-y[i];
	}
	for(i=len1-1; i>=0; i--) { //判断减法结束之后,被除数的位数
		if(x[i]) {
			digit=i+1;
			break;
		}
	}
}
int judge(int x[],int y[],int len1,int len2) {
	int i;
	if(len1<len2)
		return -1;
	if(len1==len2) { //若两个数位数相等
		for(i=len1-1; i>=0; i--) {
			if(x[i]==y[i])//对应位的数相等
				continue;
			if(x[i]>y[i])//被除数 大于 除数,返回值为1
				return 1;
			if(x[i]<y[i])//被除数 小于 除数,返回值为-1
				return -1;
		}
		return 0;//被除数 等于 除数,返回值为0
	}
}
int main() {
	int i,j=0,k=0,temp;
	int len1,len2,len;//len两个大数位数的差值
	while(~scanf("%s %s",a,b)) {
		len1=strlen(a);//被除数位数
		len2=strlen(b);//除数位数
		for(i=len1-1,j=0; i>=0; i--) //将字符串中各个元素倒序储存在数组中
			x[j++]=a[i]-'0';
		for(i=len2-1,k=0; i>=0; i--)
			y[k++]=b[i]-'0';
		if(len1<len2) { //当被除数位数 小于 除数位数时
			printf("商是:0\n");
			printf("余数是:");
			puts(a);
		} else { //当被除数位数 大于或者 除数位数时
			len=len1-len2;//两个大数位数的差值
			for(i=len1-1; i>=0; i--) { //将除数后补零,使得两个大数位数相同。被除数:4541543329 除数:98745,加零后:9874500000
				if(i>=len)
					y[i]=y[i-len];
				else
					y[i]=0;
			}
			len2=len1;//将两个大数数位相同
			digit=len1;	//将原被除数位数赋值给digit
			for(j=0; j<=len; j++) {
				z[len-j]=0;
				while(((temp=judge(x,y,len1,len2))>=0)&&digit>=k) { //判断两个数之间的关系以及位数与除数原位数的关系
					sub(x,y,len1,len2);	//大数减法函数
					z[len-j]++;//储存商的每一位
					len1=digit;//重新修改被除数的长度
					if(len1<len2&&y[len2-1]==0)
						len2=len1;//将len1长度赋给len2;
				}
				if(temp<0) { //若被除数 小于 除数,除数减小一位。例如:被除数:4541543329 除数:(原)98745,(加零后)9874500000,后退一位后:0987450000
					for(i=1; i<len2; i++)
						y[i-1]=y[i];
					y[i-1]=0;
					if(len1<len2)
						len2--;
				}
			}
			printf("商是:");
			for(i=len; i>0; i--) { //去掉前缀0
				if(z[i])
					break;
			}
			for(; i>=0; i--)
				printf("%d",z[i]);
			printf("\n");
			printf("余数是:");
			for(i=len1; i>0; i--) {
				if(x[i])
					break;
			}
			for(; i>=0; i--)
				printf("%d",x[i]);
			printf("\n");
		}
	}
	return 0;
}

二进制转换

链接:https://www.nowcoder.com/questionTerminal/0337e32b1e5543a19fa380e36d9343d7?f=discussion
来源:牛客网

#include <stdio.h>
#include <string.h>
using namespace std;
 
int main(){
    char str[31];
    int  num[31];
    int res[2000];
 
while(scanf("%s",str)!=EOF){  //字符串转数字
    for(int i=0;i<strlen(str);i++)
        num[i]=str[i]-'0';
 
int size=strlen(str);
int index=0;//结果数组游标
 
for(int i=0;i<size;){ //控制被除数开始位置
    int temp=0,remain=0;//余数
    for(int j=i;j<size;j++){ //控制除法运算,竖式除法,从头往后每一位依次作除法
        temp=(10*remain+num[j])%2;
        num[j]=(10*remain+num[j])/2;
        remain=temp;
    }
    res[index]=remain;
    index++;
    while(num[i]==0) //从第一个非0开始
        i++;
    }
 
        for(int i=index-1;i>=0;i--) //逆序输出
        {
            printf("%d",res[i]);
        }
 
            printf("\n");
 
    }
}

五.大数阶乘

#include <stdio.h>
int main() {
	int a[20001];//储存每一位所得到的数
	int temp,digit,n,i,j=0;//temp每次的得数   digit每次得数的位数
	scanf("%d",&n);
	a[0]=1;//从1开始乘
	digit=1;//位数从第一位开始
	for(i=2; i<=n; i++) {
		int num=0;
		for(j=0; j<digit; j++) {
			temp=a[j]*i+num;//将一个数的每一位数都分别乘以i,
			a[j]=temp%10;//将一个数的每一位数利用数组进行储存
			num=temp/10;
		}
		while(num) { //判断退出循环后,num的值是否为0
			a[digit]=num%10;//继续储存
			num=num/10;
			digit++;
		}
	}
	for(i=digit-1; i>=0; i--) //倒序输出每一位
		printf("%d",a[i]);
	printf("\n");
	return 0;
}
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值