详细写高精度算法

本文介绍了计算机中的浮点精度问题,特别是高精度计算的概念,以高精度加法和乘法为例,通过C语言代码展示了如何处理大整数运算,包括进位、字符串转换和高精度算法的实现过程。
摘要由CSDN通过智能技术生成

bf1b78762a8247ef9e26b0fbc7b52130.png

一:定义(我的蒟蒻见解)

        首先我们来介绍何为精度,精度顾名思义表示观测值与真值的接近程度,这里的精度缺失更多指的是浮点精度的缺失,我们知道设立浮点数的目的是建立计算机中存储的一个零一序列到抽象数学中的一个数字的一对一的映射。但是数学中是存在无限小数的,这意味着我们需要无限个互不相同的零一序列(一对一的映射)才能表示。但是,现实的计算机中,我们没有无限的内存,而有限的内存就意味着我们只能表示有限的数字。所以就导致任何浮点数标准都一定存在大量被舍弃的数字,大量在标准中不能精确表示的数字,损失了精度。这也是精度名词的由来

        那么何为高精度,顾名思义就是很大的整数(如果理解成上面的内容就是小数点后好多好多位,理论上不可能是无限位,所以就是好多好多,huaji)指当运算过程,产生一些超出整形(int)甚至(longlong)的数据的时候,计算机不能使用内置的运算器进行运算。所以这时候就引入了一种算法。下面我们就来探究探究。

二.高精度算法

        经常算数的孩子肯定知道1+1等于吧,1+1=2这个二级结论呢大家熟记于心,那么接下来我们就以985+211的算数为例,表述高精度加法。

bf0edba07dd94d26af79afe21adba429.png

        可以看到985+211的算法我们是这么算的如果把第一数组存进arr1,第二数存进arr2,那么利用每每一位的相加得到arr3不就是答案嘛,不过当我们计算10000000000000000000+122222222222的时候int 的数组就容易爆,因为int 存储一个一个的数,所以我们可以用到字符串嘛,创建arr1【100000】和arr【100000】的数组用于接收,括号的内容表示位数,利用scanf输入,然后我们再去转化成数字存在a,b两个int数组(利用减去‘0’或者48),但是我们知道是几位数加几位数腻,所以我们利用strlen计算位数sz1,sz2,再想想9+2是不是11进位了,三位数加三位数不一定是三位数,所以我们要判断最大的位数是多少,然后加一判断加完后这个位数的数字是不是为0,如果是则是三位数(以985+211为例),否则需要进位(因为打印我们可能打欠嘛)

好了有点虚是吧,看代码吧

1.1高精度加法代码(C)

#include<stdio.h>
#include<string.h>
int max(int a,int b)
{
	if(a>b)
		return a;
	else
		return b;
}
int main()
{
	char arr1[10000],arr2[10000];//用来接收的数组
	int a[10000],b[10000],c[10000];//ab都是加数,c是ans用来接收答案
	scanf("%s",arr1);
	scanf("%s",arr2);
	int sz1=strlen(arr1);//计算位数
	int sz2=strlen(arr2);
	int sz3=max(sz1,sz2)+1;//进位的计算
	for(int i=0;i<sz1;i++)//转化,转化为数字
	{
		a[sz1-i]=arr1[i]-'0';
	}
	for(int i=0;i<sz2;i++)
	{
		b[sz2-i]=arr2[i]-'0';
	}
	//高精度加法的主体本质上是模拟计算985+211,可以体会体会,我的建议熟记于心
	for(int i=0;i<=sz3;i++)
	{
		c[i]=c[i]+a[i]+b[i];
		c[i+1]=c[i]/10;
		c[i]%=10;
	}
	if(c[sz3]==0&&sz3>0)	sz3--;
	for(int i=sz3;i>0;i--)
	{
		printf("%d",c[i]);
	}
	return 0;
}

c34e2142d7af4dc6bef323c8e61f4d89.png

这里小小总结一波:

不难发现啊,高精度算法是有可背套路的

第一步创建对应运算法则函数

第二步:创建两个接收字符数组和三个运算数组,第三个作为ans

第三步:接收并计算长度,然后全部逆序成为数值

第四步:判断进位

第五步:打印

习题1:

不妨尝试P1601 A+B Problem(高精) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

2.1高精度减法

