高精度计算_string
高精度计算
对于一些比较大的数据,无法直接通过四则运算得到结果,那么这时候需要的就是高精度计算,也叫大数计算。
虽然高精度的计算不同于普通运算,但是其运算的原则和规律任然相同。
基本思想:将大数据拆分为小数据,对小数据进行四则运算,再判断进位借位。
实现方式:将数据按位存储在数组中,至于是否倒序存储看运算符,再对数组中的数字进行等位运算,并将运算结果存储到字符串中,或者输出。
高精加高精
高精度加法运算
987654321 + 123456789 = 1111111110, 具体运算过程如下:
9 8 7 6 5 4 3 2 1
+ 1 2 3 4 5 6 7 8 9
-----------------------------
= 10 10 10 10 10 10 10 10 10 // 同位相加
= 1 1 1 1 1 1 1 1 1 0 // 进位之后的结果
发现运算时总是从后开始,向前进位,于是我们会考虑逆序存储,方便计算
逆序存储
1 2 3 4 5 6 7 8 9
+ 9 8 7 6 5 4 3 2 1
-----------------------------
= 10 10 10 10 10 10 10 10 10 // 同位相加
= 0 1 1 1 1 1 1 1 1 1 // 进位之后的结果
注意事项:
- 从低位开始运算,需要反转
- 同位相加,最后进位处理
#include<iostream>
#include<string> // string
#include<cstring> // memset
#include<algorithm> // reverse
#include<iomanip> // setprecision
using namespace std;
const int N=1e6+10;
int A[N],B[N],C[N],la,lb,lc;
// 初始化,同时反转数组
void init(string a,string b) {
la = a.size(), lb = b.size();
memset(A, 0, sizeof(A));
memset(B, 0, sizeof(B));
memset(C, 0, sizeof(C));
for(int i=0; i<la; i++) A[i]=a[i]-'0';
for(int i=0; i<lb; i++) B[i]=b[i]-'0';
reverse(A, A+la), reverse(B, B+lb);
}
// 高精加高精
string add(string a,string b) {
init(a,b);
lc = max(la, lb);
for(int i=0; i<lc; i++) {
C[i] = A[i]+B[i]+C[i];
if(C[i]>9) C[i+1] = C[i]/10, C[i] %= 10;
}
while(C[lc]) lc++; //进位
string c;
for(int i=lc-1; i>=0; i--) c.append(1, C[i]+'0');
return c;
}
int main() {
string a="987654321",b="123456789";
long long x=987654321, y=123456789;
cout<<x<<" + "<<y<<" = "<<x+y<<endl;
cout<<x<<" + "<<y<<" = "<<add(a, b)<<endl;
return 0;
}
高精减高精
987654321 - 123456789 = 864197532
string sub(string a,string b) {
init(a, b);
lc = max(la, lb);
for(int i=0; i<lc; i++) {
C[i] = A[i]-B[i];
if(C[i]<0) A[i+1]--, C[i]+=10; // 借位
}
while(lc>1 && C[lc-1]==0) lc--;// 去除前导 0
string c;
for(int i=lc-1; i>=0; i--) c.append(1, C[i]+'0');
return c;
}
高精乘高精
987654321 * 123456789 = 121932631112635269
1 2 3 4 5 6 7 8 9
* 9 8 7 6 5 4 3 2 1
------------------------------
1 2 3 4 5 6 7 8 9
2 4 6 8 10 12 14 16 18
3 6 9 12 15 18 21 24 27
4 8 12 16 20 24 28 32 36
...
-----------------------------------
A[i] 中 i 控制列数,B[j]中 j 控制行数
C[i+j] = A[i]*B[j];
string mul(string a,string b) {
init(a,b);
lc = la+lb-1;
for(int i=0; i<la; i++) {
for(int j=0; j<lb; j++) {
C[i+j] += A[i]*B[j];
}
}
for(int i=0; i<lc; i++) {
C[i+1] += C[i]/10, C[i] %= 10;
}
while(C[lc]) lc++; // 进位
string c;
for(int i=lc-1; i>=0; i--) c.append(1, C[i]+'0');
return c;
}
高精除以低精
987654321 / 13 = 75973409...4
void div1(string a,int b){
la = a.size(), lc = 0;
for(int i=0; i<la; i++) A[i]=a[i]-'0';
int x=0; // x 为被除数,最后为余数
for(int i=0; i<la; i++){
x = x*10+A[i];
C[i] = x/b, x %= b;
}
while(lc<la && C[lc]==0) lc++; // 去除前导 0
for(int i=lc; i<la; i++) cout<<C[i]; cout<<" "<<x<<endl;
}
高精除以高精
基本思想是反复做除法,看从被除数里面最多能减去多少个除数,商就是多少。
逐个减显然太慢,要判断一次最多能减少多少个整的 10 的 n 次方。
以 7546 除以 23 为例。
先减去 23 的 100 倍,就是 2300,可以减 3 次,余下 646,此时商就是 300。
然后 646 减去 23 的 10 倍,就是 230,可以减 2 次,余下 186,此时商就是 320。
然后 186 减去 23,可以减 8 次,余下 2,此时商就是 328。
987654321 / 123456789 = 8...9
#include<iostream>
#include<string> // string
#include<cstring> // memset
#include<algorithm> // reverse
#include<iomanip> // setprecision
using namespace std;
const int N=1e6+10;
int A[N],B[N],C[N],la,lb,lc;
// 初始化,同时反转数组
void init(string a,string b) {
la = a.size(), lb = b.size();
memset(A, 0, sizeof(A));
memset(B, 0, sizeof(B));
memset(C, 0, sizeof(C));
for(int i=0; i<la; i++) A[i]=a[i]-'0';
for(int i=0; i<lb; i++) B[i]=b[i]-'0';
reverse(A, A+la), reverse(B, B+lb);
}
// 高精加高精
string add(string a,string b) {
init(a,b);
lc = max(la, lb);
for(int i=0; i<lc; i++) {
C[i] = A[i]+B[i]+C[i];
if(C[i]>9) C[i+1] = C[i]/10, C[i] %= 10;
}
while(C[lc]) lc++; //进位
string c;
for(int i=lc-1; i>=0; i--) c.append(1, C[i]+'0');
return c;
}
// 高精减高精
string sub(string a,string b) {
init(a, b);
lc = max(la, lb);
for(int i=0; i<lc; i++) {
C[i] = A[i]-B[i];
if(C[i]<0) A[i+1]--, C[i]+=10; // 借位
}
while(lc>1 && C[lc-1]==0) lc--;// 去除前导 0
string c;
for(int i=lc-1; i>=0; i--) c.append(1, C[i]+'0');
return c;
}
// 高精乘高精
string mul(string a,string b) {
init(a,b);
lc = la+lb-1;
for(int i=0; i<la; i++) {
for(int j=0; j<lb; j++) {
C[i+j] += A[i]*B[j];
}
}
for(int i=0; i<lc; i++) {
C[i+1] += C[i]/10, C[i] %= 10;
}
while(C[lc]) lc++; // 进位
string c;
for(int i=lc-1; i>=0; i--) c.append(1, C[i]+'0');
return c;
}
// 高精除以低精
void div1(string a,int b) {
la = a.size(), lc = 0;
for(int i=0; i<la; i++) A[i]=a[i]-'0';
int x=0; // x 为被除数,最后为余数
for(int i=0; i<la; i++) {
x = x*10+A[i];
C[i] = x/b, x %= b;
}
while(lc<la && C[lc]==0) lc++; // 去除前导 0
for(int i=lc; i<la; i++) cout<<C[i]; cout<<"..."<<x<<endl;
}
// return a>=b;
bool cmp(string a,string b) {
if(a.size() != b.size()) return a.size() > b.size();
for(int i=0; i<a.size(); i++) {
if(a[i]!=b[i]) return a[i] > b[i];
}
return 1;
}
// 高精除以高精
void div2(string a,string b) {
string c, d;
for(int i=0; i<a.size(); i++) {
d = d.append(1, a[i]);// 余数可能为 0,需要去除前导 0
while(d.find('0')==0 && d.size()>1) d.erase(0, 1);
if(cmp(d, b)) {
for(int k=9; k>=1; k--) { //试商
string x;
x.append(1, k+'0');
x = mul(x, b);
if(cmp(d, x)) {
d = sub(d, x);
c.append(1, k+'0');
break;
}
}
} else c.append(1, '0');// 不足商,则置 0
}
while(c.find('0')==0 && c.size()>1) c.erase(0,1);// 去除前导 0
cout<<c<<"..."<<d<<endl;
}
int main() {
// freopen("data.in", "r", stdin);
string a="987654321",b="123456789"; // cin>>a>>b;
long long x=987654321, y=123456789;
cout<<x<<" + "<<y<<" = "<<x+y<<endl;
cout<<x<<" + "<<y<<" = "<<add(a, b)<<endl;
cout<<x<<" - "<<y<<" = "<<x-y<<endl;
cout<<x<<" - "<<y<<" = "<<sub(a, b)<<endl;
cout<<x<<" * "<<y<<" = "<<1ll*x*y<<endl;
cout<<x<<" * "<<y<<" = "<<mul(a, b)<<endl;
cout<<x<<" / "<<y<<" = "<<x/y<<"..."<<x%y<<endl;
cout<<x<<" / "<<y<<" = "; div1(a, y);
cout<<x<<" / "<<y<<" = "; div2(a, b);
return 0;
}
987654321 + 123456789 = 1111111110
987654321 + 123456789 = 1111111110
987654321 - 123456789 = 864197532
987654321 - 123456789 = 864197532
987654321 * 123456789 = 121932631112635269
987654321 * 123456789 = 121932631112635269
987654321 / 123456789 = 8...9
987654321 / 123456789 = 8...9
987654321 / 123456789 = 8...9
string 对高精的操作速度比较慢,数据量大时容易超时,推荐使用vector实现,
高精度计算_vector:https://www.cnblogs.com/hellohebin/p/16887569.html