关于高精度计算
我又来啦!!!
今天学了高精(别问我,我不会python),为了别让我的脑子忘了,还是记录一下吧
首先,要明确高精度是啥??为啥要用高精度??高精能解决啥问题??
其实,不难理解,高精度算法,属于处理大数字的数学计算方法。在一般的科学计算中,会经常要求算到 小数点后几百位或者更多,当然也可能是几千亿几百亿的大数字。一般这类数字我们统称为 高精度数。也只有用高精度才能解决超级大的数的运算;
那么我们进入正题,关于高精度的实现:
首先说说他的思想吧,其实就是把我们因太大而无法处理的数字变成字符数组(串),通过相同位运算的思想记录每一位,判断是否要进位后输出进位后输出。
我们分为以下几种情况讨论:1.高精+高精,2.高精-高精,3.高精*单精,4.高精*高精;(5.高精除法)
一、关于高精加高精
首先输入输出,就是用字符数组(个人偏好)把每一位数字记录下来,输出也差不过,就是再把倒序输入的在倒序输出就可以了
1 #include<iostream> 2 #include<algorithm> 3 using namespace std; 4 char a[...]; 5 string s; 6 int main() 7 { 8 cin>>s; 9 int len=s.length(); 10 for(int i=0;i<len;i++) 11 { 12 a[i]=s[len-1-i]-'0';//a数组记录下这个高精数,这里要用倒序存储,便于以后关于进位等问题 13 } 14 }
那我们可以开始将两数(组)相加了,这里倒序还有个好处,就是方便排位子,这样个位就能跟另一个数的个位相加,也就是对应数位就能对应相加了。
其实也就是这样的
a[7] a[6] a[5] a[4] a[3] a[2] a[1] a[0]
+ 0 0 b[5] b[4] b[3] b[2] b[1] b[0]
————————————————————
c[7] c[6] c[5] c[4] c[3] c[2] c[1] c[0]
然而,光这样肯定是不行的,肯定还会有进位的问题,那我们怎么解决呢?
那我们就逢十进一呗(说了好像没说),其实就是让a[i+1]=a[i+1]+a[i]/10 , a[i]=a[i]%10
还有一个问题,就是如果最后一位也要进位(也可能不进位)咋办?我只要判断最前面的数字是不是零,如果不是(也就是1),那就再加一位数字,具体代码如下
1 #include<iostream> 2 #include<cstring> 3 #define ll long long 4 using namespace std; 5 ll a[...],b[...],c[...]; 6 int main() 7 { 8 string a1,b1; 9 cin>>a1>>b1;//cin只读一行 10 ll la=a1.length(),lb=b1.length(); 11 for(ll i=0;i<la;i++) 12 { 13 a[i]=a1[la-1-i]-'0';//倒序输入 14 } 15 for(ll i=0;i<lb;i++) 16 { 17 b[i]=b1[lb-1-i]-'0';//倒序输入 18 } 19 ll lc=la>lb? la:lb;//最后的结果长度就为lc 20 for(int i=0;i<lc;i++) 21 { 22 c[i]=a[i]+b[i]; 23 } 24 for(int i=0;i<lc;i++) 25 { 26 c[i+1]=c[i+1]+c[i]/10; 27 c[i]=c[i]%10; 28 } 29 if(c[lc]>0) lc++;//如果最后一位也要进位,就再加一位 30 //不在第18行就加一位是因为我不知道是否要进位 31 for(int i=lc-1;i>=0;i--)//因为倒序输入,所以倒序输出 32 { 33 cout<<c[i]; 34 } 35 return 0; 36 }
做一道题练练?
关于斐波那契数列,输入n,输出第n项的值(3<=n<=1000)
这一看就要用高精度·,毕竟第49项就已经有7778742049那么大了
#include<iostream> #include<cstring> using namespace std; int a[1001],b[1001],c[1001]; int main() { int n,la,lb,lc; cin>>n; a[0]=1; la=1; b[0]=1; lb=1; for(int k=3;k<=n;k++) { lc=lb; for(int i=0;i<lc;i++) { c[i]=a[i]+b[i]; } for(int i=0;i<lc;i++) { c[i+1]=c[i+1]+c[i]/10; c[i]=c[i]%10; } if(c[lc]>0) { lc++; } la=lb; lb=lc; memcpy(a,b,1000*sizeof(int));//内存拷贝函数 memcpy(b,c,1000*sizeof(int));//包含在<cstring> } for(int i=lc-1;i>=0;i--) { cout<<c[i]; } return 0; }
这里,memcpy( a,b,sizeof())相当于b=a :把a的全部都给b。这里相当于fib( )的逐步递推作用
二、高精减高精
首先我们还是读入两个数组,值得注意的是,我们尽量让大的减小的(比较方便),如果是小的减大的,我们交换一下值,再加一个负号就好
其实个人看来,高精减和高精加差的不多,就是把进位改成借位就好
怎么借位呢?
我们判断a[i]是否大于b[i](a-b),如果大于,就不用借位,如果小于,我们就向前一位借1,这一位再加10即可,也就是
1 for (int i=0;i<la;i++) 2 { 3 if (a[i]<b[i]) 4 { 5 a[i+1]--; a[i]+=10; 6 } 7 a[i]=a[i]-b[i]; 8 }
还有就是需要把最高位的0去掉,最后输出就是
1 while (a[la]==0&&la>0) la--;
2 for (int i=la;i>=0;i--) cout<<a[i];
综合一下,代码如下
1 #include<iostream> 2 #include<cstring> 3 #define ll long long 4 using namespace std; 5 ll a[...],b[...]; 6 int main() 7 { 8 string a1,b1; 9 cin>>a1>>b1;//cin只读一行 10 ll la=a1.length(),lb=b1.length(); 11 if(a1==b1)//如果这两个字符串相同 12 { 13 cout<<0; 14 return 0; 15 } 16 if(la<lb||la==lb&&a1<b1)//判断谁大谁小并交换 17 { 18 swap(a1,b1); 19 swap(la,lb); 20 cout<<"-"; 21 } 22 for(ll i=0;i<la;i++) 23 { 24 a[i]=a1[la-1-i]-'0';//倒序输入 25 } 26 for(ll i=0;i<lb;i++) 27 { 28 b[i]=b1[lb-1-i]-'0';//倒序输入 29 } 30 for (ll i=0;i<la;i++)//不够要借位 31 { 32 if(a[i]<b[i]) 33 { 34 a[i+1]--; 35 a[i]+=10; 36 } 37 a[i]=a[i]-b[i]; 38 } 39 while(la>0&&a[la]==0) 40 { 41 la--;//处理前面的0 42 } 43 for(ll i=la;i>=0;i--) 44 { 45 cout<<a[i]; 46 } 47 return 0; 48 }
三、高精乘单精
可以简单的想一下小学学过的列式乘法
其实就是让单精数乘高精数的每一位,并记录下来,最后再进位就好了,值得注意的是,最后一位数字可能会进很多位,所以要用个循环来解决
1 #include<iostream> 2 #include<cstring> 3 #define ll long long 4 using namespace std; 5 int main(){ 6 ll la,a[...]={0},m; 7 int b; 8 string a1; 9 cin>>a1>>b; 10 la=a1.length(); 11 for(ll i=0;i<la;i++) 12 { 13 a[i]=a1[la-1-i]-'0'; 14 } 15 for(ll i=0;i<la;i++) 16 { 17 a[i]=a[i]*b; 18 } 19 for(ll i=0;i<la;i++) 20 { 21 a[i+1]=a[i+1]+a[i]/10; 22 a[i]=a[i]%10; 23 } 24 m=a[la];//我把m作为最高位 25 while(m>0)//处理最后一位的进位问题 26 { 27 a[la]=m%10; 28 m=m/10; 29 ++la; 30 } 31 for(ll i=la-1;i>=0;i--) 32 { 33 cout<<a[i]; 34 } 35 return 0; 36 }
四、高精乘高精
其实跟单精乘高精思想差不多,我们找出位数小一点的高精数,把它分为多个数字,让他们这些数字挨个相乘,最后再整理一下就OK了
还有一点值得一提的是,两数相乘的位数就是两数的位数之和或再减1
但是,最后的整理工作也是有点繁琐的,因为他们是错位相乘,针对这种情况,我们找到一个规律·
不难发现,错位相乘再相加,同一行的数是有特点的,也就是上面所说的:
c[i+j]+=a[i]*b[i],即a的下标和b的下标之和(即i+j)对应的就是就是我们最后要输出的第几位,也就是c的下标
1 #include<iostream> 2 #include<cstring> 3 #define ll long long 4 using namespace std; 5 int a[...],b[...],c[...]; 6 int main() 7 { 8 string a1,b1; 9 ll la,lb,lc; 10 cin>>a1>>b1; 11 la=a1.length(),lb=b1.length(); 12 lc=la+lb; 13 for(ll i=0;i<la;i++) 14 { 15 a[i]=a1[la-1-i]-'0'; 16 } 17 for(ll i=0;i<lb;i++) 18 { 19 b[i]=b1[lb-1-i]-'0'; 20 } 21 for(ll i=0;i<la;i++) 22 { 23 for(ll j=0;j<lb;j++){ 24 c[i+j]+=a[i]+b[j];//有很多种组合方式,把他们加起来 25 } 26 } 27 for(ll i=0;i<lc;i++) 28 { 29 c[i+1]+=c[i]/10; 30 c[i]=c[i]%10; 31 } 32 if(c[lc-1]==0) lc--;//处理最高位0 33 for(ll i=lc-1;i>=0;i--) 34 { 35 cout<<c[i]; 36 } 37 return 0; 38 }
五、高精除
这里有代码(只有代码)可以推导一下(这里只写了高精除以单精)
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std; 5 int main() 6 { 7 char a1[5000]; 8 int a[5000]={0},c[5000]={0}; 9 long long la,lc,b,r=0,i; 10 cin>>a1>>b; 11 la=strlen(a1); 12 for(i=0;i<la;i++) 13 { 14 a[i]=a1[i]-48; 15 } 16 for(i=0;i<la;i++) 17 18 { 19 c[i]=(10*r+a[i])/b; 20 r=(r*10+a[i])%b; 21 } 22 lc=0; 23 while(c[lc]==0&&lc<la-1) 24 { 25 lc++; 26 } 27 for(i=lc;i<la;i--) 28 { 29 cout<<c[i]; 30 } 31 return 0; 32 }
-----“其实只要思想到位了,就不算多难以理解了对吧?”
-----“em...我没有思想,甚至没有脑子.....”
就先讲到这里吧,再见
2022/3/13