首页 > 编程语言 >算法学习笔记(4)——高精度

算法学习笔记(4)——高精度

时间:2022-12-09 22:11:13浏览次数:69  
标签:10 高精度 int back 笔记 算法 vector size

高精度

高精度加法

题目链接:AcWing 791. 高精度加法

题目描述

给定两个正整数(不含前导 0),计算它们的和。

输入格式

共两行,每行包含一个整数。

输出格式

共一行,包含所求的和。

数据范围

\(1 \le\) 整数长度 \(\le 100000\)

输入样例

12
23

输出样例

35

要计算两个高精度整数 \(A\) 和 \(B\) 的和,可以用列竖式的思想,从最低位开始,将低位加起来然后进位,每次将进位 \(t\) 都和两者尚未计算的最低位 \(A[i]\) 和 \(B[i]\) 加起来,其除以 \(10\) 的余数就是这一位的值,商就是进位 \(t\),所以:

由于 \(A\) 和 \(B\) 的位数不一定是一样的,这里不妨假设 \(A\) 的长度总是 > = B >=B >=B的长度(如果输入时不满足,就交换 \(A\) 和 \(B\) 再做高精度加法),这样计算的时候就只要把 \(A\) 的每一项都加完,相应的位置如果 \(B[i]\) 还存在就加,不存在就不用加了。

在加完之后还要判断一下进位 \(t\),如果还有进位的话,就要再在结果中加一位。只要用一个if判断一次就可以了,不需要用while来做,因为最后 \(t\) 的值一定是 $ 0 \leq 9 < 10$ 的,因为以 \(A\) 的长度,加上一个不超过其长度的 \(B\),结果 \(C\) 最大能达到的长度也就是 \(A\) 的长度 \(+1\) ,不可能更长了。

由于是从最低位往高位一位一位加,而读取数据的时候是从高位向低位读的,所以这里将 \(A\) 和 \(B\) 都排成从低位向高位的,这样方便计算。相应地,得到的 \(C\) 也是从低位到高位排的,最后记得翻转一下。

#include <iostream>
#include <vector>

using namespace std;

vector<int> add(vector<int> &A, vector<int> &B)
{
    vector<int> sum;
    int t = 0;  // 进位
    for (int i = 0; i < A.size() || i < B.size(); i ++ ) {
        if (i < A.size()) t += A[i];
        if (i < B.size()) t += B[i];
        sum.push_back(t % 10);
        t /= 10;
    }
    // 处理最后的进位
    if (t) sum.push_back(t);
    return sum;
}

int main()
{
    string a, b;
    cin >> a >> b;
    // 倒着存
    vector<int> A, B;
    for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');
    for (int i = b.size() - 1; i >= 0; i -- ) B.push_back(b[i] - '0');
    
    auto res = add(A, B);
    for (int i = res.size() - 1; i >= 0; i -- ) cout << res[i];
    puts("");
    
    return 0;
}

高精度减法

题目链接:AcWing 792. 高精度减法

题目描述

给定两个正整数(不含前导 0),计算它们的差,计算结果可能为负数。

输入格式

共两行,每行包含一个整数。

输出格式

共一行,包含所求的差。

数据范围

\(1 \le\) 整数长度 \(\le 10^5\)

输入样例

32
11

输出样例

21

要计算两个高精度整数 \(A\) 和 \(B\) 的差,也是用列竖式的思想。这里需要先判断一下 \(A\) 和 \(B\) 哪个大,如果 \(B\) 比 \(A\) 大,那么 \(A-B\) 的结果是 \(-(B-A)\),所以只需要实现一个较大的自然数减去一个较小的自然数这个过程就可以了。

在做减法的时候,已经确定传入的 \(A\) 是 \(\ge B\) 的了,所以 \(A\) 的长度也是 \(\ge B\) 的,可以用和前面计算高精度加法时候类似的思想。从低位到高位遍历 \(A\) 中的每一位,用 \(t\) 表示来自上一位的借位(借位只能是 \(0\) 或者 \(1\) ) 。

那么这一位的减法实际上就是 \(A[i] - t - B[i]\)(如果 \(B\) 已经遍历完了,就不用减 \(B[i]\))。如果这个值是负的,那么就说明这一位的减法是向更高一位借了 \(1\) 的,那么下一轮 \(t\) 就要变成 \(1\) ,否则下一轮 \(t\) 的值就是 \(0\) 。

向高位借 \(1\) 的效果就是在本位加了一个 \(10\),所以本位减法的计算结果可以直接用 \(+10\) 之后再 \(\bmod 10\) 来计算,这样不论有没有借位计算的都是正确的。

