【六十一】【算法分析与设计】高精度乘法和高精度除法

高精度乘以单精度

由于计算机的基本数据类型如 intlong 有其大小限制,当我们需要进行超出这些基本类型范围的计算时,就需要使用大数乘法技术。一种简单的大数乘法算法——单精度乘高精度数算法。这种方法特别适用于将一个大数(用字符串表示)与一个较小的整数相乘。

把迭代过程想象成许多节点的连接,每一个节点拥有的信息数量是相同的。

每当进入一个新的节点,信息变量存储的是上一个节点的信息。利用上一个节点的信息维护当前节点的信息,然后完成我们希望的工作。

这样的好处是明确统一了维护变量的时机----进入节点之后统一维护。

这样就避免了有一些变量在上一节点维护,有一些变量在当前节点维护而造成的混乱。

注意进入下一节点的条件和操作。

 
static string multiply_1(const string& a, const string& _b) {

    // 如果乘数是0或原数是0,直接返回"0"
    if (b == 0 || a == "0") return "0";
    
    string c;  // 结果字符串
    int b = stoi(_b);  // 将单精度数字符串转换为整数
    int i = a.size() - 1;  // 从字符串的最后一个字符开始处理
    int carry = 0;  // 进位初始化为0
    int sum = 0;  // 当前位的乘积结果加上进位

    while (i >= 0 || sum / 10) {  // 当还有数字需要处理或者进位不为0时继续循环
        carry = sum / 10;  // 更新进位
        int num = i >= 0 ? a[i] - '0' : 0;  // 获取当前位的数字,如果i小于0则取0
        sum = num * b + carry;  // 当前位数字与b相乘再加上进位
        c.push_back(sum % 10 + '0');  // 将得到的个位数转换为字符后加到结果字符串

        i--;  // 移动到下一位
    }

    reverse(c.begin(), c.end());  // 结果字符串需要翻转,因为加入时是从低位到高位
    return c;  // 返回结果字符串
}

高精度乘以高精度

在计算机编程和数值算法中,处理非常大的数字——特别是超出标准数据类型限制的数字——是一个常见且重要的问题。这是一个高精度乘法算法的实现,该算法使用字符串表示的两个大数进行相乘操作,返回它们的乘积,同样以字符串的形式表示。

本算法是高精度乘法的一种实现,通常称为“长乘法”或“列乘法”,该方法在手工乘法的基础上进行了数字化实现。

初始化变量:

 
int n = a.size(), m = b.size();
string c(n + m, '0');

  代码开始首先确定两个输入字符串 ab 的长度,分别为 nm。结果字符串 c 初始化为长度为 n + m 的字符串,所有位填充为 '0',这是基于最大位数的估算——两个数相乘的结果位数不会超过它们的位数之和。

嵌套循环处理每位乘法并累加:

for (int i = n - 1; i >= 0; --i) {
    int carry = 0;
    int sum = 0;
    for (int j = m - 1; j >= 0 || sum / 10; --j) {
        carry = sum / 10;
        sum = (c[i + j + 1] - '0') + (a[i] - '0') * (j >= 0 ? b[j] - '0' : 0) + carry;
        c[i + j + 1] = (sum % 10) + '0';
    }
}

  在这个核心的部分,两层循环分别遍历字符串 ab 的每个字符。外循环逆向遍历 a,内循环逆向遍历 b,同时考虑累计的进位(carry)。每次乘积计算后,将结果累加到对应的结果位置,并更新进位。如果内循环结束后仍有未处理的进位,它也将被正确地添加到结果中。

处理前导零并返回结果:

 
size_t startpos = c.find_first_not_of("0");
if (startpos != string::npos) {
    return c.substr(startpos);
}
return "0";

  乘法完成后,结果字符串 c 可能包含前导零。通过 find_first_not_of("0") 方法找到第一个非零字符的位置,从而移除前导零。如果字符串全为零(未找到非零字符),则返回 "0"

 
