高精度算法详解
1.前言
近期学习了高精度的相关算法,现整理如下,最重要的是要理解算法的本质:模拟实现平时的竖式计算过程
补充相关习题链接,欢迎学习后练习:
高精度加法
2.内容
直接开始学习之路吧~~
1.高精度加法
先用一个例子,引入吧
0.实现过程中需注意的细节
1.逆序存储的意义?
为了利用数组,将数组其他元素自然而然置为0,便于运算
同时为了防止进位溢出
2.数组开多大?
一般而言,可尽可能开大;
(若为了严谨,其实比最大数组长度大1即可,因为可能最后会有进位)
3.输出?
由于是逆序存储的,所以输出时也应当逆序输出
4.前导0是什么?
前导0,就是在逆序相加的过程中,由于进位而产生余数为0且在数组的前面,则逆序输出的时候要注意不输出
(同时要考虑结果为0时的输出)
1.用普通数组实现
#include <bits/stdc++.h>
using namespace std; // 高精度加法
char a[505], b[505];
int m[505], n[505];
int main()
{
cin >> a >> b;
int len1 = strlen(a);
int len2 = strlen(b);
//1.转换为字符并逆序存储
for (int i = len1 - 1, j = 1; i >= 0; i--, j++)
m[j] = a[i] - '0';
for (int i = len2 - 1, j = 1; i >= 0; i--, j++)
n[j] = b[i] - '0';
int len = max(len1, len2);
//相加
for (int i=1; i <= len; i++)
{
m[i]+= + n[i];
m[i + 1] += m[i] / 10; //考虑进位
m[i] %= 10;
}
//考虑前导0
if (m[len + 1]) //第一位是否为0
cout << m[len +1];
else
while (m[len] == 0 && len> 1)
len--;
for (int j = len; j >= 1; j--)
cout << m[j];
return 0;
}
2.用结构体实现
#include <bits/stdc++.h>
using namespace std; //2.方法2 用结构体
struct Hugeint { //大整型结构体(有必要初始化)
int num[505] = {0}; //存数字
int len = 0; //存长度
};
Hugeint strtoint(string a)
{
Hugeint ans;
//补充 .length()只可获取字符串长度 .size()除此之外,还可获取vector长度
for (int i = a.length() - 1; i >= 0; i--)
ans.num[++ans.len] = a[i]-'0'; //下标从1开始,逆序存储数组
return ans;
}
Hugeint add(Hugeint m, Hugeint n)
{
Hugeint ans;
ans.len = max(m.len, n.len);
for (int i = 1; i <= ans.len; i++)
{
ans.num[i] += m.num[i] + n.num[i]; //相加存储到ans
ans.num[i + 1] += ans.num[i] / 10; //大于10进位
ans.num[i] %= 10; //留下余数
}
//处理进位溢出
if (ans.num[ans.len + 1] > 0) //如果最后一位的下一位有数字,说明进位了,则扩大ans.len
ans.len++;
return ans;
}
void print(Hugeint z)
{
for (int i = z.len; i >= 1; i--) //与上面逆序存储保持一致
{
cout << z.num[i];
}
}
int main()
{
//1.先当成字符串输入
char a[505], b[505];
cin >> a >> b;
//2.将a b字符串里的元素转换为int类型,并存储到m.num
Hugeint m = strtoint(a);
Hugeint n = strtoint(b);
//3.将m,n中的数据相加(注意处理进位溢出)
Hugeint z = add(m, n);
//4.逆序打印
print(z);
return 0;
}
3.vector数组(推荐-)
#include <iostream>
using namespace std;
#include <vector>
int main()
{
string x, y, ans;
int lenx, leny;
cin >> x >> y;
lenx = x.length();
leny = y.length();
if (lenx < leny)
{
swap(lenx, leny);
swap(x, y);
ans = "-";
}
else if (lenx == leny)
{
for (int i = 0; i < lenx; i++)
{
if (x[i] < y[i])
{
swap(x, y);
ans = "-";
break;
}
}
}
else
ans = "";
//新建数组
vector <int> a(lenx);
vector <int> b(lenx);
//逆序存储
for (int i = lenx - 1, k = 0; i >= 0; i--, k++)
a[k] = x[i] - '0';
for (int i = leny - 1, k = 0; i >= 0; i--, k++)
b[k] = y[i] - '0';
//相减
for (int i = 0; i < lenx; i++)
{
a[i] -= b[i];
if (a[i] < 0)
{
a[i] += 10;
a[i + 1] -= 1;
}
}
//处理前导0
while (a[lenx-1] == 0 && lenx > 1)
lenx--;
//输出
if (a[lenx-1])
cout << ans;
for (int i = lenx - 1; i >= 0; i--)
cout << a[i];
return 0;
}
vector (推荐S+)
//快乐的写板子时间~~
//高精度加法
#include <iostream>
using namespace std;
#include <vector>
#include <cstring>
#include <algorithm>
vector <int> Add(vector <int>& A, vector <int>& B)
{
vector <int> res;
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];
res.push_back(t % 10);
t /= 10; //记得是 / = 更新操作
}
if (t) //最后有进位
res.push_back(1);
return vector <int>(res.rbegin(), res.rend());
}
int main()
{
string a, b;
vector <int> A, B;
cin >> a >> b;
//逆序字符串(或者可以在下一步 逆序 vector数组)
reverse(a.begin(), a.end());
reverse(b.begin(), b.end());
//存储
for (auto const x : a) A.push_back(x-'0'); //记得 - ‘0’
for (auto const x : b) B.push_back(x - '0');
//计算(返回正序的数组)
auto C = Add(A, B);
//打印
for (auto const x : C)
printf("%d", x); //也许更快?
return 0;
}
3.重载+运算符 同时考虑小数
//重载+ 运算符 来计算高精度加法(考虑小数呢? 分成两部分)
//小数同时要考虑补足相同的位数 以及最终的进位!
#include <iostream>
using namespace std;
#include <vector>
#include <algorithm>
int k; //记录是否为小数加小数
class INT {
public:
INT() :s("") {};
INT(string sc) :s(sc) {};
vector <int> operator +(INT y) {
vector <int> res;
string a = this->s, b = y.s;
reverse(a.begin(), a.end());
reverse(b.begin(), b.end());
int t = 0;
for (int i = 0; i < a.size(); i++)
{
t += a[i] - '0';
if (!i && k) k = 0, t += 1; //特判小数点后面的有进位
if (i < b.size()) t += b[i] - '0';
res.push_back(t % 10);
t /= 10; //保留进位
}
if (t) //考虑进位
res.push_back(1);
while (res.back() == 0 && res.size() > 1) res.pop_back(); //去除前导0
return vector <int>(res.rbegin(), res.rend());
}
private:
string s;
};
int main()
{
string a, b;
cin >> a >> b;
int fa = a.find("."), fb = b.find(".");
//cout << fa << ' ' << fb;
if (fa!=-1&& fb!= -1) //小数相加
{
string c = a.substr(0, fa), d = b.substr(0, fb);
string e = a.substr(fa+1), f = b.substr(fb+1);
//cout << c << ' ' << d << ' ' << e << ' ' << f << endl;
if (c.size() < d.size())
swap(c, d);
if (e.size() < f.size())
swap(e, f);
while (f.size() < e.size()) f.push_back('0'); //注意小数相加的规则 ~ //将短的补足0 //注意补足的是 '0' 直接写成0则表示为NULL
//cout << e << ' ' << f << endl;
INT x(c), y(d), u(e), v(f);
vector <int> res2 = u + v; //先计算小数
if (res2.size() > e.size()) //有进位
{
k = 1;
res2.erase(res2.begin()); //删除第一个元素
}
vector <int> res1 = x + y;
for (auto x : res1)
printf("%d", x);
cout << ".";
for (auto x : res2)
printf("%d", x);
}
else if (fa!= -1 || fb != -1) //小数和整数相加
{
string c, d;
string h;
if (fa == -1) //b为小数
{
c = a;
d = b.substr(0, fb);
h = b.substr(fb + 1);
}
else //a为小数
{
c = b;
d = a.substr(0, fa);
h = a.substr(fa + 1);
}
if (c.size() < d.size())
swap(c, d);
INT x(c), y(d);
vector <int> res = x + y;
for (auto x : res)
cout << x;
cout << "." << h;
}
else //整数与整数相加
{
//令a为较长的串
if (a.size() < b.size())
swap(a, b);
INT x(a), y(b); //y
vector <int> res = x + y;
for (auto x : res)
cout << x;
}
return 0;
}
测试样例1:(小数加小数)
测试样例2:(整数加小数)
2.高精度减法
1.vector普通
#include <iostream>
using namespace std;
#include <vector>
int main()
{
string x, y, ans;
int lenx, leny;
cin >> x >> y;
lenx = x.length();
leny = y.length();
if (lenx < leny)
{
swap(lenx, leny);
swap(x, y);
ans = "-";
}
else if (lenx == leny)
{
for (int i = 0; i < lenx; i++)
{
if (x[i] < y[i])
{
swap(x, y);
ans = "-";
break;
}
}
}
else
ans = "";
//新建数组
vector <int> a(lenx);
vector <int> b(lenx);
//逆序存储
for (int i = lenx - 1, k = 0; i >= 0; i--, k++)
a[k] = x[i] - '0';
for (int i = leny - 1, k = 0; i >= 0; i--, k++)
b[k] = y[i] - '0';
//相减
for (int i = 0; i < lenx; i++)
{
a[i] -= b[i];
if (a[i] < 0)
{
a[i] += 10;
a[i + 1] -= 1;
}
}
//处理前导0
while (a[lenx-1] == 0 && lenx > 1)
lenx--;
//输出
if (a[lenx-1])
cout << ans;
for (int i = lenx - 1; i >= 0; i--)
cout << a[i];
return 0;
}
2.vector (推荐S+)
//高精度减法
#include <iostream>
using namespace std;
#include <vector>
#include <cstring>
#include <algorithm>
vector <int> Sub(vector <int>& A, vector <int>& B)
{
vector <int> res;
int t = 0;
for (int i = 0; i < A.size(); i++)
{
t = A[i] - t;
if (i < B.size()) t -= B[i];
res.push_back((t + 10) % 10);
if (t < 0) t = 1;
else t = 0;
}
while (res.size() > 1 && res.back() == 0) res.pop_back();
return vector <int>(res.rbegin(), res.rend());
}
bool cmp(string & a,string & b) //判断是否有 a > b
{
if (a.size() < b.size())
return false;
if (a.size() == b.size())
{
for (int i = 0; i < a.size(); i++)
if (a[i] != b[i])
return a[i] > b[i];
}
return true; //包含 a.size()>b.size() 以及 a==b
}
int main()
{
string a, b, c;
vector <int> A, B;
cin >> a >> b;
if (!cmp(a, b))
c = "-", swap(a, b);
for (auto const x : a) A.push_back(x - '0');
for (auto const x : b) B.push_back(x - '0');
reverse(A.begin(), A.end());
reverse(B.begin(), B.end());
auto C = Sub(A, B);
cout << c;
for (auto const x : C)
printf("%d", x);
return 0;
}
3.高精度乘法
P1303 A*B Problem
主要思想在于 相乘后错位存储
//数据范围10^4000
#include <iostream>
using namespace std;
#include <algorithm>
#include <vector>
int main()
{
//输入
string a, b;
cin >> a >> b;
if (a.size() < b.size()) swap(a, b); //保证a长于b
reverse(a.begin(), a.end());
reverse(b.begin(), b.end());
//逆序存储
vector<int> ta, tb;
for (auto x : a) ta.emplace_back(x - '0');
for (auto x : b) tb.emplace_back(x - '0');
//相乘,错位存储
vector<int> res(5000); //数组大小开为5000
int lena = ta.size(), lenb = tb.size();
for (int i = 0; i < lena; ++i)
for (int j = 0; j < lenb; ++j)
res[i + j] += ta[i] * tb[j];
//处理进位
int len = lena * 2;
for (int i = 0; i < len; ++i)
{
res[i + 1] += res[i] / 10;
res[i] %= 10;
}
//处理前导0
while (res[len] == 0 && len > 0) len--;
//逆序输出 或者先逆序,再输出 均可
for (int i = len; i >= 0; --i) printf("%d", res[i]);
return 0;
}
4.高精度除法(高精度/低精度)
5.高精度除法(高精度/高精度)
3.总结
只详细介绍了高精度加法,但是其思想是一致的,阅读代码及注释,应当是能理解的。(不要懒惰)
4.更新日志
2022.5.12 整理上传高精度加法
2022.5.17 完善高精度加法并上传高精度减法
欢迎评论留言、指正~~