需要注意,最终的减法结果可能存在前导 \(0\) ,所以需要删除高位的连续 \(0\) 。然而当减法结果是 \(0\) 时候,需要保留一个 \(0\) ,所以在删除高位 \(0\) 的时候注意一下只剩一个数的时候也要停止删除了。

#include <iostream>
#include <vector>

using namespace std;

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];
    // A=B的情况,返回true
    return true;
}

// 计算A - B, 默认A >= B
vector<int> sub(vector<int> &A, vector<int> &B)
{
    vector<int> res;
    int t = 0; // 减法借位
    for (int i = 0; i < A.size(); i ++ ) {
        // 被减数减去上次的借位
        t = A[i] - t;
        // 减数存在则减去
        if (i < B.size()) t -= B[i];
        // 将差值存入答案
        res.push_back((t + 10) % 10);
        // 判断处理借位
        if (t < 0) t = 1;
        else t = 0;
    }
    // 处理前导零
    while (res.size() > 1 && res.back() == 0) res.pop_back();
    return res;
}

int main()
{
    string a, b;
    cin >> a >> b;
    
    vector<int> A, B;
    for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');
    for (int i = b.size() - 1; i >= 0; i -- ) B.push_back(b[i] - '0');
    
    if (cmp(A, B)) {
        auto res = sub(A, B);
        for (int i = res.size() - 1; i >= 0; i -- ) cout << res[i];
        puts("");
    }
    else {
        auto res = sub(B, A);
        cout << "-";
        for (int i = res.size() - 1; i >= 0; i -- ) cout << res[i];
        puts("");
    }
    
    return 0;
}

高精度乘法

题目链接:AcWing 793. 高精度乘法

题目描述

给定两个非负整数(不含前导 0)\(A\) 和 \(B\) ,请你计算 \(A×B\) 的值。

输入格式

共两行,第一行包含整数 \(A\),第二行包含整数 \(B\)。

输出格式

共一行,包含 \(A×B\) 的值。

数据范围

\(1 \le\) A的长度 \(\le 10^5\)
\(0 \le B \le 10000\)

输入样例

2
3

输出样例

6

这里 \(A\) 是一个高精度整数, \(b\) 是一个普通精度的整数,计算它们的乘法也是用列竖式的方法,但是因为 \(b\) 不是一个高精度的数,只需要从 \(A\) 低位到高位,每次把对应位置的数字 \(A[i]\) 乘以 \(b\) 再加上来自低位的进位 \(t\) 就可以了,而不需要把 \(b\) 的每一位拆开来。

乘的结果模 \(10\) 就是结果 \(C\) 的这一位上的数,而除以 \(10\) 的结果就是进位。在 \(A\) 的每一位都乘完之后,进位的值可能还没用完,所以要重复“模 \(10\) 放在下一位,除以 \(10\) 作为进位”这个操作。

结果 \(C\) 中的高位可能有连续的 \(0\) ,所以还是像高精度减法中那样处理一下高位的连续 \(0\) ,同时注意如果结果就是 \(0\) 要留下一个 \(0\) 。

#include <iostream>
#include <vector>

using namespace std;

vector<int> mul(vector<int> &A, int &b)
{
    vector<int> C;
    int t = 0; // 乘法进位
    for (int i = 0; i < A.size(); i ++ ) {
        t += A[i] * b;
        C.push_back(t % 10);
        t /= 10;
    }
    if (t) C.push_back(t);
    while (C.size() > 1 && C.back() == 0) C.pop_back();
    return C;
}

int main()
{
    string a;
    int b;
    cin >> a >> b;
    
    vector<int> A;
    for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');
    
    auto C = mul(A, b);
    for (int i = C.size() - 1; i >= 0; i -- ) cout << C[i];
    
    return 0;
}

高精度除法

题目链接:AcWing 794. 高精度除法

题目描述

给定两个非负整数(不含前导 \(0\)) \(A\),\(B\),请你计算 \(A/B\) 的商和余数。

输入格式

共两行,第一行包含整数 \(A\),第二行包含整数 \(B\)。

输出格式

共两行,第一行输出所求的商,第二行输出所求余数。

数据范围

\(1 \le\) A的长度 \(\le 10^5\)
\(0 \le B \le 10000\)
\(B\) 一定不为 \(0\)。

输入样例

7
2

输出样例

3
1

