高精度除法

高精度除法

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

所用方法和基本原理

  1. 数据预处理
        - 首先获取两个字符串 Ab 的长度 ALenbLen
        - 创建一个数组 divA 来存储 A 的每一位数字,从字符串 A 的末尾开始遍历,将每个字符转换为数字存入数组。
        - 将字符串 b 转换为一个整数 divb。通过遍历字符串 b,利用 Math.pow(10, j) * (b.charAt(i) - '0') 这种方式,将每一位数字乘以对应的权值并累加,从而得到 b 对应的整数值。
  2. 模拟除法运算
        - 定义一个长度为 ALen + 1 的结果数组 res 用于存储商。
        - 初始化一个变量 t 表示余数,初始值为 0。
        - 从 A 的高位开始遍历 divA 数组。对于每一位,先将当前余数 t 乘以 10 再加上当前位的数字,得到 tmp。然后计算 tmp 除以 divb 的结果作为当前位的商存入 res 数组,同时更新余数 ttmp 除以 divb 的余数。
  3. 结果处理
        - 计算结束后,t 即为最终的余数 remainder
        - 从 res 数组的高位开始跳过前导 0,找到第一个非零元素的位置 startIdx
        - 将从 startIdx 开始到数组开头的元素依次添加到 StringBuffer 中,得到商的字符串表示。
        - 最后返回一个包含商和余数的字符串数组。

代码及注释

public class BigNumberDivision {
    // 实现两个非负整数字符串的除法,返回商和余数
    public static String[] div(String A, String b) {
        // 计算字符串A和b的长度
        int ALen = A.length();
        int bLen = b.length();
        // 创建数组divA用于存储字符串A按位拆分后的数字
        int[] divA = new int[ALen];
        // 将字符串b转换为整数divb
        int divb = 0;
        // 将字符串A按位拆分并存入divA数组
        for (int i = ALen - 1, j = 0; i >= 0; i--, j++) {
            divA[j] = A.charAt(i) - '0';
        }
        // 将字符串b转换为整数
        for (int i = bLen - 1, j = 0; i >= 0; i--, j++) {
            divb += Math.pow(10, j) * (b.charAt(i) - '0');
        }


        // 定义结果数组res用于存储商
        int[] res = new int[ALen + 1];
        // 定义变量t表示余数,初始值为0
        int t = 0;
        // 模拟除法运算
        for (int i = ALen - 1; i >= 0; i--) {
            // 计算当前位的临时值
            int tmp = t * 10 + divA[i];
            // 计算当前位的商
            res[i] = tmp / divb;
            // 更新余数
            t = tmp % divb;
        }
        // 最终的余数
        int remainder = t;


        // 去除商的前导0
        int startIdx = ALen;
        while (res[startIdx] == 0) {
            startIdx--;
            if (startIdx < 0) {
                // 如果商全为0,直接返回["0", 余数]
                return new String[]{"0", String.valueOf(remainder)};
            }
        }


        // 创建StringBuffer用于构建商的字符串
        StringBuffer resStr = new StringBuffer();
        // 将商的有效位添加到StringBuffer中
        for (int i = startIdx; i >= 0; i--) {
            resStr.append(res[i]);
        }
        // 返回包含商和余数的字符串数组
        return new String[]{resStr.toString(), String.valueOf(remainder)};
    }
}

举例说明

假设 A = "123"b = "4"

  1. 数据预处理
        - ALen = 3bLen = 1
        - divA = [3, 2, 1]
        - divb = 4
  2. 模拟除法运算
        - 第一次循环:i = 2tmp = 0 * 10 + 1 = 1res[2] = 1 / 4 = 0t = 1 % 4 = 1
        - 第二次循环:i = 1tmp = 1 * 10 + 2 = 12res[1] = 12 / 4 = 3t = 12 % 4 = 0
        - 第三次循环:i = 0tmp = 0 * 10 + 3 = 3res[0] = 3 / 4 = 0t = 3 % 4 = 3。此时 res = [0, 3, 0]remainder = 3
  3. 结果处理
        - 跳过前导 0,startIdx = 1
        - 将 res[1]res[0] 依次添加到 resStr 得到 "30"
        - 最终返回 ["30", "3"],即商为 30,余数为 3。

方法的优劣

  1. 时间复杂度
        - 预处理部分,将 A 拆分为数组的时间复杂度为 (O(m)),其中 mA 的长度;将 b 转换为整数的时间复杂度为 (O(n)),其中 nb 的长度。模拟除法运算部分,时间复杂度为 (O(m))。总体时间复杂度为 (O(m + n)),假设 (m \geq n)。
  2. 空间复杂度
        - 创建了长度为 ALen + 1 的数组 res 和长度为 ALen 的数组 divA,空间复杂度主要由这两个数组决定,为 (O(m)),其中 mA 的长度。同时在计算 divb 时,可能涉及与 n 相关的临时空间,但总体空间复杂度为 (O(m))。

优点
    - 实现思路较为直观,模拟了手工除法的过程,代码逻辑相对清晰,容易理解和实现。
    - 能够处理较大整数的除法运算,不受编程语言内置整数类型范围的限制。

缺点
    - 当 b 是一个非常大的数时,将其转换为整数可能会导致整数溢出,限制了处理大数的能力。
    - 空间复杂度较高,需要额外的数组来存储中间结果,对于非常大的数,内存消耗较大。
    - 对于大规模数据,该算法的时间复杂度不是最优的,存在更高效的大数除法算法。

posted @ 2025-06-30 01:22  起个数先  阅读(22)  评论(0)    收藏  举报