初见 | 基础算法 | 高精度
更改
2021.2.6
更改了文末统计字数的方式
1 高精度算法
正经的定义是:
高精度算法(High Accuracy Algorithm)是处理大数字的数学计算方法。在一般的科学计算中,会经常算到小数点后几百位或者更多,当然也可能是几千亿几百亿的大数字。一般这类数字我们统称为高精 度数,高精度算法是用计算机对于超大数据的一种模拟加,减,乘,除,乘方,阶乘,开方等运算。对于非常庞大的数字无法在计算机中正常存储,于是,将这个数字拆开,拆成一位一位的,或者是四位四位的存储到一个数组中, 用一个数组去表示一个数字,这样这个数字就被称为是高精度数。高精度算法就是能处理高精度数各种运算的算法,但又因其特殊性,故从普通数的算法中分离,自成一家。
但是,作为一个刚入门的,我个人的理解就是:
用数组以按照数位拆分一个大于double/long long等范围的数据,然后进行处理。
至于好处,我认为,就是对于超大(高精度)数据的使用
(当然肯定比处理一般的数据麻烦一点,要不然单列出来学干嘛)
2 高精度加法
①算法分析
上来一看,这个数据被拆分成了一个个位,作为计算能力非常之差的憨批,我很容易就想到了早在小学就学习过的最基础的加法计算方法:列 竖 式
(图源度娘 相信大家都对这陌生了,但是我两位数加两位数天天用)
这个的基础就是两个数对应的每个数位相加,但是这样就涉及一个问题:进 位
不过这个还是挺简单的,两个一位数相加最多也就进1,于是我们马上就可以愉快地开始用代码实现了。
②代码实现
首先是理一下思路:
- 录入数据:用字符串/字符数组接收,再逆序存到int/float/double数组,以便于计算
(这个地方倒序的原因是如果牵扯到进位,正序的话进位,下标要-1,要是第一个进位的话......那数组会非常的开心,我也是。于是用倒序,进位的时候下标+1,只要数组开的够长就好了,最后逆序输出就好了) - 进行加法运算:根据刚才算法分析中的用while循环完成就可以了(
虽然我个人比较喜欢用for,但是那样有麻烦了亿点) - 输出数据:因为是逆序输入,所以逆序输出。
以下是代码
害怕自己以后看不懂所以加了很多很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,非常简单
②代码实现
还是理一下思路:
- 录入数据:用字符串/字符数组接收,再逆序存到int/float/double数组,以便于计算
- 进行减法运算:
- 判断被减数是否小于减数,如果是的话交换顺序并先输出一个负号
- while+判断完成算法分析中分析到的借位问题
- 输出数据:因为是逆序输入,所以逆序输出。
(应该没有人发现我是复制的)
以下是代码
很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的方法来完成计算进位的多少。
实际上分析完这些,乘法也没有难很多。
②代码实现
-
输入数据,和之前一样。
-
进行乘法运算
- 得到当前的数据,即本次乘积+上次计算结果(最初默认为0)+进位(默认为0)
- 用本次数据求出本次产生进位和本次数据最终结果(即%10)
-
逆序输出,和之前一样
这次分析的多了点,上代码
多理解还是很有用的(至少对我来说),理解了加和减,乘法也能触类旁通的亚子
#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字符