大整数四则运算(vector)

目录

  • 基础

    • 1. 高精度加法
    • 2. 高精度减法
    • 3. 高精度乘低精度
    • 4. 高精度除以低精度
    • 5. 高精度乘高精度
    • 6. 高精度除以高精度
  • 综合
  • 总结

每逢大整数四则运算,都会怯懦,虽是算法竞赛必会的东西,也零散的学过,简单的总结过,但不成体系的东西心里一直没底。

所以今天消耗了大量的卡路里,啃了几套模板之后终于总结成了一套自己的模板

再也不用担心大整数啦

基础

1. 高精度加法

高精度加法等同于算术加法,做单个的加法运算之后存下进位

  • A和B都为正整数
  • vector中下标为0存的是低位(以下都是)
vector<int> add(vector<int> &A,vector<int> &B){
    if(A.size() < B.size()) return add(B,A);//这是为了保证A的位数比B大
    vector<int> C;
    int t = 0;
    for(int i = 0;i < A.size();i++){
        t += A[i];
        if(i < B.size()) t += B[i];
        C.push_back(t % 10);
        t /= 10;
    }
    if(t) C.push_back(t);
    //清除前缀0,为了防止000+0等输入情况
    while(C.size() > 1 && C.back() == 0)C.pop_back();
    return C;
}

2. 高精度减法

减法同加法原理一样,也是计算一位,下面用了t这个变量存下向更高一位借的1

  • A和B必须为正整数,结果返回|A-B|
vector<int> sub(vector<int> &A,vector<int> &B){
    vector<int> C;
    for(int i= 0,t = 0;i<A.size();i++){
        t = A[i] - t;
        if(i < B.size()) t -= B[i];
        C.push_back((t+10)%10);
        if(t < 0) t = 1;
        else t = 0;
    }
    //清除前缀0
    while(C.size() > 1 && C.back() == 0)C.pop_back();
    return C;
}

3. 高精度乘低精度

高精度的每一位与低精度数字进行相乘,因为相乘最大为81,所以结果对10取余得到的数字就是结果在当前位的结果,然后对结果除以10保存更高一位的进位数字

  • A存放正整数,b也为正整
vector<int> mul(vector<int> &A,int b){
    vector<int> C;
    int t = 0;
    for(int i = 0;i < A.size() || t; i++){
        if(i < A.size()) t += A[i] * b;
        C.push_back(t%10);
        t /= 10;
    }
    return C;
}

4. 高精度除以低精度

在计算除法时,会从被除数的最高位算起,每一位的计算结果就是当前余数对除数做除法的结果。然后计算下一位(更小的一位,设第x位)时,要更新余数即 余数*10 + 被除数的x位。

  • A 存放正整数,b也为正整数,r为余数
vector<int> div(vector<int> & A,int b,int &r){
    vector<int> C;
    r = 0;
    for(int i = A.size() - 1;i >= 0;i--){
        r = r * 10 + A[i];
        C.push_back(r/b);
        r %= b;
    }
    reverse(C.begin(),C.end());
    while(C.size() > 1 && C.back() == 0)C.pop_back();
    return C;
}

什么?乘低精度还不够?除低精度太low?那我们考虑一些不太常见的情况(openjudge百炼2980)

5. 高精度乘高精度

首先说一下乘法计算的算法,从低位向高位乘,在竖式计算中,我们是将乘数第一位与被乘数的每一位相乘,记录结果之后,用第二位相乘,记录结果并且左移一位,以此类推,直到计算完最后一位,再将各项结果相加,得出最后结果。

? 计算的过程基本上和小学生列竖式做乘法相同。为了编程方便,并不急于处理进位,而是先将所有位上的结果计算之后,再从前往后以此处理进位。

? 总结一个规律: 即一个数的第i 位和另一个数的第j 位相乘所得的数,一定是要累加到结果的第i+j 位上。这里i, j 都是从数字低位开始从0 开始计数。
ans[i+j] = a[i]*b[j];

? 另外注意进位时要处理,当前的值加上进位的值再看本位数字是否又有进位;前导清零。

vector<int> mul( vector<int> &A, vector<int> &B) {
    int la = A.size(),lb = B.size();
    vector<int> C(la+lb+10,0);//提前申请结果所需的空间
    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<C.size();i++){
        if(C[i] >= 10){
            C[i + 1] += C[i] / 10;
            C[i] %= 10;
        }
    }
    //处理前导0
    while(C.size() > 1 && C.back() == 0)C.pop_back();
    return C;
}

6. 高精度除以高精度

