高精度

头文件

此模板涉及的头文件较多,建议用 <bits/stdc++.h> 库。

强制转换

此模板支持两种转换:

  • 整型 BigInteger\Rightarrow\text{BigInteger}BigInteger(n),其中 nn 是整型。

  • 字符串型 BigInteger\Rightarrow\text{BigInteger}BigInteger(s),其中 ss 是字符型。

定义

定义时直接用强制转换类型赋值即可。

设置变量类型

第一行是这样的一个代码:

#define T long long

TT 代表大整数运算中说使用的类型,可为 int\text{int}long long\text{long long}

请根据情况选择,类型将会影响接下来的压位。

设置压位

BigInteger\text{BigInteger} 类的 private\text{private} 部分中有这个代码:

static const T base = 10000;
static const int width = 4;

设你需要压的位为 xx 位,把 base = 10000 中的 1000010000 改成 10x10^x,把 width = 4 中的 44 改成 xx

当然位数也是有限制的,你要保证 102x10^{2x} 不超过 TT 的范围,例如 long long\text{long long} 最多只能有 99 位。

操作

输出流和左移运算可能会造成歧义,如果你想输出 a×baseba\times base^b,不能这么写:

cout<<a<<b;

应该加上括号:

cout<<(a<<b);

代码

#define T long long
// 常量,分别表示正数、负数、相等。
const int POSITIVE = 1, NEGATIVE = -1, EQUAL = 0;
class BigInteger
{
	// 重载输出符号。
	friend ostream& operator<<(ostream&, const BigInteger&);
	// 比较操作。
	friend int compare(const BigInteger&, const BigInteger&);
	friend bool operator<(const BigInteger&, const BigInteger&);
	friend bool operator<=(const BigInteger&, const BigInteger&);
	friend bool operator==(const BigInteger&, const BigInteger&);
	// 相关运算的实现:加法、减法、乘法、除法、模、乘方、左移。
	friend BigInteger operator+(const BigInteger&, const BigInteger&);
	friend BigInteger operator-(const BigInteger&, const BigInteger&);
	friend BigInteger operator*(const BigInteger&, const BigInteger&);
	friend BigInteger operator/(const BigInteger&, const BigInteger&);
	friend BigInteger operator%(const BigInteger&, const BigInteger&);
	friend BigInteger operator^(const BigInteger&, const unsigned int&);
	friend BigInteger operator^(const BigInteger&, const BigInteger&);
	friend BigInteger operator<<(const BigInteger&, const unsigned int&);
	public:
		BigInteger() {};
		// 将长整型及字符串转换为高精度整数的构造函数。
		BigInteger(const T&);
		BigInteger(const string&);
		// 获取高精度整数的最后一个数位值。
		T lastDigit() const { return digits.front(); }
		~BigInteger() {};
	private:
		// 清除运算产生的前导零。
		void zeroJustify(void);
		static const T base = 1000000;
		static const int width = 6;
		// 符号位和保存数位的向量。
		T sign;
		vector<T> digits;
		T stoi(string s)
		{
			T ans=0;
			int l=s.size();
			for(int i=0;i<l;i++){
				ans*=10;
				ans+=s[i]-'0';
			}
			return ans;
		}
};
// 移除无效的前导零。
void BigInteger::zeroJustify(void){
	for (int i = digits.size() - 1; i >= 1; i--)
	{
		if (digits[i] > 0)
			break;
		digits.erase(digits.begin() + i);
	}
	if (digits.size() == 1 && digits[0] == 0)
		sign = POSITIVE;
}
// 重载输出符号以输出大整数。
ostream& operator<<(ostream& os, const BigInteger& number){
	os << (number.sign > 0 ? "" : "-");
	os << number.digits[number.digits.size() - 1];
	for (int i = (int)number.digits.size() - 2; i >= 0; i--)
		os << setw(number.width) << setfill('0') << number.digits[i];
	return os;
}
// 将长整型数转换为大整数。
BigInteger::BigInteger(const T& value){
	if (value == 0)
	{
		sign = POSITIVE;
		digits.push_back(0);
	}
	else
	{
		// 不断模基数取余得到各个数位。
		sign = (value >= 0 ? POSITIVE : NEGATIVE);
		T number = abs(value);
		while (number)
		{
			digits.push_back(number % base);
			number /= base;
		}
	}
	// 移除前导零。
	zeroJustify();
};
// 将十进制的字符串转换为大整数。
BigInteger::BigInteger(const string& value){
	if (value.length() == 0)
	{
		sign = POSITIVE;
		digits.push_back(0);
	}
	else
	{
		// 设置数值的正负号。
		sign = value[0] == '-' ? NEGATIVE : POSITIVE;
		//转换为整数存储到数位数组中。
		string block;
		for (int index = value.length() - 1; index >= 0; index--)
		{
			if (isdigit(value[index]))
				block.insert(block.begin(), value[index]);
			if (block.length() == width)
			{
				digits.push_back(stoi(block));
				block.clear();
			}
		}
		if (block.length() > 0)
			digits.push_back(stoi(block));
	}
	// 移除前导零。
	zeroJustify();
}
// 比较两个高精度整数的大小。
// x 大于 y,返回 1,x 小于 y,返回-1,x 等于 y,返回 0。
// 为了后续除法的需要调整了实现,使得对于未经前导零调整的整数也能够得到正确的处理。
int compare(const BigInteger& x, const BigInteger& y){
	// 符号不同,正数大于负数。
	if (x.sign == POSITIVE && y.sign == NEGATIVE ||
	x.sign == NEGATIVE && y.sign == POSITIVE)
		return (x.sign == POSITIVE ? 1 : -1);
	// 确定 x 和 y 的有效数位个数,前导零不计入有效数位。
	int xDigitNumber = x.digits.size() - 1;
	for (; xDigitNumber && x.digits[xDigitNumber] == 0; xDigitNumber--) ;
	int yDigitNumber = y.digits.size() - 1;
	for (; yDigitNumber && y.digits[yDigitNumber] == 0; yDigitNumber--) ;
	// 符号相同,同为正数,数位个数越多则越大,同为负数,数位个数越多则越小。
	if (xDigitNumber > yDigitNumber)
		return (x.sign == POSITIVE ? 1 : -1);
	// 符号相同,同为正数,数位个数越少则越小,同为负数,数位个数越少则越大。
	if (xDigitNumber < yDigitNumber)
		return (x.sign == NEGATIVE ? 1 : -1);
	// 符号相同,数位个数相同,逐位比较。
	for (int index = xDigitNumber; index >= 0; index--)
	{
		if (x.digits[index] > y.digits[index])
			return (x.sign == POSITIVE ? 1 : -1);
		if (x.digits[index] < y.digits[index])
			return (x.sign == NEGATIVE ? 1 : -1);
	}
	// 两数相等。
	return 0;
}
// 等于比较运算符。
bool operator==(const BigInteger& x, const BigInteger& y){
	return compare(x, y) == 0;
}
// 小于比较运算符。
bool operator<(const BigInteger& x, const BigInteger& y){
	return compare(x, y) < 0;
}
// 小于等于比较运算符。
bool operator<=(const BigInteger& x, const BigInteger& y){
	return compare(x, y) <= 0;
}
// 高精度整数加法。
BigInteger operator+(const BigInteger& x, const BigInteger& y){
	BigInteger z;
	// 如果两个加数的符号不同,转换为减法运算。
	if (x.sign == NEGATIVE && y.sign == POSITIVE)
	{
		z = x;
		z.sign = POSITIVE;
		return (y - z);
	}
	else if (x.sign == POSITIVE && y.sign == NEGATIVE)
	{
		z = y;
		z.sign = POSITIVE;
		return (x - z);
	}
	// 确保 x 的位数比 y 的位数多,便于计算。
	if (x.digits.size() < y.digits.size())
		return (y + x);
	// 两个加数的符号相同时才进行加法运算。预先为结果分配存储空间。
	z.sign = x.sign + y.sign >= 0 ? POSITIVE : NEGATIVE;
	z.digits.resize(max(x.digits.size(), y.digits.size()) + 1);
	fill(z.digits.begin(), z.digits.end(), 0);
	// 逐位相加,考虑进位。
	int index = 0;
	T carry = 0;
	for (; index < x.digits.size(); index++)
	{
		// 获取对应位的和。
		T sum = x.digits[index] + carry;
		sum += index < y.digits.size() ? y.digits[index] : 0;
		// 确定进位。
		carry = sum / z.base;
		// 将和保存到结果的相应位中。
		z.digits[index] = sum % z.base;
	}
	// 保存最后可能产生的进位。
	z.digits[index] = carry;
	// 移除前导零。
	z.zeroJustify();
	return z;
}
// 高精度整数减法。
BigInteger operator-(const BigInteger& x, const BigInteger& y){
	BigInteger z;
	// 当 x 和 y 至少有一个是负数,转换为加法运算。
	if (x.sign == NEGATIVE || y.sign == NEGATIVE)
	{
		z = y;
		z.sign = -y.sign;
		return x + z;
	}
	// 都为正数,确保 x 大于 y,便于计算。
	if (x < y)
	{
		z = y - x;
		z.sign = NEGATIVE;
		return z;
	}
	// 设置符号位并预先分配存储空间。
	z.sign = POSITIVE;
	z.digits.resize(max(x.digits.size(), y.digits.size()));
	fill(z.digits.begin(), z.digits.end(), 0);
	// 逐位相减,考虑借位。
	int index = 0;
	T borrow = 0;
	for (; index < x.digits.size(); index++)
	{
		// 获取对应位的差。
		T difference = x.digits[index] - borrow;
		difference -= index < y.digits.size() ? y.digits[index] : 0;
		// 确定是否有借位。
		borrow = 0;
		if (difference < 0)
		{
			difference += z.base;
			borrow = 1;
		}
		// 保存相应位差的结果。
		z.digits[index] = difference % z.base;
	}
	// 移除前导零。
	z.zeroJustify();
	return z;
}
// 高精度整数乘法。
BigInteger operator*(const BigInteger& x, const BigInteger& y){
	BigInteger z;
	// 设置符号位并预先分配存储空间。
	z.sign = x.sign * y.sign;
	z.digits.resize(x.digits.size() + y.digits.size());
	fill(z.digits.begin(), z.digits.end(), 0);
	// 一行一行相乘然后相加。
	for (int i = 0; i < y.digits.size(); i++)
		for (int j = 0; j < x.digits.size(); j++)
		{
			z.digits[i + j] += x.digits[j] * y.digits[i];
			z.digits[i + j + 1] += z.digits[i + j] / z.base;
			z.digits[i + j] %= z.base;
		}
	// 移除前导零。
	z.zeroJustify();
	return z;
}
// 高精度整数除法,为整除运算。
BigInteger operator/(const BigInteger& x, const BigInteger& y){
	// z 表示整除得到的商,r 表示每次试除时的被除数。
	BigInteger z, r;
	// 设置商和被除数的符号位。
	z.sign = x.sign * y.sign;
	r.sign = POSITIVE;
	// 为商 z 和表示被除数的 r 预先分配存储空间。
	z.digits.resize(max(1,(int)(x.digits.size() - y.digits.size() + 1)));
	r.digits.resize(y.digits.size() + 1);
	// 初始化值。
	fill(z.digits.begin(), z.digits.end(), 0);
	fill(r.digits.begin(), r.digits.end(), 0);
	// 从高位到低位逐位试除得到对应位的商。
	for (int i = x.digits.size() - 1; i >= 0; i--)
	{
		// 获取被除数,将上一次未被除尽的余数的移到高位加上当前数位继续除。
		r.digits.insert(r.digits.begin(), x.digits[i]);
		// 通过二分试除法得到对应位的商。
		T low = 0, high = z.base - 1, middle = (high + low + 1) >> 1;
		while (low < high)
		{
			if ((y * BigInteger(middle)) <= r)
				low = middle;
			else
				high = middle - 1;
			middle = (high + low + 1) >> 1;
		}
		// 执行减法,从被除数中减去指定数量的 y。
		for (int index = 0; index < y.digits.size(); index++)
		{
			T difference = r.digits[index] - middle * y.digits[index];
			// 确定是否有借位产生。
			T borrow = 0;
			if (difference < 0)
				borrow = (z.base - 1 - difference) / z.base;
			// 高位减去借位数量。
			r.digits[index + 1] -= borrow;
			// 低位加上借位。
			difference += z.base * borrow;
			r.digits[index] = difference % z.base;
		}
		// 将对应位的商存入结果中。
		z.digits.insert(z.digits.begin(), middle);
	}
	// 移除前导零。
	z.zeroJustify();
	return z;
}
// 高精度整数求模运算。适用于同为正整数的情形。
BigInteger operator%(const BigInteger& x, const BigInteger& y){
	return (x - (x / y) * y);
}
// 高精度整数乘方运算,乘法次数为内置整数类型。
BigInteger operator^(const BigInteger& x, const unsigned int& y){
	if (y == 0)
		return BigInteger(1);
	if (y == 1)
		return x;
	if (y == 2)
		return x * x;
	if (y & 1 > 0)
		return ((x ^ (y / 2)) ^ 2) * x;
	else
		return ((x ^ (y / 2)) ^ 2);
}
const BigInteger ZERO = BigInteger(0), ONE = BigInteger(1), TWO = BigInteger(2);
// 高精度整数乘方运算,乘方次数为高精度整数。
BigInteger operator^(const BigInteger& x, const BigInteger& y){
	if (y == ZERO)
		return BigInteger(1);
	if (y == ONE)
		return x;
	if (y == TWO)
		return x * x;
	if (y.lastDigit() & 1 > 0)
		return ((x ^ (y / 2)) ^ 2) * x;
	else
		return ((x ^ (y / 2)) ^ 2);
}
// 高精度整数左移运算,左移一位相当于将此数乘以基数。
BigInteger operator<<(const BigInteger& x, const unsigned int& shift){
	BigInteger z;
	// 设置符号位,复制向量中的数据。
	z.sign = x.sign;
	z.digits.resize(x.digits.size());
	copy(x.digits.begin(), x.digits.end(), z.digits.begin());
	// 移动指定位数,补零。
	for (int i = 0; i < shift; i++)
		z.digits.insert(z.digits.begin(), 0);
	// 移除前导零。
	z.zeroJustify();
	return z;
}

back

posted @ 2021-08-14 23:41  luckydrawbox  阅读(12)  评论(0)    收藏  举报  来源