// 首先将两个加数都用字符串来表示,因为精度过大,所以使用常规的整型无法存储 // 字符串每个字符占用一个字节,可以存储高精度大数 // 定义A,B两个数组来记录两个加数的每一位 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');
// t 变量复用,同时记录进位情况和每一次对应位相加所计算得到的结果 // 最开始没有进位,初始化为 0 int t = 0; // 模拟加法列竖式计算过程 for (int i = 0; i < A.size() || i < B.size(); i ++ ) { if (i < A.size()) t += A[i]; if (i < B.size()) t += B[i]; // C 数组存取的为计算结果模10的余数,如:5 + 7 = 12,则 C 数组存取的为 2 【12 % 10 = 2】 C.push_back(t % 10); // t 变量记录进位,如果计算结果大于等于 10,则 t /= 10 为 1,否则为 0 t /= 10; }
// 在传递参数的过程中直接引用A、B两个数组的地址,就不需要额外再开辟新的空间,节省空间复杂度 vector<int> add(vector<int> &A, vector<int> &B) { // 定义 C 数组来存取两个数组每一位相加所得到的和 vector<int> C; // t 变量复用,同时记录进位情况和每一次对应位相加所计算得到的结果 // 最开始没有进位,初始化为 0 int t = 0; // 模拟加法列竖式计算过程 for (int i = 0; i < A.size() || i < B.size(); i ++ ) { if (i < A.size()) t += A[i]; if (i < B.size()) t += B[i]; // C 数组存取的为计算结果的余数,如:5 + 7 = 12,则 C 数组存取的为 2 【12 % 10 = 2】 C.push_back(t % 10); // t 变量记录进位,如果计算结果大于等于 10,则 t /= 10 为 1,否则为 0 t /= 10; } // 如果上述所有为相加之后还有进位,则直接在 C 数组中再记录一个高位 1 // 例如: 99 + 1 = 100,上述模拟结束之后 t 为 1,则在 C 数组中再记录高位 1 表示最终的进位 if (t) C.push_back(1); return C; }
intmain() { // 首先将两个加数都用字符串来表示,因为精度过大,所以使用常规的整形无法存储 // 字符串每个字符占用一个字节,可以存储高精度大数 // 定义A,B两个数组来记录两个加数的每一位 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');
// auto 可以自动获取变量类型,在此处相当于vector<int> auto C = add(A, B); // 因为存储时为逆向存储,所以输出的过程也需要逆向输出才可以还原本来的顺序 for (int i = C.size() - 1; i >= 0; i -- ) cout << C[i]; cout << endl;
vector<int> add(vector<int> &A, vector<int> &B) { vector<int> C; int t = 0; for (int i = 0; i < A.size() || i < B.size(); i ++ ) { if (i < A.size()) t += A[i]; if (i < B.size()) t += B[i]; C.push_back(t % 10); t /= 10; } if (t) C.push_back(1); return C; }
intmain() { 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');
auto C = add(A, B);
for (int i = C.size() - 1; i >= 0; i -- ) cout << C[i]; cout << endl;
// 判断 A, B 两个数的大小 // 如果返回 ture,直接计算 A - B 即可 if (cmp(A, B)) C = sub(A, B); // 否则,则需要通过计算 -(B - A) 来得到计算结果 // 代码表示的话就先输出一个负号,然后计算 B — A 的结果输出就好 else C = sub(B, A), cout << '-';
// 因为存储时为逆向存储,所以输出的过程也需要逆向输出才可以还原本来的顺序 for (int i = C.size() - 1; i >= 0; i -- ) cout << C[i]; cout << endl;
// t 变量复用,同时记录借位情况和每一次对应位相减的结果 // 最开始没有借位,初始化为 0 int t = 0; // 通过正负数比较可以保证 A 是大于等于 B 的,所以此处循环范围只需要【i < A.size()】 即可 for (int i = 0; i < A.size(); i ++ ) { // 如果有借位,首先要将被减数减去借位 // 此时 t 记录的就是减去借位后的被减数 t = A[i] - t; // 模拟被减数减去减数的过程 if (i < B.size()) t -= B[i]; // 此处 【(t + 10) % 10】 是将被减数大于减数和被减数小于减数的两种情况都合并表示了 // 如果被减数 >= 减数,则计算得到的 t 为正数,那么通过上式计算仍然为 t // 如果被减数 < 减数,则计算的 t 为负数,则模拟了借位后[t + 10 - B]的过程,通过上式计算的结果为t+10 C.push_back((t + 10) % 10); // 记录是否借位,如果此时 t 为负数,则说明被减数小于减数,那么则需要借位 // 此处将 t 置为 1,在下次循环的过程中可以减去借位 if (t < 0) t = 1; // 反之如果此处 t >= 0,那么则说明不需要借位,t 为 0 即可 else t = 0; }
// 如果两个数组长度相同的话,则比较每一位的大小 // 因为在存储的时候是逆序存储的,所以高位在数组的末尾,所以此处要从数组的末尾开始比较 for (int i = A.size() - 1; i >= 0; i -- ) if (A[i] != B[i]) return A[i] > B[i]; // 如果上述代码还没有返回结果的话,说明两个数字大小相同 // 大小相同的话符合 A >= B 的情况,返回 true returntrue; }
vector<int> sub(vector<int> &A, vector<int> &B) { vector<int> C; // t 变量复用,同时记录借位情况和每一次对应位相减的结果 // 最开始没有借位,初始化为 0 int t = 0; // 通过 cmp 函数可以保证 A 是大于等于 B 的,所以此处循环范围只需要【i < A.size()】 即可 for (int i = 0; i < A.size(); i ++ ) { // 如果有借位,首先要将被减数减去借位 // 此时 t 记录的就是减去借位后的被减数 t = A[i] - t; // 模拟被减数减去减数的过程 if (i < B.size()) t -= B[i]; // 此处 【(t + 10) % 10】 是将被减数大于减数和被减数小于减数的两种情况都合并表示了 // 如果被减数大于等于减数,则计算得到的 t 为正数,那么通过上式计算仍然为 t // 如果被减数小于减数,则计算的 t 为负数,则模拟了借位后 【t + 10 - B】 的过程 // 通过上式计算的结果为 t + 10 C.push_back((t + 10) % 10); // 记录是否借位,如果此时 t 为负数,则说明被减数小于减数,那么则需要借位 // 此处将 t 置为 1,在下次循环的过程中可以减去借位 if (t < 0) t = 1; // 反之如果此处 t >= 0,那么则说明不需要借位,t 为 0 即可 else t = 0; } // 因为计算得到的结果是存储到数组中的,所以可能会导致某些位结果为 0 // 如果高位计算为 0 则不需要输出,该操作就是去除前置 0 的过程 // 如果 C 数组的长度为 1,则说明计算结果就为 0,那么就不需要去除 0 // 如果长度大于 1,则从数组的最后一位开始判断,如果是 0 的话,那么则说明高位为 0,需要去除前置 0 // 例如 129 - 126,计算得到的结果在数组中表示为[3, 0, 0],所以需要从最后一位开始去除前置 0 // 如果不去除前置 0 的话,那么输出结果会是 003 while (C.size() > 1 && C.back() == 0) C.pop_back(); return C; }
intmain() { // 首先将被减数和减数都用字符串来表示,因为精度过大,所以使用常规的整形无法存储 // 字符串每个字符占用一个字节,可以存储高精度大数 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;
// 判断 A, B 两个数的大小 // 如果返回 ture,直接计算 A - B 即可 if (cmp(A, B)) C = sub(A, B); // 否则,则需要通过计算 -(B - A) 来得到计算结果 // 代码表示的话就先输出一个负号,然后计算 B — A 的结果输出就好 else C = sub(B, A), cout << '-';
// 因为存储时为逆向存储,所以输出的过程也需要逆向输出才可以还原本来的顺序 for (int i = C.size() - 1; i >= 0; i -- ) cout << C[i]; cout << endl;
for (int i = A.size() - 1; i >= 0; i -- ) if (A[i] != B[i]) return A[i] > B[i]; returntrue; }
vector<int> sub(vector<int> &A, vector<int> &B) { vector<int> C; int t = 0; for (int i = 0; i < A.size(); i ++ ) { t = A[i] - t; if (i < B.size()) t -= B[i]; C.push_back((t + 10) % 10); if (t < 0) t = 1; else t = 0; } while (C.size() > 1 && C.back() == 0) C.pop_back(); return C; }
intmain() { 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; if (cmp(A, B)) C = sub(A, B); else C = sub(B, A), cout << '-';
for (int i = C.size() - 1; i >= 0; i -- ) cout << C[i]; cout << endl;
return0; }
高精度乘法
实现思路详解
数据存储
注意乘法的运算是一个高精度数乘以一个低精度数
所以在存储的过程中,要将高精度数使用数组逆序按位存储,低精度数直接定义为整型即可。
1 2 3 4 5 6 7 8
string a; int b;
cin >> a >> b; // 用数组来按位逆序存储高精度数 vector<int> A; for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');
两个高精度数相乘的模拟过程太过繁琐,感兴趣可以自己研究,noi考试不会涉及。
模拟计算过程
注意乘法的运算是一个高精度数乘以一个低精度数
在乘法的模拟过程中,与平时手算的过程会有一点点区别
在平时手动列竖式的过程中,是每一位相乘,最后相加求积
而在代码模拟过程中,我们要将低精度的数看做整体来进行运算,不需要逐位相乘进行计算
1 2 3 4 5 6 7 8 9 10 11
for (int i = 0; i < A.size() || t; i ++ ) { // 将低精度数 b 看做整体进行计算 if (i < A.size()) t += A[i] * b; // 结果数组中存储的是每一次对应为相乘所得的结果 % 10的余数 C.push_back(t % 10); // 此处 t 变量记录进位情况,每次的进位是 t / 10 取整得到的结果 t /= 10; }
vector<int> mul(vector<int> &A, int b) { vector<int> C;
// t 变量复用 // 1、记录每一位相乘计算结果 // 2、记录进位情况 // 最开始进位为 0 ,所以将 t 初始化为 0 int t = 0; // A 为高精度数,所以循环次数以 A 的长度为限制 // 同时为了保证如果计算结果有 len(A) + 1 位,所以循环次数还要以 t 的存在情况为限制 // 例如 10 * 10 = 100 // 计算结果为 3 为,A 为 2 位 // 如果仅仅以 A 的位数为限制的话,会导致最终的计算结果错误 for (int i = 0; i < A.size() || t; i ++ ) { // 将低精度数 b 看做整体进行计算 if (i < A.size()) t += A[i] * b; // 结果数组中存储的是每一次对应为相乘所得的结果 % 10的余数 C.push_back(t % 10); // 此处 t 变量记录进位情况,每次的进位是 t / 10 取整得到的结果 t /= 10; }
vector<int> mul(vector<int> &A, int b) { vector<int> C; int t = 0; for (int i = 0; i < A.size() || t; i ++ ) { if (i < A.size()) t += A[i] * b; C.push_back(t % 10); t /= 10; }
vector<int> div(vector<int> &A, int b, int &r) { vector<int> C; r = 0; 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; }
intmain() { string a; vector<int> A;
int B; cin >> a >> B; for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');
int r; auto C = div(A, B, r);
for (int i = C.size() - 1; i >= 0; i -- ) cout << C[i];
cout << endl << r << endl;
return0; }
Q&A
在学习的过程中遇到任何的问题都可以评论到评论区,看到有价值的提问会记录到Q&A之中!
auto 是什么意思?
auto 关键字可以自动推导变量的类型,在使用过程中必须给定变量一个值才可以使用 auto
1 2 3
auto x = 10; // x的类型将被推导为int auto y = 3.14; // y的类型将被推导为double auto z = "hello"; // z的类型将被推导为const char*