好了,我们现在进军减法,其实也是一样的985-211,一位一位的减,但是就是如果被减数的某一位数小于减数的时候我们就该进一,所以他就是

for(int i=0;i<=sz3;i++)
{
    if(a[i]<b[i])
    {
        a[i+1]--;//借一
        a[i]+=10;//a[i]=a[i]+10;
    }
    
    c[i]=a[i]-b[i];
}

ok吧理解了加法,减法洒洒水啦

所以上代码?

等等等等等等,你知道的,减法嘛,会有负数的啊

所以我们需要一个判断函数,判断一下是否为负数,如果是,我们只要把小减大换成大减小,然后加个符号即可。

2.2行动(高精度减法代码)(C)(利用总结的内容)

# include <stdio.h>
# include <string.h>
 
int compare(char a1[10090],char a2[10090],int sz1,int sz2)
{
	if(sz1>sz2)    return 1;
	else if(sz1<sz2)   return 0;
	else 
	{
		for(int i=0;i<sz1;i++)
		{
			if(a1[i]>a2[i])   return 1;
			else if(a1[i]<a2[i])    return 0;
			else   continue;
  		}
	}
}
int main ()
{
	char a1[10090]="123",a2[10090]="123";
	int b1[10090]={0},b2[10090]={0},b3[10090]={0};
	scanf("%s",&a1);scanf("%s",&a2);
	int sz1=strlen(a1);
	int sz2=strlen(a2);
 
 
 
    //比较大小
	int m=compare(a1,a2,sz1,sz2);
 
 
    //判断是否改变两个的位置
	if(m==0)
	{
		char q[10090]="123";
		strcpy(q,a1);
		strcpy(a1,a2);
		strcpy(a2,q);
	}
	int sz3=strlen(a1);
	int sz4=strlen(a2);
 
 
 
    //将字符串变为数组形式
	for(int i=0;i<sz3;i++)
	{
		b1[sz3-i]=a1[i]-'0';
	}
	for(int i=0;i<sz4;i++)
	{
		b2[sz4-i]=a2[i]-'0';
	}
 
 
    //高精度减法主体
	for(int i=1;i<=sz3;i++)
	{
		if(b1[i]<b2[i])
		{
			b1[i+1]--;
			b1[i]=b1[i]+10;
		}
		b3[i]=b1[i]-b2[i];
	} 
 
 
 
    //判断是否为0
	while(b3[sz3]==0&&sz3>1)    sz3--;
 
    
    //判断是否要变为负数
	if(m==0)   printf("-");
 
    //输出0
	for(int i=sz3;i>0;i--)
	{
		printf("%d",b3[i]);
	}
	return 0;
}
 

自己写代码的思路

可能在思路大家看得不是很清晰

其实可以这么写

在写之前把套路先打出来

至少再未了解主体之前,我们可以这么写(C++)

#include<iostream>
#include<cstring>
using namespace std;
int compare()//创建一个函数比较大小
{	
	
}
int main()
{
	char a[100000],b[100000];//1
	int a1[100000],a2[100000],ans[100000];//2
	int s1,s2,s3;//3
	cin>>a;cin>>b;
	s1=strlen(a);s2=strlen(b);//3
	for(int i=0;i<s1;i++)//4
		a1[s1-i]=a[i]-'0';
	for(int i=0;i<s2;i++)
		a2[s2-i]=b[i]-'0';
	if(s3>0&&ans[s3]==0)	s3--;//5
	for(int i=s3;i>0;i--)//6
		cout<<ans[i];
	
}

接下来就是怎么完善的问题了,compare是比较函数而减法主体又在上方,故直接写

