- 算法学习
高精度减法
- @ 2025-10-1 10:40:31
1. 概念引入与基本原则
高精度减法用于计算超大整数之间的减法。与高精度加法类似,其核心思想是模拟我们日常进行竖式减法的过程。
基本原则(核心区别):
在小学,我们学习竖式减法时,必须保证被减数大于或等于减数。
- 如果 ,结果为 。
- 如果 ,我们需要计算 ,并在结果前添加一个负号(
-)。
因此,高精度减法的第一步是确定被减数和减数,并预判结果的正负号。
2. 实现步骤与代码解析
我们将以您提供的代码为例,分步解析高精度减法的实现。
步骤 A:输入、判断大小与预处理负号
程序首先输入两个数 和 (以字符串形式),然后判断 和 的大小。
代码解析:
char a[200],b[200];
cin >> a >> b;
int lena = strlen(a);
int lenb = strlen(b);
// 判断 A 是否小于 B:
// 1. A 的长度 < B 的长度,或者
// 2. 长度相等,但字符串 A 小于 B (strcmp(a,b)<0)
if (lena < lenb || (lena == lenb && strcmp(a, b) < 0))
{
swap(a, b); // 交换 A 和 B,确保 A >= B
cout << "-"; // 提前输出负号
}
// 重新获取可能的交换后的长度
lena = strlen(a);
lenb = strlen(b);
- 目标: 确保后面的计算始终是 较大数减去较小数。
- 如果发现 ,则交换 ,并立即输出负号,以确保最终结果正确。
步骤 B:大整数的逆序存储
与加法相同,我们使用逆序存储,将个位放在数组的低位(下标 1)。
// 将 A 逆序存入 a1,从下标 1 开始存个位
for (int i = 1; i <= lena; i++)
{
a1[i] = a[lena - i] - '0';
}
// 将 B 逆序存入 b1,从下标 1 开始存个位
for (int i = 1; i <= lenb; i++)
{
b1[i] = b[lenb - i] - '0';
}
// 此时,a1[i] 存储的是 A 的第 i 位数字,b1[i] 存储的是 B 的第 i 位数字。
步骤 C:模拟竖式逐位相减与借位处理(核心)
这是减法的核心循环,它负责模拟从低位到高位的减法,并实时处理借位。
关键逻辑: 从低位开始,逐位相减 :
- 无需借位: 如果 ,则直接相减。
- 需要借位: 如果 ,则需要向高位借 1。
- 借位操作:将当前位 加上 10。
- 高位处理:将高一位 减去 1(即被减数的下一位减 1)。
代码解析:
int lenc = 1; // 当前计算到的位数
int maxlen = max(lena, lenb); // 决定循环次数
while (lenc <= maxlen)
{
// 1. 判断是否需要借位
if (a1[lenc] < b1[lenc])
{
a1[lenc] += 10; // 当前位借 1,数值上加 10
a1[lenc + 1]--; // 高一位被减数 (a1[lenc+1]) 减 1
}
// 2. 计算当前位的结果
c[lenc] = a1[lenc] - b1[lenc];
lenc++; // 移向下一位
}
// 注意:这段代码**没有显式的进位变量 x**。
// 因为借位操作是直接修改了被减数 a1[lenc+1],将借位的影响提前传递给了下一位的计算。
步骤 D:处理前导零与输出结果
由于结果数组 c 是逆序存储的,我们需要逆序输出。
减法结果可能存在多个高位零(例如 ),这些零是无效的,需要跳过。
// lenc 在循环结束后会多加 1,所以它是结果最高位再往上的一位。
// lenc-- 是因为上一个循环多加了 1,c[lenc] 在这里已经是 0 了。
while (c[lenc] == 0 && lenc > 1)
{
lenc--; // 缩短结果长度,跳过最高位的无效 0
}
// 逆序输出数组 c,从最高位开始
for (int i = lenc; i >= 1; i--)
{
cout << c[i];
}
return 0;
3. 完整代码(高精度减法)
#include <bits/stdc++.h>
using namespace std;
int a1[200],b1[200],c[200]; // 数组用于逆序存储大整数的每一位
int main()
{
char a[200],b[200];
cin >> a >> b;
int lena = strlen(a);
int lenb = strlen(b);
// --- 步骤 A: 预处理负号,确保 A >= B ---
if (lena < lenb || (lena == lenb && strcmp(a, b) < 0))
{
swap(a, b); // 交换,确保 A >= B
cout << "-"; // 输出负号
}
// 重新获取可能交换后的长度
lena = strlen(a);
lenb = strlen(b);
// --- 步骤 B: 逆序存储 ---
for (int i = 1; i <= lena; i++)
{
a1[i] = a[lena - i] - '0';
}
for (int i = 1; i <= lenb; i++)
{
b1[i] = b[lenb - i] - '0';
}
// --- 步骤 C: 模拟竖式减法(借位)---
int lenc = 1;
int maxlen = max(lena, lenb);
while (lenc <= maxlen)
{
// 如果当前位 a1 < b1,进行借位
if (a1[lenc] < b1[lenc])
{
a1[lenc] += 10; // 当前位加 10
a1[lenc + 1]--; // 高一位减 1
}
// 计算当前位结果
c[lenc] = a1[lenc] - b1[lenc];
lenc++;
}
// --- 步骤 D: 处理前导零与输出 ---
// lenc-- 确保我们从最高的非零位开始输出
lenc--; // 因为循环中 lenc 多加了一次
while (c[lenc] == 0 && lenc > 1)
{
lenc--;
}
for (int i = lenc; i >= 1; i--)
{
cout << c[i];
}
// 额外处理:如果结果是 0,只输出一个 0
if (lenc == 1 && c[1] == 0) {
// 这种情况已经被上述代码正确处理,因为 lenC 会停在 1,且 c[1] 为 0,输出 c[1]
}
return 0;
}
0 条评论
目前还没有评论...