【算法基础】1.4 高精度(模拟大数运算:整数加减乘除)


本文主要讲解高精度计算,包括加法、减法、乘法和除法。
对于Python选手,python自带高精度计算;Java也有BigInteger类。但是对于C选手和C++选手,高精度计算的算法还是很重要的。

加法和乘法比较简单,减法和除法要难一些。

java相关的大数类可以参见:Java【大数类】整理


高精度加法

题目描述

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

数据范围
1 ≤ 整数长度 ≤ 100000

在这里插入图片描述

解法

cpp

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

using namespace std;

vector<int> add(vector<int> &A, vector<int> &B) {
    if (A.size() < B.size()) return add(B, A);
    
    vector<int> C;
    int t = 0;		// t是进位
    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);
    return C;
}


int main()
{
    string a, b;
    vector<int> A, B;
    cin >> 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');
    
    vector<int> C = add(A, B);
    for (int i = C.size() - 1; i >= 0; --i) printf("%d", C[i]);
    
    return 0;
}

Java

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        char[] a = scanner.next().toCharArray(), b = scanner.next().toCharArray();
        int c = 0;
        StringBuilder ans = new StringBuilder();
        for (int i = a.length - 1, j = b.length - 1; i >= 0 || j >= 0 || c != 0; --i, --j) {
            if (i >= 0) c += a[i] - '0';
            if (j >= 0) c += b[j] - '0';
            ans.append(c % 10);
            c /= 10;
        }
        System.out.println(ans.reverse());
    }
}


高精度减法⭐⭐⭐

题目描述

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

数据范围
1 ≤ 整数长度 ≤ 10^5

在这里插入图片描述

解法

CPP

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

using namespace std;

bool cmp(vector<int> &a, vector<int> &b) {
    /* 判断是否 a 大于等于 b */
    int an = a.size(), bn = b.size();
    if (an != bn) return an > bn;
    for (int i = an - 1; i >= 0; i--) {     // 从最高位开始比较
        if (a[i] != b[i]) {
            return a[i] > b[i];
        }
    }
    return true;
}

vector<int> sub(vector<int> &a, vector<int> &b) {
    int an = a.size(), bn = b.size();
    vector<int> c;
    int t = 0;      // t表示是否借位
    for (int i = 0; i < an; i++) {
        a[i] -= t;      // 减去借位
        if (i < bn) a[i] -= b[i];
        c.push_back((a[i] + 10) % 10);
        if (a[i] < 0) t = 1;
        else t = 0;
    }
    while (c.size() > 1 && c.back() == 0) c.pop_back();
    return c;
}


int main()
{
    string a, b;
    vector<int> A, B, C;
    cin >> 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)) C = sub(A, B);
    else {
        printf("-");
        C = sub(B, A);
    }
    
    for (int i = C.size() - 1; i >= 0; --i) printf("%d", C[i]);
    
    return 0;
}

Java

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String a = scanner.next(), b = scanner.next();
        List<Integer> A = new ArrayList<>(), B = new ArrayList<>(), C = new ArrayList<>();
        for (int i = a.length() - 1; i >= 0; --i) A.add(a.charAt(i) - '0');
        for (int i = b.length() - 1; i >= 0; --i) B.add(b.charAt(i) - '0');

        if (compare(A, B)) C = sub(A, B);
        else {
            System.out.print("-");
            C = sub(B, A);
        }
        for (int i = C.size() - 1; i >= 0; --i) System.out.print(C.get(i));
    }

    public static boolean compare(List<Integer> A, List<Integer> B) {
        int an = A.size(), bn = B.size();
        if (an != bn) return an > bn;
        for (int i = an - 1; i >= 0; --i) {
            if (A.get(i) != B.get(i)) return A.get(i) > B.get(i);
        }
        return true;
    }

    public static List<Integer> sub(List<Integer> A, List<Integer> B) {
        int an = A.size(), bn = B.size();
        List<Integer> C = new ArrayList<>();
        int t = 0;
        for (int i = 0; i < an; ++i) {
            A.set(i, A.get(i) - t);		// 看看这一位有没有被借
            if (i < bn) A.set(i, A.get(i) - B.get(i));
            C.add((A.get(i) + 10) % 10);
            if (A.get(i) < 0) t = 1;
            else t = 0;
        }
        while (C.size() > 1 && C.get(C.size() - 1) == 0) C.remove(C.size() - 1);
        return C;
    }
}

讲解

注意点包括:
判断a和b的大小;去除结果末尾的所有0;使用(t+10)%10;


高精度乘法

题目描述

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

数据范围
1 ≤ A的长度 ≤ 100000,
0 ≤ B ≤ 10000

在这里插入图片描述

解法

和加法类似,每次用大数字的一位乘上整个小数字。

Cpp

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

vector<int> mul(vector<int> A, int b) {
    int t = 0;      // t类似加法中的进位
    vector<int> C;
    for (int i = 0; i < A.size() || t; ++i) {
        if (i < A.size()) t += A[i] * b;
        C.push_back(t % 10);
        t /= 10;
    }

    while (C.size() > 1 && C.back() == 0) C.pop_back(); // 删除前导零
    return C;
}


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

Java

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String a = scanner.next();
        int b = scanner.nextInt(), c = 0;
        List<Integer> A = new ArrayList<>(), C = new ArrayList<>();
        for (int i = a.length() - 1; i >= 0; --i) A.add(a.charAt(i) - '0');
        for (int i = 0; i < a.length() || c != 0; ++i) {
            if (i < a.length()) c += A.get(i) * b;
            C.add(c % 10);
            c /= 10;
        }
        for (int i = C.size() - 1; i > 0; --i) {
            if (C.get(i) == 0) C.remove(i);
            else break;
        }
        for (int i = C.size() - 1; i >= 0; --i) System.out.print(C.get(i));
    }
}

