初见 | 基础算法 | 高精度

更改
2021.2.6
更改了文末统计字数的方式

1 高精度算法

正经的定义是:

高精度算法(High Accuracy Algorithm)是处理大数字的数学计算方法。在一般的科学计算中,会经常算到小数点后几百位或者更多,当然也可能是几千亿几百亿的大数字。一般这类数字我们统称为高精 度数,高精度算法是用计算机对于超大数据的一种模拟加,减,乘,除,乘方,阶乘,开方等运算。对于非常庞大的数字无法在计算机中正常存储,于是,将这个数字拆开,拆成一位一位的,或者是四位四位的存储到一个数组中, 用一个数组去表示一个数字,这样这个数字就被称为是高精度数。高精度算法就是能处理高精度数各种运算的算法,但又因其特殊性,故从普通数的算法中分离,自成一家。

但是,作为一个刚入门的,我个人的理解就是

用数组以按照数位拆分一个大于double/long long等范围的数据,然后进行处理。
至于好处,我认为,就是对于超大(高精度)数据的使用
当然肯定比处理一般的数据麻烦一点,要不然单列出来学干嘛

2 高精度加法

①算法分析

上来一看,这个数据被拆分成了一个个位,作为计算能力非常之差的憨批,我很容易就想到了早在小学就学习过的最基础的加法计算方法:列 竖 式