大数除法是四则运算里面最难的一种。不同于一般的模拟,除法操作不是模仿手工除法,而是利用减法操作来实现的。其基本思想是反复做减法,看从被除数里面最多能减去多少个除数,商就是多少。逐个减显然太慢,要判断一次最多能减少多少个整数(除数)的10的n次方。

以7546除以23为例:

? 先用7546减去23的100倍,即减去2300,可以减3次,余下646,此时商就是300 (300=100*3);

? 然后646减去23的10倍,即减去230,可以减2次,余下186,此时商就是320 (320=300+10*2);

? 然后186减去23,可以减8次,余下2,此时商就是328 (328=320+1*8);

? 因为2除以23的结果小于1,而我们又不用计算小数点位,所以不必再继续算下去了。

计算结果中没有小数:

vector<int> div(vector<int> A,vector<int> B){
    int la = A.size(),lb = B.size();
    int dv = la - lb; // 相差位数
    vector<int> C(dv+1,0);//提前申请结果所需空间
    //将除数扩大,使得除数和被除数位数相等
    reverse(B.begin(),B.end());
    for(int i=0;i<dv;i++)B.push_back(0);
    reverse(B.begin(),B.end());
    lb = la;
    for(int j=0;j<=dv;j++){
        while(!cmp(A,B)){//这里用到一个比较函数,cmp返回的是A是否比B小,此处判断的是A是否大于等于B,该循环当A无法再进行减法时结束
            A = sub(A,B);
            C[dv-j]++;//答案里相应的那一位数字++
        }
        B.erase(B.begin());//缩小被除数
    }
    while(C.size()>1 && C.back() == 0)C.pop_back();
    return C;
}

好了,基础部分到此结束,将上面的内容封装好之后,我们就可以比较完美的在比赛中使用了。

