综述
最近闲来无事写了一个比较简单的高精度模板……想来之前没有总结过这一块的知识,就写一篇博客来简单记录一下吧。关于高精度模板的资料其实挺多的,有某个方法不是很理解的话可以再搜搜资料或者自己手动模拟一下。这个模板的效率肯定不是最好的,可能还会有隐藏的bug,仅给大家提供一种实现高精度的思路。
朴素想法
我们先从最简单的开始,如果让你实现一个正数的高精度加法,你会怎么做?大部分人的做法肯定是用数组或者链表来模拟这个过程,以数组为例,从低位到高位依次存储数字的每一位,那么加法无非就是从最低位开始逐位进行加法计算,进行进位。没错,这就是高精度算法的实现思路。不过为了简化算法,我们还需要使用一些技巧。比如,针对加法,我们仅计算同符号的大数相加,若不同符号则视为减法。针对减法,我们仅计算较大的数减去较小的数,然后通过取反运算将其扩展到全部情况。
那么乘法怎么计算呢?类似于竖式乘法,可以把乘数逐位拆分,然后再进行计算。很显然这样做的复杂度是比较高的,也有更加优秀的做法,不过这里就不讨论了~
比较复杂的是除法,这里提供一种思路吧,感觉复杂度还是比较高的。有点逆推的意思,不妨设
a
/
b
=
c
a/b=c
a/b=c,也即
a
=
b
?
c
a=b*c
a=b?c,此时把
c
c
c逐位拆分可得:
a
=
b
?
c
n
+
b
?
c
n
?
1
+
…
+
b
?
c
0
a=b*c_n+b*c_{n-1}+…+b*c_0
a=b?cn?+b?cn?1?+…+b?c0?。那么只要我们依次(从高位到低位)计算出
c
n
、
c
n
?
1
、
…
c_n、c_{n-1}、…
cn?、cn?1?、…,再把它们拼起来就可以得到
c
c
c了。显然只要确定了
c
i
c_i
ci?的位数,就可以通过减法来模拟出
c
i
c_i
ci?的值。这个位数其实是比较容易确定的,如果
a
a
a有
x
x
x位,
b
b
b有
y
y
y位,那么
c
c
c最多有
x
?
y
+
1
x-y+1
x?y+1位,逐位模拟即可。
压位
如果每次只存储数字的一位的话,是不是有点浪费?考虑到
l
o
n
g
?
l
o
n
g
long\ long
long?long可以存储
64
64
64位数据,同时为了避免乘法溢出,我们可以每次存储
8
8
8位——这就是压位操作。算法的思想还是通用的,只不过要把之前的一位当作八位来处理,可以降低算法的时间复杂度!
模板
愉快的模板时间~
#include<vector>
#include<iostream>
#include<string>
#define INF 0x3f3f3f3f
using namespace std;
class BigInteger
{
public:
using ll = long long;
BigInteger() {};
BigInteger(const string& s);
BigInteger(ll a) :BigInteger(to_string(a)) {}
BigInteger(const BigInteger& bInt) :nums(bInt.nums), isPositive(bInt.isPositive), length(bInt.length) {}
BigInteger(BigInteger&& bInt) noexcept :nums(move(bInt.nums)), isPositive(bInt.isPositive), length(bInt.length) {}
BigInteger(const vector<int>& vec, bool sign = true) :nums(vec), isPositive(sign) { cutLeadZero(); }
friend istream& operator >>(istream& is, BigInteger& bInt)
{
string s;
is >> s;
bInt = move(BigInteger(s));
return is;
}
friend ostream& operator <<(ostream& os, const BigInteger& bInt);
operator string() const;
const BigInteger& operator +() const { return *this; }
BigInteger operator -() const
{
BigInteger tmp(*this);
if (!tmp.isZero())
tmp.isPositive = !isPositive;
return tmp;
}
bool operator <(const BigInteger& bInt) const;
bool operator <=(const BigInteger& bInt) const;
bool operator ==(const BigInteger& bInt) const;
BigInteger operator +(const BigInteger& bInt) const;
BigInteger operator -(const BigInteger& bInt) const;
BigInteger operator *(const BigInteger& bInt) const;
pair<BigInteger, BigInteger> operator /(const BigInteger& bInt) const;
int operator[](int idx) const { return nums[idx]; }
BigInteger& operator =(const BigInteger& bInt)
{
if (bInt == *this)
return *this;
nums = bInt.nums;
isPositive = bInt.isPositive;
length = bInt.length;
return *this;
}
BigInteger& operator =(BigInteger&& bInt)noexcept
{
nums = move(bInt.nums);
isPositive = bInt.isPositive;
length = bInt.length;
return *this;
}
size_t size() const { return nums.size(); }
void cutLeadZero();
bool isZero() const;
BigInteger absValue() const
{
return move(BigInteger(nums));
}
static BigInteger e(size_t n)
{
if (n <= 0)
return move(BigInteger(vector<int>(1, 1)));
int m = n / digit;
n -= m * digit;
vector<int> ans(m);
string s = "1";
s += move(string(n, '0'));
ans.push_back(stoi(s));
return move(BigInteger(ans));
}
private:
vector<int> nums;
bool isPositive = 1;
int length = 0;
static int digit;
static int mod;
};
int BigInteger::digit = 8;
int BigInteger::mod = 100000000;
BigInteger::BigInteger(const string& s)
{
int n = s.size(), minIdx = 0;
if(s[0]=='-')
isPositive = false, minIdx = 1;
else if(s[0]=='+')
isPositive = true, minIdx = 1;
for (int i = n - 1; i >= minIdx; i -= digit)
{
int beg = max(minIdx, i - digit + 1);
nums.push_back(stoi(s.substr(beg, i - beg + 1)));
}
cutLeadZero();
}
ostream& operator <<(ostream& os, const BigInteger& bInt)
{
os << (string)bInt;
return os;
}
BigInteger::operator string() const
{
string ans;
if (!isPositive)
ans += "-";
int n = nums.size();
for (int i = n - 1; i >= 0; i--)
{
string s = to_string(nums[i]);
if (i != n - 1)
ans += string(digit - s.size(), '0');
ans += s;
}
return ans;
}
bool BigInteger::operator<(const BigInteger& bInt) const
{
if (isPositive && !bInt.isPositive)
return false;
if (!isPositive && bInt.isPositive)
return true;
bool flag = true;
if (!isPositive)
flag = false;
if (length < bInt.length)
return flag;
else if (length > bInt.length)
return !flag;
int n = size();
for (int i = n - 1; i >= 0; i--)
{
if (nums[i] < bInt[i])
return flag;
else if (nums[i] > bInt[i])
return !flag;
}
return false;
}
bool BigInteger::operator<=(const BigInteger& bInt) const
{
if (isPositive && !bInt.isPositive)
return false;
if (!isPositive && bInt.isPositive)
return true;
bool flag = true;
if (!isPositive)
flag = false;
if (length < bInt.length)
return flag;
else if (length > bInt.length)
return !flag;
int n = size();
for (int i = n - 1; i >= 0; i--)
{
if (nums[i] < bInt[i])
return flag;
else if (nums[i] > bInt[i])
return !flag;
}
return true;
}
bool BigInteger::operator==(const BigInteger& bInt) const
{
if (length != bInt.length)
return false;
int n = size();
for (int i = 0; i < n; i++)
if (nums[i] != bInt[i])
return false;
return true;
}
BigInteger BigInteger::operator+(const BigInteger& bInt) const
{
if (!bInt.isPositive)
return *this - (-bInt);
if (!isPositive)
return bInt - (-*this);
vector<int> ans;
int n = size(), m = bInt.size(), sum = 0, i = 0, j = 0;
while (i < n || j < m || sum)
{
if (i < n)
sum += nums[i++];
if (j < m)
sum += bInt[j++];
ans.push_back(sum % mod);
sum /= mod;
}
return move(BigInteger(ans, isPositive));
}
BigInteger BigInteger::operator-(const BigInteger& bInt) const
{
if (!bInt.isPositive)
return *this + (-bInt);
if (!isPositive)
return -((-*this) + bInt);
if (*this < bInt)
return -(bInt - *this);
vector<int> ans;
int i = 0, j = 0, n = size(), m = bInt.size(), sum = 0;
while (i < n || j < m || sum)
{
if (i < n)
sum += nums[i++];
if (j < m)
sum -= bInt[j++];
int flag = 0;
if (sum < 0)
{
flag = -1;
sum += mod;
}
ans.push_back(sum);
sum = flag;
}
return move(BigInteger(ans));
}
BigInteger BigInteger::operator*(const BigInteger& bInt) const
{
int n = size(), m = bInt.size();
vector<int> ans(n + m);
using ll = long long;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
ll tmp = ans[i + j] + nums[i] * 1ll * bInt[j];
ans[i + j] = tmp % mod;
ans[i + j + 1] += tmp / mod;
}
}
return move(BigInteger(ans, isPositive == bInt.isPositive));
}
pair<BigInteger, BigInteger> BigInteger::operator/(const BigInteger& bInt) const
{
BigInteger a = absValue();
BigInteger b = bInt.absValue();
if (b.isZero())
return pair<BigInteger, BigInteger>(*this, move(b));
if (a < b)
return pair<BigInteger, BigInteger>(move(BigInteger(0)), *this);
int len = a.length - b.length + 1;
string ans;
if (isPositive != bInt.isPositive)
ans = "-";
for (int i = 0; i < len; i++)
{
BigInteger tmp = e(len - i - 1) * b;
int times = 0;
while (tmp <= a)
{
a = a - tmp;
++times;
}
ans += times + '0';
}
a.isPositive = isPositive;
return pair<BigInteger, BigInteger>(move(BigInteger(ans)), move(a));
}
void BigInteger::cutLeadZero()
{
while (nums.size() > 1 && nums.back() == 0)
nums.pop_back();
if (nums.empty())
length = 0;
else
{
length = (nums.size() - 1) * digit + to_string(nums.back()).size();
}
}
bool BigInteger::isZero() const
{
return nums.size() == 1 && nums.back() == 0;
}
int main()
{
BigInteger a,b;
cin>>a>>b;
cout<<a*b<<endl;
return 0;
}
|