这里 \(A\) 是一个高精度整数, \(b\) 是一个普通精度的整数,计算它们的除法也是用列竖式的方法,但是因为 \(b\) 不是一个高精度的数,只需要从 \(A\) 高位到低位(注意这里和前面都不一样),每次把对应位置的数字 \(A[i]\) 加上来自高位的余数 \(r * 10\) 再除以 \(b\) 就可以了,而不需要把 \(b\) 的每一位拆开来。

这里高位的余数 \(r\) 是在高位那个量级上的,所以在这一位(较低一位)上要乘以 \(10\) 才行。

和前面保持一致, \(A\) 还是从低位到高位来存,但是计算的时候从高位向低位依次做除法,得到的结果 \(C\) 是从高位到低位存的。所以还要记得把 \(C\) 用reverse翻转一下,变成从低位到高位存的,然后还是要删除一下高位的连续 \(0\) 。

在代码实现中,函数div的返回值返回的是 \(A\) 除以 \(b\) 的商,而余数 \(r\) 从参数表里用引用的形式返回。

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

// 返回A/b的值,余数c通过参数列表引用形式返回
vector<int> div(vector<int> &A, int &b, int &c)
{
    vector<int> C;
    c = 0;
    // 从高位开始做除法
    for (int i = A.size() - 1; i >= 0; i -- ) {
        c = c * 10 + A[i];
        C.push_back(c / b);
        c %= b;
    }
    reverse(C.begin(), C.end());
    while (C.size() > 1 && C.back() == 0) C.pop_back();
    return C;
}

int main()
{
    string a;
    int b;
    cin >> a >> b;
    
    vector<int> A;
    for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');
    
    int c;
    auto C = div(A, b, c);
    
    for (int i = C.size() - 1; i >= 0; i -- ) cout << C[i];
    puts("");
    cout << c << endl;
    
    return 0;
}

标签:10,高精度,int,back,笔记,算法,vector,size
From: https://www.cnblogs.com/I-am-Sino/p/16970129.html

相关文章

  • 算法学习笔记(5)——前缀和与差分
    前缀和与差分前缀和与差分一维前缀和差分一维前缀和前缀和可以用于快速计算一个序列的区间和,也有很多问题里不是直接用前缀和,但是借用了前缀和的思想。用\(s[i......
  • 算法学习笔记(2)——归并排序
    归并排序归并排序的思想是基于分治法,其思路是:将待排序区间平分成左右两半,左右两侧分别递归地做归并排序。将这两个有序的区间合并(每次落一个较小的下来),就将这个区间排......
  • 算法学习笔记(1)——快速排序
    快速排序算法思想快排的思想是基于分治法,其思路是:确定分界点:给定要排序的数组q,先在数组中找一个数作为分界点值x,分界点可以取左边界值x=q[l],可以取右边界值x=q[r],......
  • docker学习笔记
    Docker&CKA学习笔记第一章、容器存在的意义、优势、docker介绍1.1容器存在的意义1、上线流程繁琐开发->测试->申请资源->审批->部>测试等环节2、资源利用率低......
  • 小林笔记【面试】
    小林笔记【面试】​​前言​​​​推荐​​​​小林笔记【面试】​​​​最后​​推荐​​https://xiaolincoding.com/​​小林笔记【面试】​​操作系统笔记【面试】​​​......
  • crypto-gmsm国密算法库
    crypto-gmsm国密算法库一、开发背景crypto-gmsm国密算法库是国密商密算法(SM2,SM3,SM4)工具类封装,国产密码算法(国密算法)是指国家密码局认定的国产商用密码算法,目前主要使用......
  • 代码随想录算法训练营第三天 | 203.移除链表元素、707.设计链表、206.反转链表
    203. 移除链表元素tag:#链表leetcode地址:203. 移除链表元素代码:functionremoveElements(head:ListNode|null,val:number):ListNode|null{constnewH......
  • hutools密码算法库
    hutool密码算法库一、开发背景Hutool针对BouncyCastle做了简化包装,用于实现国密算法中的SM2、SM3、SM4。国密算法工具封装包括:非对称加密和签名:SM2摘要签名算法:SM3......
  • 【主色提取】模糊C均值(FCM )聚类算法和彩色图像快速模糊C均值( CIQFCM )聚类算法
    OverridetheentrypointofanimageIntroducedinGitLabandGitLabRunner9.4.Readmoreaboutthe extendedconfigurationoptions.Beforeexplainingtheav......
  • 学习笔记281—word不能插入公式
    点击辅助功能在文档中点击状态栏下辅助功能。 点击转换在辅助功能界面,点击转换。 点击公式点击公式,这样就可以插入公式。END方法/步......