综合

  • 该结构体是借用紫书上的模板,BASE是数组一位上面可以存数的值。也可以理解为是一个BASE进制数,WIDTH对应的是BASE的宽度
  • 因为大整数乘大整数所用计算方法的特殊性,BASE应当设置为10,WIDTH设置为1,相应的重载输出流里面的sprinf函数中也应该控制为1位而不是8位
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct BigInteger{
    //BASE为vector数组中一位中最大存储的数字,前面都是以10计算的
    //WIDTH为宽度
    static const int BASE = 100000000;
    static const int WIDTH = 8;
    vector<int> s;
    //正数为1,负数为-1
    int flag = 1;

    //构造函数
    BigInteger(ll num = 0){*this = num;}
    BigInteger(string str){*this = str;}
    BigInteger(const BigInteger& t){
        this->flag = t.flag;
        this->s = t.s;
    }
    //赋值函数
    BigInteger operator = (ll num){
        s.clear();
        do {
            s.push_back(num % BASE);
            num /= BASE;
        }while(num > 0);
        return *this;
    }
    BigInteger operator = (string &str){
        s.clear();
        int x,len = (str.length()-1)/WIDTH + 1;
        for(int i=0;i<len;i++){
            int end = str.length() - i*WIDTH;
            int start = max(0,end - WIDTH);
            sscanf(str.substr(start,end-start).c_str(),"%d",&x);
            s.push_back(x);
        }
        return *this;
    }

    //基本比较函数 A < B
    bool cmp( vector<int> &A, vector<int> & B){
        if(A.size() != B.size())return A.size() < B.size();
        for(int i=A.size()-1;i>=0;i--){
            if(A[i] != B[i]){
                return A[i] < B[i];
            }
        }
        return false;
    }
    //比较函数如果小于则返回真
    bool operator < ( BigInteger & b){
        return cmp(s,b.s);
    }
    bool operator > ( BigInteger& b){
        return b < *this;
    }
    bool operator <= ( BigInteger &b){
        return !(b < *this);
    }
    bool operator >= ( BigInteger &b){
        return !(*this < b);
    }
    bool operator == ( BigInteger &b){
        return !(b < *this) && (*this < b);
    }
    //基本四则运算
    vector<int> add(vector<int> &A, vector<int> &B);
    vector<int> sub(vector<int> &A, vector<int> &B);
    vector<int> mul(vector<int> &A, int b);
    vector<int> mul(vector<int> &A, vector<int> &B);
    vector<int> div(vector<int> &A, int b);
    vector<int> div(vector<int> A, vector<int> B);

    //重载运算符
    BigInteger operator + (BigInteger &b);
    BigInteger operator - (BigInteger &b);
    BigInteger operator * (BigInteger &b);
    BigInteger operator * (int& b);
    BigInteger operator / (BigInteger & b);
    BigInteger operator / (int b);
};
//重载<<
ostream& operator << (ostream &out,const BigInteger& x) {
    if(x.flag == -1)out << '-';
    out << x.s.back();
    for(int i = x.s.size() - 2; i >= 0;i--){
        char buf[20];
        sprintf(buf,"%08d",x.s[i]);//08d此处的8应该与WIDTH一致
        for(int j=0;j<strlen(buf);j++)out<<buf[j];
    }
    return out;
}
//重载输入
istream& operator >> (istream & in,BigInteger & x){
    string s;
    if(!(in>>s))return in;
    x = s;
    return in;
}
vector<int> BigInteger::add( vector<int> &A, vector<int> &B){
    if(A.size() < B.size())return add(B,A);
    int t = 0;
    vector<int> C;
    for(int i=0;i<A.size();i++){
        if(i<B.size())t += B[i];
        t += A[i];
        C.push_back(t%BASE);
        t /= BASE;
    }
    if(t)C.push_back(t);
    while(C.size() > 1 && C.back() == 0)C.pop_back();
    return C;
}
vector<int> BigInteger::sub( vector<int> &A, vector<int> &B){
    vector<int> C;
    for(int i=0,t=0;i<A.size();i++){
        t = A[i] - t;
        if(i<B.size())t -= B[i];
        C.push_back((t+BASE)%BASE);
        if(t < 0)t = 1;
        else t = 0;
    }
    while(C.size() > 1 && C.back() == 0)C.pop_back();
    return C;
}
vector<int> BigInteger::mul(vector<int> &A,int b){
    vector<int> C;
    int t = 0;
    for(int i = 0;i < A.size() || t; i++){
        if(i < A.size()) t += A[i] * b;
        C.push_back(t%BASE);
        t /= BASE;
    }
    return C;
}
//大数乘大数乘法需要将BASE设置为10,WIDTH设置为1
vector<int> BigInteger::mul( vector<int> &A, vector<int> &B) {
    int la = A.size(),lb = B.size();
    vector<int> C(la+lb+10,0);
    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<C.size();i++){
        if(C[i] >= BASE){
            C[i + 1] += C[i] / BASE;
            C[i] %= BASE;
        }
    }
    while(C.size() > 1 && C.back() == 0)C.pop_back();
    return C;
}
//大数除以整数
vector<int> BigInteger::div(vector<int> & A,int b){
    vector<int> C;
    int r = 0;
    for(int i = A.size() - 1;i >= 0;i--){
        r = r * 10 + A[i];
        C.push_back(r/b);
        r %= b;
    }
    reverse(C.begin(),C.end());
    while(C.size() > 1 && C.back() == 0)C.pop_back();
    return C;
}
//大数除以大数
vector<int> BigInteger::div(vector<int> A,vector<int> B){
    int la = A.size(),lb = B.size();
    int dv = la - lb; // 相差位数
    vector<int> C(dv+1,0);
    //将除数扩大,使得除数和被除数位数相等
    reverse(B.begin(),B.end());
    for(int i=0;i<dv;i++)B.push_back(0);
    reverse(B.begin(),B.end());
    lb = la;
    for(int j=0;j<=dv;j++){
        while(!cmp(A,B)){
            A = sub(A,B);
            C[dv-j]++;
        }
        B.erase(B.begin());
    }
    while(C.size()>1 && C.back() == 0)C.pop_back();
    return C;
}
BigInteger BigInteger::operator + ( BigInteger & b){
    BigInteger c;
    c.s.clear();
    c.s = add(s,b.s);
    return c;
}

BigInteger BigInteger::operator - ( BigInteger & b) {
    BigInteger c;
    c.s.clear();
    if(*this < b){
        c.flag = -1;
        c.s = sub(b.s,s);
    }
    else{
        c.flag = 1;
        c.s = sub(s,b.s);
    }
    return  c;
}
BigInteger BigInteger::operator *(BigInteger & b){
    BigInteger c;
    c.s = mul(s,b.s);
    return c;
}
BigInteger BigInteger::operator *(int& b){
    BigInteger c;
    c.s = mul(s,b);
    return c;
}
BigInteger BigInteger::operator /(BigInteger & b){
    BigInteger c;
    if(*this < b){
        c.s.push_back(0);
    }
    else{
        c.flag = 1;
        c.s = div(s,b.s);
    }
    return c;
}
BigInteger BigInteger::operator /(int b){
    BigInteger c;
    BigInteger t = b;
    if(*this < t){
        c.s.push_back(0);
    }
    else{
        c.flag = 1;
        c.s = div(s,b);
    }
    return c;
}
int main(){
    BigInteger A,B;
    cin>>A>>B;
    cout<<A+B<<endl;
    cout<<A-B<<endl;
    cout<<A*B<<endl;
    cout<<A/B<<endl;
    return 0;
}

