高精度模板——C++实现

综述

最近闲来无事写了一个比较简单的高精度模板……想来之前没有总结过这一块的知识,就写一篇博客来简单记录一下吧。关于高精度模板的资料其实挺多的,有某个方法不是很理解的话可以再搜搜资料或者自己手动模拟一下。这个模板的效率肯定不是最好的,可能还会有隐藏的bug,仅给大家提供一种实现高精度的思路。

朴素想法

我们先从最简单的开始,如果让你实现一个正数的高精度加法,你会怎么做?大部分人的做法肯定是用数组或者链表来模拟这个过程,以数组为例,从低位到高位依次存储数字的每一位,那么加法无非就是从最低位开始逐位进行加法计算,进行进位。没错,这就是高精度算法的实现思路。不过为了简化算法,我们还需要使用一些技巧。比如,针对加法,我们仅计算同符号的大数相加,若不同符号则视为减法。针对减法,我们仅计算较大的数减去较小的数,然后通过取反运算将其扩展到全部情况。

那么乘法怎么计算呢?类似于竖式乘法,可以把乘数逐位拆分,然后再进行计算。很显然这样做的复杂度是比较高的,也有更加优秀的做法,不过这里就不讨论了~

比较复杂的是除法,这里提供一种思路吧,感觉复杂度还是比较高的。有点逆推的意思,不妨设 a / b = c a/b=c a/b=c,也即 a = b ∗ c a=b*c a=bc,此时把 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=bcn+bcn1++bc0。那么只要我们依次(从高位到低位)计算出 c n 、 c n − 1 、 … c_n、c_{n-1}、… cncn1,再把它们拼起来就可以得到 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 xy+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);
	// 转换为string
	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));
	}
	// 构造10^n n<=0会返回1
	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;
	// nums中的每一个元素代表digit位数据
	static int digit;
	static int mod;
};

int BigInteger::digit = 8;        // 8位
int BigInteger::mod = 100000000; // 1e9 可能用mask会更好?

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;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值