static string multiply_2(const string& a, const string& b) {
    // 获取两个数字的长度
    int n = a.size(), m = b.size();

    // 初始化结果字符串为n+m位,所有位初始化为'0'
    string c(n + m, '0');

    // 进位和临时求和变量初始化
    int carry = 0;
    int sum = 0;

    // 从a的最后一位开始,向前遍历每一位
    for (int i = n - 1; i >= 0; --i) {
        // 对每一位a[i],重置进位和求和变量
        carry = 0;
        sum = 0;

        // 内层循环:从b的最后一位开始,向前遍历每一位
        for (int j = m - 1; j >= 0 || sum / 10; --j) {
            // 更新进位值
            carry = sum / 10;

            // 计算当前位的乘积加上之前的进位和结果中已有的数值
            sum = (c[i + j + 1] - '0') + (a[i] - '0') * (j >= 0 ? b[j] - '0' : 0) + carry;

            // 更新结果字符串的对应位置
            c[i + j + 1] = (sum % 10) + '0';
        }
    }

    // 移除结果字符串中的前导零
    size_t startpos = c.find_first_not_of("0");
    if (startpos != string::npos) {
        return c.substr(startpos); // 如果找到非零字符,则返回从此位置到末尾的子字符串
    }

    // 如果全为零,则返回"0"
    return "0";
}

高精度除以单精度

在计算大数运算,特别是在处理如金融分析、科学计算和编程领域中的数据时,常常需要进行精确的除法操作。当我们面对的数字超出了标准数据类型(如 int 或 long)的处理范围时,就需要用到高精度的算法来处理这些数据。我们将详细介绍一种使用字符串表示的大数进行除法运算的高精度算法。

该算法的主要目的是将一个由字符串表示的大数 a 除以一个较小的整数 _b,并以字符串的形式返回商。这种方法在处理非常大的数时特别有用,因为它避开了传统数值类型的限制。

初始化和转换:

 
string c;  // 存放结果的字符串
int b = stoi(_b);  // 将字符串 _b 转换为整数 b
int nums = 0;  // 用于暂存中间结果的数值
int i = 0;  // 字符串 a 的索引,从第一个字符开始

初始阶段包括设置用于储存结果的字符串 c,将除数 _b 从字符串转换为整数 b,以及初始化辅助变量 nums(用于储存部分除法结果)和索引 i

遍历并计算每一步的商:

 
while (i < a.size()) {
    nums = nums % b;  // 取上一次的余数
    nums = nums * 10 + a[i] - '0';  // 将余数乘以10后加上当前字符表示的数字

    c.push_back(nums / b + '0');  // 将当前得到的商转换为字符后加入结果字符串

    ++i;  // 移动到下一个字符
}

在这个循环中,算法逐个处理字符串 a 中的每个数字字符。首先,计算当前累积值 numsb 的余数,然后将这个余数乘以10并加上新的数字,模拟手工除法中的“下拉”一位的步骤。计算得到的商被转换为字符并添加到结果字符串 c 中。

处理结果中的前导零:

 
size_t startpos = c.find_first_not_of("0");
if (startpos != string::npos)  // 如果找到非零字符
    return c.substr(startpos);  // 返回从这个位置到字符串末尾的子字符串
return "0";  // 如果全部都是零,则返回"0"

最后,算法检查结果字符串 c 中的前导零,并将它们移除。如果字符串中没有非零数字,函数返回 "0"

这种高精度除法算法适用于处理大规模数字,特别是当常规整型无法处理这些数据时。它模拟了人们在纸上进行除法的传统方法,因此容易理解和实现。

 
static string divide_1(const string& a, const string& _b) {
    string c;  // 存放结果的字符串
    int b = stoi(_b);  // 将字符串 _b 转换为整数 b
    int nums = 0;  // 用于暂存中间结果的数值
    int i = 0;  // 字符串 a 的索引,从第一个字符开始

    // 遍历字符串 a 的每一个字符
    while (i < a.size()) {
        nums = nums % b;  // 取上一次的余数
        nums = nums * 10 + a[i] - '0';  // 将余数乘以10后加上当前字符表示的数字

        c.push_back(nums / b + '0');  // 将当前得到的商转换为字符后加入结果字符串

        ++i;  // 移动到下一个字符
    }

    // 找到结果字符串中第一个不是'0'的字符位置
    size_t startpos = c.find_first_not_of("0");
    if (startpos != string::npos)  // 如果找到非零字符
        return c.substr(startpos);  // 返回从这个位置到字符串末尾的子字符串

    return "0";  // 如果全部都是零,则返回"0"
}

结尾

最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。

同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。

谢谢您的支持,期待与您在下一篇文章中再次相遇!

  • 25
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

妖精七七_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值