总结

模板只提供了正整数的运算,对于含有负整数的运算,只需要进行合理的转换即可,见下表

A B + - * /
+ + |A|+|B| |A|-|B| |A|*|B| |A|/|B|
+ - |A|-|B| |A|+|B| -(|A|*|B|) -(|A|/|B|)
- + |B|-|A| -(|A|+|B|) -(|A|*|B|) -(|A|/|B|)
- - -(|A|+|B|) |B|-|A| |A|*|B| |A|/|B|

【附:参考资料】

  1. https://www.cnblogs.com/wuqianling/p/5387099.html
  2. ACwing算法基础课
  3. 算法竞赛入门经典
  4. 红宝书

原文地址:https://www.cnblogs.com/1625--H/p/11141106.html

时间: 2024-10-08 22:32:32

大整数四则运算(vector)的相关文章

大整数四则运算

============ 日后再写!先将设计思想留下 ============= 定义并实现超长整数类double long,要求如下: 64位数据长度,有符号 支持+.-.*./运算 支持+=.-=./=运算 支持cin>>和cout<<操作 首先,我们的运算对象是大整数并且要支持cout<<和cin>>操作,数组和链表是可选择项.在这里我们用数组.数组有int型和char型,选哪个好呢?只能选char型,因为我们的大整数是有符号位的,必须用一个char字

7.2 大整数四则运算

7-2 BigInt1.c 1 #include <stdio.h> 2 #include <string.h> 3 typedef struct bigint 4 { 5 char *num; //指向长整数数组(序号0中保存着最低位) 6 char minus; //符号(1表示正数,-1表示负数) 7 int digit; //保存该数的位数(实际位数) 8 }BIGINT, *pBIGINT; 9 void BigIntTrans(pBIGINT num); //字符串转数

[带符号大整数模板]vector版

1 #include <iostream> 2 #include <cstdio> 3 #include <vector> 4 #include <cstring> 5 6 using namespace std; 7 8 typedef vector<int> vi; 9 typedef long long LL; 10 11 12 const int maxI = 1e8; 13 const int Len = 8; 14 15 struct

【面试】大整数四则运算

面试手写代码,当时只写出了加减乘,还写的漏洞百出.下面是加减法: 1 #include<iostream> 2 using namespace std; 3 4 struct BigN { 5 int data[1000]; //设最长1000位,数组的每个数字代表一位,data[0]存最低位 6 int len; 7 int sign;//0为负,1为正 8 BigN() { 9 memset(data, 0, sizeof(data)); 10 len = 0; 11 sign = 1;

模板 高精度大整数

#include<bits/stdc++.h> #define clr(x,y) memset((x),(y),sizeof(x)) using namespace std; typedef long long LL; const int MaxL=400; //大数长度 //大整数 //减法只能大减小 struct bign { int len, s[MaxL]; //构造函数 bign () { memset(s, 0, sizeof(s)); len = 1; } bign (int n

算法学习——大整数运算(高精度)C++

1.大整数加法 用数组来存储大整数的每一位,然后模拟人工运算,用for循环按位运算和处理,原理十分简单,直接上模板. #include<iostream> #include<vector> using namespace std; //大整数加法 vector<int> add(vector<int>& A,vector<int>& B){ vector<int> C; int t = 0; for(int i = 0

大整数的存储与加减乘除四则运算

当需要计算的整数或计算结果可能会超出long long 所能表示的范围时,应该用大整数来存储和计算(Java里面有BigInteger来存储大整数,这里讨论的是C++语言). 大整数的存储形式是下面这个结构体(包含了构造函数): // 大整数结构体 struct bign{ int d[1000]; int len; bign(){ memset(d, 0, sizeof(d)); len = 0; } }; 首先以字符串的形式读去数据,然后将字符串逐个逆序字符存入d[]数组(可以采用用reve

大整数类

大整数类又叫高精度. 就是求大数的四则运算的算法, (其实就是模拟小学生算数的方法, 什么? 你不会, 那你还不如小学生, 哈哈!). 在这里只贴加法运算符的重载,其他的运算符与加法类似.闲言少叙, 直接上代码(小声告诉你, 里面用了几个库函数和STL, 嘿嘿!!!). 1 #include<cstdio> 2 #include<cstring> 3 #include<vector> 4 #include<iostream> 5 using namespac

c++大整数

这里不是必须用c++的话不推荐用c++大整数,py和java的支持要好得多. 大整数类  (非负) #include <iostream> #include <vector> #include <string> using namespace std; struct BigInteger{ static const int BASE=100000000; static const int WIDTH=8; vector<int> s; BigInteger(