题目
C++编写大整数类型BigInteger详细解析
叙述
大整数类在算法题中一般出现在精度上的问题,虽然Java上有现成的大整数类,而且还比较好用,但是我们也可以了解一下大整数类的实现,然后接下来的代码和算法入门竞赛第二版的实现是差不多的,但是我对整个算法做了一定的分析
代码
struct BigInteger
{
static const int BASE = 100000000;
static const int WIDTH = 8;
vector<int> s;
// 构造函数
BigInteger(long long num = 0)
{
// 调用 = 的重载符号
*this = num;
}
// 重载 = 符号
// 当输入在long long范围
BigInteger operator = (long long num)
{
s.clear();
do {
// 每 8 位存储一次, 不足 8 位算一个
// 从低位到高位的存储
// 比如 123456789
// 23456789 1 按照出栈顺序即可
s.push_back(num % BASE);
num /= BASE;
} while (num > 0);
return *this;
}
// 超过long long范围
BigInteger operator = (const string& str)
{
s.clear();
// len = 计算整个 str 有多少个八位数, 以及最后不住八位的算上一组
int x, len = (str.length() - 1) / WIDTH + 1;
for (int i = 0; i < len; ++i)
{
// 从字符串的后八位开始存储
// 从低位到高位存储
// 比如 123456789123456789
// 23456789 34567891 12 按照出栈顺序即可
int end = str.length() - i * WIDTH;
int start = max(0, end - WIDTH);
// 将str获得八位,然后转化为c类型字符串,然后使用sscanf转换为整数x, 最后存储到s中
sscanf(str.substr(start, end - start).c_str(), "%d", &x);
s.push_back(x);
}
return *this;
}
// 重载输出 << 符号
friend ostream& operator << (ostream& out, const BigInteger& x)
{
// 先出去第一个高位,以免中间不足八位使用0补充
// 比如 123
// 如果放入循环后输出就是 00000123
out << x.s.back();
// 为啥是s.size()-2, 由于下标从0开始,并且前面输出了最后一个数字,所以剩下的位置为size()-2
for (int i = x.s.size() - 2; i >= 0; i--)
{
// 用于转化的字符数组
char buf[20];
// 将s[i]中的数字转换为八个字符的数组,如果不足八位的使用0补全
sprintf(buf, "%08d", x.s[i]);
// 将buf中每个字符添加到输出流中
for (int j = 0; j < strlen(buf); ++j) out << buf[j];
}
return out;
}
// 重载输入 << 符号
friend istream& operator >> (istream& in, BigInteger& x)
{
string s;
// 如果输入为空,返回in
if (!(in >> s)) return in;
// 如果不为空,就将赋值到s中给复制给x
// 调用 BigInteger的 = 符号
x = s;
return in;
}
// 四则运算符号重载
// 加法 +
BigInteger operator + (const BigInteger& b) const
{
// 加法涉及到超出进位
BigInteger c;
c.s.clear();
for (int i = 0, g = 0; ; ++i)
{
// 循环跳出的条件是 g == 0 表示没有任何的数可以存储或者相加的时候,即最长的向量遍历完后
// i >= s.size()是当前BigInteger遍历完后
// i >= b.s.size()是b的遍历完后
// 三个条件都满足的时候,才能说都相加完全
if (g == 0 && i >= s.size() && i >= b.s.size()) break;
// x存储上次循环超过八位的数
int x = g;
// i小于s中元素个数或者小于b.s的元素个数是就将其中的值加入到x中存储
if (i < s.size()) x += s[i];
if (i < b.s.size()) x += b.s[i];
// 处理当前“位”的数,求余数是获得前8位数字,放入新的BigIntger中
c.s.push_back(x % BASE);
// g是这次超过八位的数字
g = x / BASE;
}
return c;
}
// 减法 -
BigInteger operator - (const BigInteger& b)
{
BigInteger c;
c.s.clear();
if (*this > b)
{
int i, g;
for (i = 0, g = 0; ; ++i)
{
// 这里同加法的解释
if (g == 0 && i >= b.s.size()) break;
int x = g;
// 这里是如果小于后者,就向高位借 1
// 然后前者 加上基础 BASE
if (s[i] < b.s[i])
{
s[i + 1] -= 1;
s[i] = s[i] + BASE;
}
if (i < s.size()) x += s[i];
if (i < b.s.size()) x -= b.s[i];
c.s.push_back(x%BASE);
g = x / BASE;
}
// 这个地方继续上面的将s的复制到c中
for (int x = 0; i < s.size(); ++i)
{
x += s[i];
c.s.push_back(x%BASE);
x /= BASE;
}
}
return c;
}
// 比较运算符的重载
// 只需要一个小于重载后,就可以根据逻辑的推理,然后推出所有的符号
// 小于 <
bool operator < (const BigInteger& b) const
{
if (s.size() != b.s.size()) return s.size() < b.s.size();
for (int i = s.size() - 1; i >= 0; --i)
{
if (s[i] != b.s[i]) return s[i] < b.s[i];
}
return false;
}
// 大于 >
bool operator > (const BigInteger& b) const
{
return b < *this;
}
// 小于等于 <=
bool operator <= (const BigInteger& b) const
{
return !(b < *this);
}
// 大于等于 >=
bool operator >= (const BigInteger& b) const
{
return !(*this < b);
}
// 不等于 !=
bool operator != (const BigInteger& b) const
{
return b < *this || *this < b;
}
// 等于 ==
bool operator == (const BigInteger& b) const
{
return !(b < *this || *this < b);
}
};
总结
大部分的都总结了,但是乘法和除法以及其余的幂方算法没有添加,后面有机会能够实现,就继续分析,注释上几乎是对每个语句进行解释
参考资料
刘汝佳 算法竞赛入门经典第二版
c++大整数类的几种实现方法与解析