附带详细说明,复杂度分析
文中用a表示左操作数,b表示右操作数,n代表操作数位数
首先c++存储大整数有3个思路
思路1
采用十进制vector或者string来存储0-9,加减乘除算法全模拟人工实现
实际上建议用deque来存储
因为加法结果从低位产生到高位
除法结果却是从高位产生到低位
需要从前后和插入结果需要push_back()和push_front()
优点
缺点
算法效率应该不高
思路2
使用2进制存储bit数组deque实现(原因同上),加减乘除模拟计算机底层位操作,自己实现移位还有所有位操作再来构建加减乘除
优点
缺点
效率应该一般自己实现的位操作(依靠循环遍历),没用计算机自带的运算器
思路3
ps:uint32_t=unsigned int ,uint64_t=unsigned long long
思路2改进
使用2^32进制存储用deque
优点
我的思路
思路3再改进
使用10^9 进制存储用deque
相比3牺牲点存储效率,改善了缺陷,10^9进制可以拼接成10进制输出
我的实现只处理正整数,因为实现了减法所以也影响不是很大只是觉得负数麻烦想自己在外面控制下也能解决问题就没有实现
有想法的同学可以在类里加一个static bool flag=0 自己完善;
每个节点最大值为10^9次方
class BigInt {
friend ostream& operator<<(ostream& os, const BigInt& x);
friend istream& operator>>(istream& is, BigInt& x);
deque<uint32_t> number;
static const uint32_t LIMIT = 1000000000;
}
之后重载需要的运算符就好
设计成string输入直观方便
我是高位在前低位在后
例如BigInt a(“1987654321”);构造后
其中的结构为a.number[0]=1;a.number[1]=987654321;
所以使用push_front()
BigInt() {
}
BigInt(const string& s) {
stringstream sstr;
string ss;
ss.reserve(s.size()*2);//预留足够出空间防止反复扩容
size_t i = s.size();
while (i >= 9) {
//每隔九个数字加一个空格
i -= 9;
ss += s.substr(i, 9);//从低位往高位9位一组分割写入
ss.push_back(' ');
}
if (i)ss += s.substr(0, i);
sstr << ss;
uint64_t temp;
while (sstr >> temp)
number.push_front(temp);
while (number.size() > 1 && number.front() == 0)//处理字符串的最前多余的0
number.pop_front();
}
BigInt(initializer_list<uint32_t> list) {
number.assign(list);
}
BigInt(deque<uint32_t>::const_iterator beg, deque<uint32_t>::const_iterator end) {
number.assign(beg, end);
}
~BigInt() {
}
直接用cout输出即可,注意除了第一个(最高位)以外其余数要用0补全9位
输入调用输入string的构造函数
ostream& operator<<(ostream& os, const BigInt& x) {
for (auto p = x.number.begin(); p != x.number.end(); ++p) {
if (p != x.number.begin())os << setw(9) << setfill('0');
os << *p;
}
return os;
}
istream& operator>>(istream& is, BigInt& x) {
string temp;
is >> temp;
x = {
temp };
return is;
}
下面的其他符号的实现会用到<号
deque
因为最前面的是最高位所以正好可以使用deque自带的<一步到位
<实现了其他的也都出来了一起实现了
bool operator<(const BigInt& b) const {
if (this->number.size() < b.number.size())
return true;
else if (this->number.size() == b.number.size() && this->number < b.number)
return true;
else return false;
}
bool operator>(const BigInt& b)const {
return b < *this;
}
bool operator==(const BigInt& b)const {
return !(*this < b || *this > b);
}
bool operator>=(const BigInt& b)const {
return !(*this < b);
}
bool operator<=(const BigInt& b)const {
return !(*this > b);
}
挨个加起来处理好进位 -------------------复杂度max(an,bn)
从低位处理到高位所以是反向迭代器
BigInt operator+(const BigInt& b) const {
BigInt res;
auto ap = this->number.rbegin();
auto bp = b.number.rbegin();
uint32_t s = 0;
uint32_t carry = 0;
while (ap != this->number.rend() || bp != b.number.rend())
{
if (ap == this->number.rend()) {
//左操作数已经到结尾
s = *bp + carry;
carry = s / (LIMIT);//处理溢出10^9的进位
res.number.push_front(s % LIMIT);//计算结果本位
++bp;
}
else if (bp == b.number.rend()) {
//右操作数已经到结尾
s = *ap + carry;
carry = s / (LIMIT);
res.number.push_front(s % LIMIT);
++ap;
}
else {
s = *bp + *ap + carry;
carry = s / (LIMIT);
res.number.push_front(s % LIMIT);
++ap;
++bp;
}
}
if (carry)res.number.push_front(carry);//处理最后的进位
return res;
}
BigInt& operator+=(const BigInt& b) {
return *this = *this + b;
}
乘法实现使用到上面的实现的加法
先实现BigInt x uint32_t ---------------------------------复杂度 (an)
再将BigInt a x BigInt b拆分为bn个BigIntuint32_t
再移位相加调用BigInt + BigInt ----------------------复杂度 (an x bn)
32位数32位数再加上进位绝不会超过64位的极限自己可以验证一下
BigInt operator*(const uint32_t& b) const {
BigInt res;//最前方为最高位,最高位为0则BigInt为0
if (this->number.front() == 0 || b == 0)
return res = {
0 };
uint32_t carry = 0;
uint64_t s;//使用64位防止溢出
for (auto ap = this->number.rbegin(); ap != this->number.rend(); ++ap) {
s = (uint64_t)*ap * b + carry;
carry = s / LIMIT;
res.number.push_front(s % LIMIT);
}
if (carry)res.number.push_front(carry);
return res;
}
BigInt& operator*=(const uint32_t& b) {
return *this = *this * b;
}
BigInt operator*(const BigInt& b) const {
BigInt res;
if (this->number.front() == 0 || b.number.front() == 0)
return res = {
0 };
int shift = 0;//移位计数
for (auto bp = b.number.crbegin(); bp != b.number.crend(); ++bp) {
BigInt temp;
temp = *this * *bp;//调用上面的BigInt*uint32_t
if (temp.number.front())//BigInt不为0
for (int i = 0; i < shift; ++i)
temp.number.push_back(0);
++shift;
res += temp;
}
return res;
}
BigInt& operator*=(const BigInt& b) {
return *this = *this * b;
}
挨个相减处理借位没什么好说的
调用上面的符号重载< 和=处理特殊情况
因为BigInt只表示正整数所以减法只能左边的操作数大于右操作数否则丢出异常-----------------------------------------------------------复杂度 max(an,bn)
BigInt operator-(const BigInt& b) const {
//由调用者保证左边大于右边
BigInt res;
if (*this < b) {
cout << "输入错误 小数减大数" << endl; throw "MinusOverFlow"; }
if (*this == b) return res = {
0 };
auto ap = this->number.rbegin();
auto bp = b.number.rbegin();
uint32_t s = 0;
uint32_t carry = 0;
while (ap != this->number.rend() || bp != b.number.rend())
{
if (bp == b.number.rend()) {
//只可能右边先结束
if (*ap >= carry) {
//不用借位
s = *ap - carry;
carry = 0;
}
else
{
s = *ap - carry + LIMIT;
carry = 1;
}
++ap;
}
else {
if (*ap >= (*bp + carry)) {
//不用借位
s = *ap - *bp - carry;
carry = 0;
}
else {
s = *ap - *bp - carry + LIMIT;
carry = 1;
}
++ap;
++bp;
}
if(s)res.number.push_front(s);
}
return res;
}
BigInt& operator-=(const BigInt& b) {
return *this = *this - b;
}
除法取余的话就比较难找到好的实现
BigInt/uint32_t BigInt%uint32_t和可以利用计算机自带的除法和取余挨个计算处理进位 --------------------------------------------复杂度为(an)
其他的我暂未找到更好的实现
BigInt a/BigInt b采用估测法
pair<BigInt, uint32_t>divide(const uint32_t& b)const {
BigInt res;
uint64_t carry = 0;
if (b == 0) {
cout << "输入错误 除或者取余0" << endl; throw "DivideByZero"; }
for (auto ap = this->number.begin(); ap != this->number.end(); ++ap) {
if (b > * ap&& carry == 0) {
//有余数必然大
carry = *ap;
if(!res.number.empty())res.number.push_back(0);//商0
continue;
}
else
{
carry *= LIMIT;
carry += *ap;
res.number.push_back(carry / b);//直接除法计算商
carry %= b;//取余计算余数
}
}
if (res.number.empty())res = {
0 };//防止BigInt
return make_pair(res, carry);
}
pair<BigInt, BigInt> divide(const BigInt& b) const {
//性能薄弱点
BigInt res;
BigInt carry;
if (b.number.front() == 0) {
cout << "输入错误 除或者取余0" << endl; throw "DivideByZero"; }
for (auto ap = this->number.begin(); ap != this->number.end(); ++ap) {
carry.number.push_back(*ap);
if (carry < b) {
if(!res.number.empty())res.number.push_back(0);//商0
continue;
}
else if (b < carry) {
uint32_t l = 1, r = LIMIT - 1, m;
BigInt cur, next;
while (1)
{
m = (l + r) / 2;
cur = {
b * m };
next = {
cur + b };
if (cur < carry && carry < next)break;
if (cur < carry)
l = m + 1;
else if (carry < cur)
r = m - 1;
else break;
}
res.number.push_back(m);
carry -= cur;
}
else {
//b=carry
res.number.push_back(1);
carry.number.clear();
}
}
if (carry.number.empty())carry = {
0 };
if (res.number.empty())res = {
0 };
return make_pair(res, carry);
}
BigInt operator/(const uint32_t& b)const {
return divide(b).first;
}
BigInt& operator/=(const uint32_t b) {
return *this = *this / b;
}
BigInt operator/(const BigInt& b)const {
return divide(b).first;
}
BigInt& operator/=(const BigInt& b) {
return *this = *this / b;
}
uint32_t operator%(const uint32_t& b)const {
return divide(b).second;
}
BigInt& operator%=(const uint32_t& b) {
return *this = BigInt({
*this % b });
}
BigInt operator%(const BigInt& b) const {
return divide(b).second;
}
BigInt& operator%=(const BigInt& b) {
return *this = *this % b;
}
采用快速幂递归实现
因为b太大根本算不完(时间太长)所以没必要实现BigInt^=BigInt
进行最多log2(2^32)次方次递归每次都要自己乘自己BigInt x BigInt
--------------------------------- 复杂度32(an)^2
BigInt& operator^=(const uint32_t& b) {
if (b == 1)return *this;
if (b & 1) {
BigInt a = *this;
*this *= *this;
*this ^= b / 2;
*this *= a;
}
else {
*this *= *this;
*this ^= b / 2;
}
return *this;
}
BigInt operator^(const uint32_t& b)const {
BigInt res = {
*this };
return res ^= b;
}
整体代码放在了GitHub上了
都看到了这里麻烦点个赞留个言再走呀同学们秋梨膏
链接在此-ps:点个星号也行