讲解

与加法类似。


高精度除法⭐⭐⭐⭐⭐

题目描述

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

数据范围
1 ≤ A的长度 ≤ 100000,
1 ≤B ≤ 10000,
B 一定不为 0

在这里插入图片描述

解法

Cpp

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

vector<int> div(vector<int> a, int b, int &r) {
    vector<int> c;
    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;
}

int main()
{
    string a;
    int b;
    cin >> a >> b;
    
    vector<int> A, C;
    for (int i = a.size() - 1; i >= 0; --i) A.push_back(a[i] - '0');
    int r = 0;  // r是余数
    C = div(A, b, r);
    for (int i = C.size() - 1; i >= 0; --i) printf("%d", C[i]);
    printf("\n%d\n", r);
    return 0;
}

Java

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String a = scanner.next();
        int b = scanner.nextInt();
        List<Integer> A = new ArrayList<>(), C;
        for (int i = a.length() - 1; i >= 0; --i) A.add(a.charAt(i) - '0');
        int r = 0;      // r是余数
        div(A, b, r);
    }

    static void div(List<Integer> A, int b, int r) {
        List<Integer> C = new ArrayList<>();
        // 从高位开始
        for (int i = A.size() - 1; i >= 0; --i) {
            r = r * 10 + A.get(i);
            C.add(r / b);
            r %= b;
        }
        Collections.reverse(C);
        while (C.size() > 1 && C.get(C.size() - 1) == 0) C.remove(C.size() - 1);
        for (int i = C.size() - 1; i >= 0; --i) System.out.print(C.get(i));
        System.out.println("\n" + r);
    }
}

讲解

与 加减乘 不同,除法是从高位开始的。
在这里插入图片描述
从最高位开始枚举大数字的每一位,在这个过程中注意余数的变化为:

r = r * 10 + a[i];

然后使用余数 r 去除以小数字,加入答案商,然后 r %= b;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 大整数乘除运算算法设计与分析中的一个重要问题。对于大整数乘除运算,需要设计效的算法来处理。其中,法和法可以采用类似于小学数学中的竖式计算方法,乘法可以采用类似于小学数学中的乘法计算方法,而除法则需要采用更为复杂的算法,如长除法或牛顿迭代法等。在算法设计与分析中,需要考虑算法的时间复杂度和空间复杂度,以及算法的正确性和稳定性等问题。同时,还需要考虑算法的实际应用场景,如密码学、计算机图形学、计算机视觉等领域,以便选择合适的算法。 ### 回答2: 随着计算机科学和技术的不断发展,大整数计算逐渐成为计算机科学中的一个重要问题。 在日常生活中,我们经常需要处理大数乘除,例如:财务管理、密码学、科学计算等。这些雄厚的数字往往需要通过一些算法进行效的计算,这就需要一些针对大整数算法设计与分析。 大整数法:大整数法其实就是小学数学中的竖式法。我们可以先将两个大数的最低位相,将结果的个位保存下来,然后将得到的进位到下一位的计算中,直到所有位数都完。 大整数法:大整数法可以通过上相反数来实现。具体来说,我们将被数和数的位数对齐,然后按位进行法操作。需要注意的是,如果被数小于数,则需要借位。 大整数乘法:大整数乘法一般采用传统的乘法规则:将两个数的每一位相乘,并保持结果在正确的位上。需要注意的是,乘法可以通过使用一些技巧来速计算,例如:Karatsuba算法和分治法。 大整数除法:大整数除法一般采用长除法或者二分法进行。长除法与小学时候学习的是一样的,通过将被除数的一位一位与除数对齐,然后逐个相除。二分法则通过不断地将除数倍增来速计算。 总而言之,针对大整数乘除运算需要使用一些效的算法来进行计算。这些算法的设计和分析将会涉及到大量的数学原理和计算机科学的知识。同时,算法的实现也需要考虑到程序的效率和正确性。 ### 回答3: 随着计算机科学的发展,数字运算一直是算法设计和分析中应该掌握的基础部分。除了基本的整数运算,对于大整数、乘、除等运算也非常重要。大整数运算是指超过计算机原始字长的整数运算。 对于大整数、乘、除,我们需要使用不同的算法来执行。以下是针对大整数的四种运算算法简介: 大整数法:大整数法的基本思路很简单:将大整数分割成同样长度的数字部分并对相应的数字部分执行运算。如果有进位,进位值将被带入下一位的计算中。最终结果是一个大整数。 大整数法:大整数法比法稍微复杂一些。常用的方法是将 位权相同的两个位数相。如果被数小于数,则需要进行借位。借位意味着从位借一位,同时在当前位去10。如果需要多次借位,就需要连续地对更的位进行借位操作。 大整数乘法:用笔和纸做乘法的方法是讲算术方法还原成使用乘法表,那在算法设计中我们就可以使用竖式乘法。在这种方法中,使用被乘数和乘数的每一位进行部分乘法计算,并将结果起来。这种方法可以极大地降低计算量。 大整数除法:除法在大整数中是最棘手的运算。可以将从左到右的数字逐位与除数相除。如果它小于除数,则将下一位的数字添到被除数中。具体的除数和被除数都需要调整,以确保它们具有相同的数字长度。最终结果是商和余数。 总之,算法设计和分析中大整数、乘、除是非常重要的部分。在实际项目中,大整数的四种运算也经常出现。所以我们必须理解这些算法并熟练掌握它们。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wei *

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值