(图源度娘 相信大家都对这陌生了,但是我两位数加两位数天天用
这个的基础就是两个数对应的每个数位相加,但是这样就涉及一个问题:进 位
不过这个还是挺简单的,两个一位数相加最多也就进1,于是我们马上就可以愉快地开始用代码实现了。

②代码实现

首先是理一下思路:

  1. 录入数据:用字符串/字符数组接收,再逆序存到int/float/double数组,以便于计算
    (这个地方倒序的原因是如果牵扯到进位,正序的话进位,下标要-1,要是第一个进位的话......那数组会非常的开心,我也是。于是用倒序,进位的时候下标+1,只要数组开的够长就好了,最后逆序输出就好了)
  2. 进行加法运算:根据刚才算法分析中的用while循环完成就可以了(虽然我个人比较喜欢用for,但是那样有麻烦了亿点
  3. 输出数据:因为是逆序输入,所以逆序输出。

以下是代码

害怕自己以后看不懂所以加了很多很Dai的注释

//高精度加法 
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
int a[201],b[201],c[201];//三个int数组来储存数据进行运算 
int main()
{
	char na[201],nb[201];
	//两个用来接收数据的字符串(字符数组) 
	gets(na);
	gets(nb); 
	//接收数据
	int la=strlen(na);
	int lb=strlen(nb);
	//计算长度 
	for(int i=0;i<la;i++){
		a[la-i]=na[i]-'0';
	}
	for(int i=0;i<lb;i++){
	b[lb-i]=nb[i]-'0';
	}
	//将数据倒序存入 
	int lc=1,x=0;
	//这里的x是进位的数 ,初始为0 
	while(lc<=la||lc<=lb){//这里算到不包括最后一次进位的最高位
		c[lc]=a[lc]+b[lc]+x;//这里是a+b+进位数 
		x=c[lc]/10;//让进位数等于结果的十位 
		c[lc]%=10;//让此位结果变为初步结果的个位 
		lc++;//要计算的位数+1 
	}		
	//加法计算的核心,这里算到不包括最后一次进位的最高位 
	c[lc]=x;
	//如果最后一次计算有进位数,让进位数等于新高位
	if(c[lc]==0)
		lc--;
	//如果没有进位,那么把lc--以便输出
	for(int i=lc;i>0;i--)
	cout<<c[i];
	cout<<endl;
	//输出,收尾(	
	return 0;
}

学的时候没觉出来,自己敲的时候发现还是得注意一些小细节的,比如要判断最后的lc这一位是否等于0
当然应该是我太菜了才会把这些东西当成细节

3 高精度减法

①算法分析

和加法一样,用列 竖 式的方法,这里只需要考虑不 够 减 要 借 位的问题。

(图源某小学课件 又是大家非常陌生而我每次两位数减一位数的时候都要用的东西
那么具体的解决方法就是不够减的时候本位+10,上一位-1,非常简单

②代码实现

还是理一下思路:

  1. 录入数据:用字符串/字符数组接收,再逆序存到int/float/double数组,以便于计算
  2. 进行减法运算
    1. 判断被减数是否小于减数,如果是的话交换顺序并先输出一个负号
    2. while+判断完成算法分析中分析到的借位问题
  3. 输出数据:因为是逆序输入,所以逆序输出。
    应该没有人发现我是复制的

以下是代码

很Dai的注释更多了,各位看着笑笑就好

//高精度减法 
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int a1[256]={0},a2[256]={0},a[256]={0};
//声明来存数的数组 
int main()
{
	char n1[256],n2[256],m[256];//用于接收数据的字符串(字符数组),以及m这个后面用来交换用的中间量 
	gets(n1); 
	gets(n2);
	//接收数据,这里默认是n1-n2
	if((strlen(n1)<strlen(n2))||(strlen(n1)==strlen(n2)&&strcmp(n1,n2)<0))
	//判断被减数是否小于减数
	//第一个判断条件很好懂,是被减数的位数少于减数
	//第二个是当两者位数相同时,使用strcmp进行比较
	/*strcmp:
	
	strcmp(n1,n2)就是从n1[0]和n2[0]开始比较(以ASCⅡ里的顺序来比较)
	若前者大,则返回大于0的数 
	反之,返回小于0的数
	若相同,则返回0 
	
	(这样理解,可能不准确,于是去问了下度娘:)
	
	strcmp函数是string compare(字符串比较)的缩写,
	用于比较两个字符串并根据比较结果返回整数。
	基本形式为strcmp(str1,str2),若str1=str2,则返回零;
	str1<str2,则返回负数;若str1>str2,则返回正数。
	(嘛,还是差不多的) 
	*/ 
	{
		strcpy(m,n1);
		strcpy(n1,n2);
		strcpy(n2,m);
		/*
		strcpy就是用后者赋值给前者,复制贴贴 
		
		怕自己这个蒟蒻看不懂于是又去了一趟度娘(蒟蒻的日常) 
		
		strcpy,即string copy(字符串复制)的缩写。
		strcpy是C++语言的一个标准函数 ,
		strcpy把含有'\0'结束符的字符串复制到另一个地址空间,
		返回值的类型为char*。
		*/ 
		cout<<"-";//先输出一个负号,这样后面就可以安心的运算了(蒟蒻发言) 
	} 
	int l1=strlen(n1),l2=strlen(n2);
	
	for(int i=0;i<l1;i++)
	a1[l1-i]=int(n1[i]-'0');
	for(int i=0;i<l2;i++)
	a2[l2-i]=int(n2[i]-'0');
	//将数据存入int类型数组,不用多说
	int x=1;
	//x代表要计算的是第几位数 
	while(x<=l1||x<=l2)
	{
		if(a1[x]<a2[x])
		{
			a1[x]+=10;
			a1[x+1]--;
		}//这里的意思就是如果不够减就向高位借10,高一位-1,此位+10 
		a[x]=a1[x]-a2[x];
		x++;	
	}
	//减法核心程序
	int l=x; 
	while(a[l]==0&&l>1)
	l--;
	//如果最高位是0,那么x--以免最高位是0 
	for(int i=l;i>=1;i--)
	cout<<a[i];
	cout<<endl;
	//收尾(来自蒟蒻的喜悦,前几次没成功竟然是因为最后输出的时候打成i++了,果然是Dai) 
	return 0;
 } 

也就我能把这么基础的东西打75行了,自卑

4 高精度乘法

①算法分析

个人认为这是本次学习最难的地方了(毕竟菜)
但是如果前面的高精度加减法没有问题的话,这个理解起来也比较简单。
和前面的思路差不多,我还是从列 竖 式入手

相信各位现在或许还时常能见到这个东西。
我们根据竖式来算的话,就是用a[i]去乘b[1],b[2],b[3]...,然后每次i在变化的时候需要错位,这个还是比较好想的,让接收答案的数组c的下标是c[内层循环变量+外层循环变量-1]即可。(当然我最一开始想的时候没太想通,于是举了个例子,在这次的高精度学习之中,我个人觉得举例子是个非常助于理解的方法)
还有一点就是,在进位时的问题,这个也比较好解决。参照刚才加法的方法,可以声明一个变量x来代表每次进位的数,只不过这里不只是进位1了,而是最多进位9,我们还是可以用当前结果/10的方法来完成计算进位的多少。
实际上分析完这些,乘法也没有难很多。

②代码实现

  1. 输入数据,和之前一样。

  2. 进行乘法运算

    1. 得到当前的数据,即本次乘积+上次计算结果(最初默认为0)+进位(默认为0)
    2. 用本次数据求出本次产生进位本次数据最终结果(即%10)
  3. 逆序输出,和之前一样

这次分析的多了点,上代码

多理解还是很有用的(至少对我来说),理解了加和减,乘法也能触类旁通的亚子

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int a[256],b[256],c[256000],x=0;
int main()
{
	char s1[256],s2[256];
	scanf("%s",s1);
	scanf("%s",s2);
	int l1=strlen(s1),l2=strlen(s2);
	for(int i=0;i<=l1-1;i++)
	a[l1-i]=s1[i]-'0';
	//没想到啊,查了15分钟错误,原来是这一行的s1[i]-0 打成了s1[i]=0 
	for(int i=0;i<=l2-1;i++)
	b[l2-i]=s2[i]-'0';
	//前边的就和高精度加减法一样 
	for(int i=1;i<=l1;i++)
	{
		x=0;
		for(int p=1;p<=l2;p++)
		{
			c[i+p-1]=c[i+p-1]+x+(a[i]*b[p]);
		  //上次处理的数+进位+当前乘积 
			x=c[i+p-1]/10;
			//x是进位数 
			c[p+i-1]%=10;
			//当前结果取余10 
		}
		c[l2+i]=x;
		//当内循环结束后,当前进位是下一位 
	}
	//这里是乘法核心程序,注解都在上面了,实际上结合高精度的加减法来说理解起来并不难 
	int l3=l1+l2;
	while(c[l3]==0&&l3>1)
	l3--;
	for(int i=l3;i>=1;i--)
	cout<<c[i];
	cout<<endl; 
	//收尾>o< 
	return 0;
}

5 总结

本次学习的新知识点

  • 对于高精度算法的进一步了解,以及了解其使用的范围
  • 掌握高精度算法的加减乘如何实现

本次学习的感悟

  • 要耐心理解,现在知识点的难度不和之前一样了

本次学习欠缺

  • 还没练题

End

2021.1.28
2915词
5555字符

posted @ 2021-01-28 14:34  HerikoDeltana  阅读(285)  评论(9编辑  收藏  举报