高精度
目录
高精度的出现是为了解决计算机所不能存储的数据范围。当数字超出了这个范围,我们必须用一些算法来模拟人们计算的过程。
高精度数字的存储方法
通常采用数组存储数字的每一位。要注意的是尽量采用倒序存储,这是为了考虑进位问题:进位可以直接插到后面。
A+B的问题
问题分析
高精度加法是一个最简单的高精度问题:
每一位相加的时候需要加三个量:
- A[i]----A第i位上的数字
- B[i]----B第i位上的数字
- t----从A[i]+B[i]上获得的进位
t从整除算出
ans[i]从取模算出
AC代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define in(x) scanf("%d",&x)
#define debug(x) cerr<<#x<<" : "<<x<<endl
using namespace std;
const int N=100005;
int a[N],b[N];
int main(){
string aa,bb;
cin>>aa>>bb;
for(int i=aa.length()-1,j=0;i>=0;i--,j++){
a[j]=aa[i]^48;
}
for(int i=bb.length()-1,j=0;i>=0;i--,j++){
b[j]=bb[i]^48;
}
int n=0;
int i=0;
int t=0;
while(i!=max(aa.length(),bb.length())){
int now=a[i]+b[i]+t;
a[i]=(now)%10;
t=now/10;
if(t&&i==max(aa.length(),bb.length())-1){
a[i+1]=t;
n++;
}
i++;
n++;
}
for(int i=n-1;i>=0;i--){
cout<<a[i];
}
cout<<endl;
return 0;
}
A-B问题
问题分析
与加法一样,减法要考虑三个量。
不过除去这三个数之外,要判断数的大小,若A<B,则让AB互换,输出“-”。(这里有个自己菜的坑,下意识认为数的大小可以用字典序比较。。。)
除去这个,还有一个最终答案先导0的问题。
这里先去除先导零,在输出答案。不过要注意的是,个位数也是先导零必须输出。
AC代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define debug(x) cerr<<#x<<" : "<<x<<endl
using namespace std;
const int N=100005;
int a[N],b[N];
bool cmp(string a,string b){
if(a.length()==b.length()){
for(int i=0;i<a.length();i++){
if(a[i]==b[i]){
continue;
}
return a[i]>b[i];
}
return true;
}
else{
return a.length()>b.length();
}
}
int main(){
bool pos=true;
string aa,bb;
cin>>aa>>bb;
if(!cmp(aa,bb)){
string t=aa;
aa=bb;
bb=t;
pos=false;
}
for(int i=aa.length()-1,j=0;i>=0;i--,j++){
a[j]=aa[i]^48;
}
for(int i=bb.length()-1,j=0;i>=0;i--,j++){
b[j]=bb[i]^48;
}
int len=max(aa.length(),bb.length());
int i=0;
int t=0;
int n=len;
while(i!=len){
if(a[i]-b[i]-t>=0){
a[i]=a[i]-b[i]-t;
t=0;
}
else{
a[i]=a[i]+10-b[i]-t;
t=1;
}
i++;
}
if(!pos){
cout<<"-";
}
int tag=0;
for(int i=n-1;i>=0;i--){
if(a[i]==0&&tag==0){
if(i==0){
cout<<0;
}
continue;
}
cout<<a[i];
tag=1;
}
cout<<endl;
return 0;
}
A*a问题(一个大整数乘一个普通整数)
问题分析
大整数各位 | 0 | 1 | 2 | 3 |
---|---|---|---|---|
整数 | 12 | |||
答案位 | 1 | 4 | 7 | 6 |
进位 | 1 | 2 | 3 | 0 |
上表是一个相乘的过程:
答案位的计算过程:(大整数位乘以整数+进位)%10
下一位进位计算过程:(大整数位乘以整数+进位)/ 10
!!同样与减法一样有先导0的问题;
AC代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define debug(x) cerr<<#x<<" : "<<x<<endl
using namespace std;
const int N=100010;
int a[N];
int main(){
string aa;
cin>>aa;
for(int i=aa.length()-1,j=0;i>=0;i--,j++){
a[j]=aa[i]^48;
}
int k;
cin>>k;
int n=0;
for(int i=0,t=0;i<aa.length();i++){
// debug(a[i]);
int tt=(a[i]*k+t);
// debug(tt);
a[i]=tt%10;
// debug(a[i]);
t=tt/10;
// debug(t);
if(i==aa.length()-1&&t){
a[i+1]=t;
n++;
}
n++;
}
int tag=0;
for(int i=n-1;i>=0;i--){
if(tag==0&&a[i]==0){
if(i==0){
cout<<0;
}
continue;
}
cout<<a[i];
tag=1;
}
cout<<endl;
return 0;
}
A/a问题(一个大整数除一个普通整数)
问题分析
太简单,回头再写
AC代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int N=100010;
int a[N];
int main(){
string aa;
cin>>aa;
for(int i=aa.length()-1,j=0;i>=0;i--,j++){
a[j]=aa[i]^48;
}
int k;
cin>>k;
int t=0;
for(int i=aa.length()-1;i>=0;i--){
int tt=a[i]+t*10;
a[i]=(tt)/k;
t=tt%k;
}
int tag=0;
for(int i=aa.length()-1;i>=0;i--){
if(tag==0&&a[i]==0){
if(i==0){
cout<<0;
}
continue;
}
cout<<a[i];
tag=1;
}
cout<<endl<<t<<endl;
return 0;
}
高精度乘高精度
思路分析
高精度乘以高精度利用分支法的思想,将每个数字分为前半部分和后半部分,假设数一为n位,数二位m位,即:
(
A
n
2
+
B
)
(A^\frac{n}{2}+B)
(A2n+B)
(
C
m
2
+
D
)
(C^\frac{m}{2}+D)
(C2m+D)
其相乘结果为:(暂记公式1)
(
A
n
2
C
m
2
+
B
D
)
+
(
A
n
2
D
+
C
m
2
B
)
(A^\frac{n}{2}C^\frac{m}{2}+BD)+(A^\frac{n}{2}D+C^\frac{m}{2}B)
(A2nC2m+BD)+(A2nD+C2mB)
此公式可以变形为:(暂记公式2)
2
(
A
n
2
C
m
2
+
B
D
)
+
(
A
n
2
−
B
)
(
C
m
2
−
D
)
2(A^\frac{n}{2}C^\frac{m}{2}+BD)+(A^\frac{n}{2}-B)(C^\frac{m}{2}-D)
2(A2nC2m+BD)+(A2n−B)(C2m−D)
我们可以想到,分治后会有以下几种情况:
- 两数字长度为1
- 其中只有以个数字长度为1
- 都不为1
且第二种情况还可以分为两种情况- 有进位
- 无进位
上代码-----------------------------------------------------------------------
代码
代码1(由公式1推导而来)
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
string multi(string A, string B); //计算大整数相乘
string Plus(string q, string w, string e, string r); //计算大整数相加
stringstream ss;
int main() {
string A, B;
while (cin >> A >> B) {
cout << multi(A, B) << endl;
}
return 0;
}
string multi(string A, string B) {
int len_A = A.length();
int len_B = B.length();
if (len_A == 1) {
if (len_B == 1) { //最基本的情况:A和B都是一位数,把A、B从string转为int(我这里用的stringstream),然后相乘后转回为string型return回去。
ss << A;
int a;
ss >> a;
ss.clear();
ss << B;
int b;
ss >> b;
ss.clear();
ss << b*a;
string str_out;
ss >> str_out;
ss.clear();
return str_out;
}
else { //A是个位数,B不是的情况下,按照分治的思想把B分开分别与A相乘。
string B1, B2;
B1 = B.substr(0, len_B / 2);
B2 = B.substr(len_B / 2);
string AB1 = multi(A, B1);
string AB2 = multi(A, B2);
if (AB2.length() > B2.length()) {
string str = AB2.substr(0, 1);
ss << str;
int ab2;
ss >> ab2;
ss.clear();
ss << AB1;
int ab1;
ss >> ab1;
ss.clear();
ss << ab1 + ab2;
ss >> AB1;
ss.clear();
return AB1 + AB2.substr(1);
}
else
return AB1 + AB2;
}
}
else {
if (len_B == 1) {? //B是个位数,A不是的情况与上述A是个位数B不是的情况相同。
string A1, A2;
A1 = A.substr(0, len_A / 2);
A2 = A.substr(len_A / 2);
string A1B = multi(A1, B);
string A2B = multi(A2, B);
if (A2B.length() > A2.length()) {
string str = A2B.substr(0, 1);
ss << str;
int a2b;
ss >> a2b;
ss.clear();
ss << A1B;
int a1b;
ss >> a1b;
ss.clear();
ss << a1b + a2b;
ss >> A1B;
ss.clear();
return A1B + A2B.substr(1);
}
else {
return A1B + A2B;
}
}
else { //A和B都不是个位数,就按照上述方法分治就可以了,只是为了最后相加的时候方便,把返回的四个部分都用0凑成了位数相同的。
string A1, A2, B1, B2;
A1 = A.substr(0, len_A / 2);
A2 = A.substr(len_A / 2);
B1 = B.substr(0, len_B / 2);
B2 = B.substr(len_B / 2);
string part1_ = multi(A1, B1);
string part1_0(A2.length()+B2.length(), '0');//1. 补零但没有补全
part1_ = part1_ + part1_0;
string part2_ = multi(A2, B2);
string part2_00(part1_.length() - part2_.length(), '0');// 2. 补1没补全的零
part2_ = part2_00 + part2_;
string part3_ = multi(A1, B2);
string part3_0(A2.length(), '0');
part3_ = part3_ + part3_0;
string part3_00(part1_.length() - part3_.length(), '0');
part3_ = part3_00 + part3_;
string part4_ = multi(A2, B1);
string part4_0(B2.length(), '0');
part4_ = part4_ + part4_0;
string part4_00(part1_.length() - part4_.length(), '0');
part4_ = part4_00 + part4_;
return Plus(part1_, part2_, part3_, part4_);
}
}
}
string Plus(string q, string w, string e, string r) { //大整数相加
int len_q = q.length();
string y, out;
int a, b, c, d;
for (int i = 1; i <= len_q; i++) {
ss << q.substr(len_q - i, 1);
ss >> a;
ss.clear();
ss << w.substr(len_q - i, 1);
ss >> b;
ss.clear();
ss << e.substr(len_q - i, 1);
ss >> c;
ss.clear();
ss << r.substr(len_q - i, 1);
ss >> d;
ss.clear();
ss << a + b + c + d;
ss >> y;
ss.clear();
if (i == 1)
out = y;
else if (out.length() > i - 1) {
ss << out.substr(0, 1);
ss >> a;
ss.clear();
ss << y;
ss >> b;
ss.clear();
ss << a + b;
ss >> y;
ss.clear();
out = y + out.substr(1);
}
else {
out = y + out;
}
}
return out;
}
代码2(由公式2推导而来)
此代码要比一复杂度低,因为其在计算过程中有几步的乘法计算是相同的,可以进行存值保存,减少计算次数。
且注意此代码与上面代码1同样都用到了前四种高精度计算,不会的要看前面。
//太复杂回头再码