Initial commit: C++ courseware

This commit is contained in:
xiaolinquan committed 2026-11-05 22:51:02 +08:00
commit 4a4ee7c287
1789 files changed
+373489

No files matched your search

@@ -0,0 +1,120 @@
# **C++ 高精度减法讲义**
#### 1. 思想
既然高精度加法可以通过“反转 -> 补零 -> 逐位相加”的流程简化计算,减法是否也可以呢?
答案是肯定的!通过**完全相同的预处理步骤**,我们同样可以将减法问题转化为一个从左到右的、下标对齐的简单循环。
**产生了新的问题:**
我们已经知道如何用字符串来模拟大数的加法。减法同样可以模拟,但比加法多了三个问题:
* **预处理**:减法必须先判断大小,确定结果的符号。
* **计算**:将进位(`carry`) 逻辑替换为借位(`borrow`) 逻辑。
* **后处理**:减法的结果可能产生前导零 (例如 `123-120=003`),需要特殊处理。
#### 2. 算法步骤详解
**判断符号 (这是减法独有的)**
* 在计算前,必须比较 `a` 和 `b` 的大小。
* 创建一个 `bool negative = false;` 标志位。
* 如果 `b` 比 `a` 大,说明结果是负数。我们记下 `negative = true;`,然后交换 `a` 和 `b`。
* **目的**:通过这一步,我们保证了接下来的计算永远是 `大数 - 小数`,极大地简化了借位逻辑。
**反转与补零 (与加法完全一致)**
* 将 `a` 和 `b` (已经是大数和小数了) 同时反转。
* 在较短的字符串**末尾**补 `0`,使其与较长的等长。
* **目的**:让原本的“个位、十位、百位...”对齐到字符串的索引 `0, 1, 2...`,从而可以使用一个简单的 `for` 循环从左到右处理。
**核心循环——逐位相减**
这是算法的核心,我们将在这里实现借位逻辑。
* 创建一个 `int borrow = 0;` 用于记录借位。
* 循环从 `i = 0` 开始:
1. **取数计算**:`diff = (a[i]-'0') - (b[i]-'0') - borrow;`
* `a[i]` 是被减数,`b[i]` 是减数。
* `borrow` 是来自**更低位**的借位请求。如果上一轮(`i-1`)发生了借位,`borrow` 就是 `1`,意味着当前位的 `a[i]` 已经被借走了一个 `1`。
2. **判断并处理借位**:
* 如果 `diff < 0`,说明当前位不够减。
* `diff += 10;` (向更高位借来的 `1` 在本位体现为 `10`)。
* `borrow = 1;` (设置借位标记,告诉**下一轮循环**,它的 `a[i+1]` 被借走了)。
* 如果 `diff >= 0`,说明够减。
* `borrow = 0;` (重置借位标记)。
3. **拼接结果**:`ans += to_string(diff);`
**去除前导零 (在反转前操作)**
* 我们得到的 `ans` 是一个倒序的结果。例如,`123 - 120` 的计算结果 `3`,在 `ans` 中会是 `"300"`。
* 如果直接反转,会得到 `"003"`。
* 所以,我们必须在**最终反转之前**,去除 `ans` **末尾**多余的 `0`。
* **注意**:要保留最后一位 `0` (例如 `10 - 10` 的结果是 `0`),所以循环条件是 `ans.size() > 1`。
**最终反转与添加符号**
* 将清理好的 `ans` 字符串反转回来,得到正确的顺序。
* 检查设置的 `negative` 标志位。如果为 `true`,在结果的最前面加上 `"-"`。
#### 3. 完整代码实现
```cpp
// 比较函数,判断 a 是否大于 b
bool isGreater(string a, string b) {
if (a.length() != b.length()) {
return a.length() > b.length();
}
return a > b; // 长度相同时,直接按字典序比较
}
string sub(string a, string b) {
// 判断符号,并确保 a >= b
bool negative = false;
if (isGreater(b, a)) {
negative = true;
swap(a, b);
}
// 将两个输入字符串都反转
reverse(a.begin(), a.end());
reverse(b.begin(), a.end());
// 在较短的字符串末尾补 '0',使其与较长的等长
while (b.size() < a.size()) b += '0';
string ans = "";
int borrow = 0;
// 核心循环,逐位相减
for (int i = 0; i < a.size(); i++) {
// 计算当前位的差值(考虑上一位的借位)
int diff = (a[i] - '0') - (b[i] - '0') - borrow;
// 判断是否需要向更高位借位
if (diff < 0) {
// 当前位不够减,需要借位
diff += 10; // 借来的 1 在本位变成了 10
borrow = 1; // 产生了借位,标记下一轮计算要被借走
} else {
// 当前位够减,无需借位
borrow = 0; // 重置借位标记
}
// 将当前位的结果拼接到 ans 末尾
ans += to_string(diff);
}
// 在反转前,把倒序结果末尾的 0 去掉 (处理前导零)
while (ans.size() > 1 && ans.back() == '0') {
ans.pop_back();
}
// 将最终结果反转回来
reverse(ans.begin(), ans.end());
// 如果结果为负,添加负号
if (negative) ans = "-" + ans;
return ans;
}
```