#include<iostream>
#include<cstring>
using namespace std;
int compare(char a[10090],char b[10090],int s1,int s2)//创建一个函数比较大小,以1为正,0为负//至于为什么不先转化为数值是因为现在是正序
{	
	if(s1>s2)	return 1;//比较位数
	else if(s1<s2)	return 0;
	else //再逐个比较
	{
		for(int i=0;i<s1;i++){
			if(a[i]>b[i])	return 1;
			else if(a[i]<b[i])	return 0;
			else continue;
		}
	}
}
int main()
{
	char a[10090],b[10090];//1
	int a1[10090],a2[10090],ans[10090];//2
	int s1,s2;//3
	cin>>a;cin>>b;
	s1=strlen(a);s2=strlen(b);//3

	int p=compare(a,b,s1,s2);
	
	if(p==0)
	{
		char q[10090]="123";
		strcpy(q,a);
		strcpy(a,b);
		strcpy(b,q);
		cout<<'-';
	}
	int s3=strlen(a);
	int s4=strlen(b);
	for(int i=0;i<s3;i++)//4
		a1[s3-i]=a[i]-'0';
	for(int i=0;i<s4;i++)
		a2[s4-i]=b[i]-'0';
	for(int i=1;i<=s3;i++)
	{
		if(a1[i]<a2[i])
		{
			a1[i+1]--;
			a1[i]+=10;
		}
		ans[i]=a1[i]-a2[i];
	}
	
	while(s3>0&&ans[s3]==0)	s3--;//5
	for(int i=s3;i>0;i--)//6
		cout<<ans[i];
	return 0;
}

c9c7f2b4595342fb891ae00f74bf87be.png

3.高精度乘法

万变不离其宗!无非主体不同,所以这里先跟大家讨论主体的研究

以985*211(没错,跟他俩过不去了)为例(忽略我的丑字

这里注意我们先把数组逆过来

#include<iostream>
#include<cstring>
using namespace std;
int main()
{
	char a[100000],b[100000];
	cin>>a;cin>>b;
	s1=strlen(a);s2=strlen(b);
	for(int i=0;i<s1;i++)
		a1[s1-i]=a[i]-'0';
	for(int i=0;i<s2;i++)
		a2[s2-i]=b[i]-'0'
	
}

前面有讲就不多解释!

da5984d1fb4e4247a238f5c4e952eaa1.jpeg

其实我们在算乘法的时候呀就是每隔一位进10,我把零补全了是不是更加明显了呢!

那么就有以下推论代码

for(i=1;i<=sz1;i++)
    for(j=1;j<=sz2;j++)
        c[i+j-1]+=a[j]*b[i];

内外循环表示我们乘法的过程,用211的1去分别乘985中的9,8,5,这里为什么写成c[i+j-1]是因为

4014fa075ddf48a6a7ab35caa9fdaa7a.jpeg

c数组就存放这些数据,然后给他们标个号

c7986c50db154275be5e6aef58379eca.jpeg

然后上面就是j下面就是i,我们进行枚举

for(i=1;i<=sz1;i++)
    for(j=1;j<=sz2;j++)
        c[i+j-1]+=a[j]*b[i];

发现c[i+j-1]就是

c19bb71698a9485aa00b7019b2d4295e.jpeg

怎么样好理解了吧存好之后我们就可以相加了,现在这一步不就是加法嘛,大于10%10,前一位加1呗(不过有可能超过10,20所以用以下代码)

for(i=1;i<sz1+sz2;i++)
{
     if(c[i]>9)
	   {
		    c[i+1]+=c[i]/10;
		    c[i]%=10;
       }
}

为何i<sz1+sz2那是因为,不是这个就小学生了吧,二位数乘二位数最小3位数然后四位数嘛,那sz1位数*sz2位数不就是最小sz1+sz2-1位数吗我们知道最高位会在上一次的循环中被进位,所以就是最高位减一次循环呗

然后固定套路即可

高精度乘法代码(C++)

//高精度乘法
#include<iostream>
#include<cstring>
using namespace std;
int main()
{
	char a[100000],b[100000];
	int c[10000],d[10000],ans[10000];
	cin>>a;cin>>b;
	int s1=strlen(a);int s2=strlen(b);
	for(int i=0;i<=s1;i++)
		c[s1-i]=a[i]-'0';
	for(int i=0;i<s2;i++)
		d[s1-i]=b[i]-'0';
	for(int i=1;i<=s2;i++)
		for(int j=1;j<=s1;j++ )
			ans[i+j-1]+=c[j]*d[i];
	for(int i=1;i<s1+s2;i++)
	{
		if(ans[i]>9)
		{
			ans[i+1]+=ans[i]/10;
			ans[i]%=10;
		}
	}
	int s3=s1+s2;
	while(ans[s3]==0&&s3>1)	s3--;
	for(int i=s3;i>=1;i--)
		cout<<ans[i];
	return 0;
}

69063171e6c74f6e94f6a610b61ab358.png

习题2.

P1303 A*B Problem - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

 

除法待续.............

 

  • 16
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值