高精度计算_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 // 进位之后的结果

注意事项:

  1. 从低位开始运算,需要反转
  2. 同位相加,最后进位处理
#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

posted @ 2022-04-23 11:30  HelloHeBin  阅读(286)  评论(0编辑  收藏  举报