C++ 高精度整型数 - 采用位压缩策略优化
测试题目链接:https://www.luogu.com.cn/problem/P1932
本篇博客主要讲位压缩策略,不懂高精度思路的可以参考我上一篇博客:https://www.cnblogs.com/peichaoL/p/12516987.html
如果我们想存储一个高精度整型数:123456789
假如我们这么存:
一个 int 拿着 4 字节的资源却只存了一位数字,这无疑是对内存的极大浪费,于是我们可以让一个 int 多存几位,像这样:
我们可以认为是将十进制变换成万进制,这便是位压缩的一个鲜明例子。
用这种方式来实现高精度整型数,最强大的地方在于对时间效率的优化:别的高精度一次加法出 1 位的结果,这种高精度一次加法出 4 位的结果。我们知道高精除法的实现中,假如我们正在求商的某一位(已做好对齐操作),试商的过程有两种:(下面所提及的除数 、被除数并不是真正的除数、被除数,而是计算到了某个过程时的除数、被除数,疑惑的童鞋可以参考我上一篇博客中对除法运算的详细解释。)
- 用被除数一次次地减除数,直到小于除数。商的当前位即为减去的次数。
- 二分去求这个位数,使得 该位数 * 除数 <= 被除数 且 (该位数 + 1) * 除数 > 被除数。
假如我们采用传统的存储策略,即使二分地去找这个数,由于求的只是一位,所以只能在 1 ~ 9 之间二分,完全没有发挥二分的作用,对时间效率的提升极低。而如果我们用位压缩的策略去存储,就扩大了二分的范围,从而真正发挥其作用。(例如上图中,每一位所代表的权值 base = 10000,我们可以在 1 ~ 9999 之间二分,一次性求出商的 4 位)
对于位压缩中每位的权值 base:
base 的选取肯定是大越好,为了好实现,取10的整数次幂,对于 int[],考虑到乘法操作可能幂次翻倍,比较省事的取法是 base = 1e4,当然,如果细心点把可能溢出的地方用 long long 接住,可以取到 1e9。
附上类代码:
const int base = 10000; const int digit = 4; /** * @brief 高精度整型 - 采用位压缩策略 * 要求无前导零 要求非负 */ class HighPrecision{ private: int num[10000] = {0}; int length = 1; int digital_num() const{ HighPrecision temp = *this; int ans = (temp.length - 1) * digit; int temp2 = temp.num[temp.length - 1]; while(temp2){ ans++; temp2 /= 10; } return ans; } public: HighPrecision(){} HighPrecision(const HighPrecision& a){ for(int i = 0; i < a.length; i++)this->num[i] = a.num[i]; this->length = a.length; } HighPrecision(const int& a){ int temp = a; this->num[0] = temp % base; temp /= base; while(temp){ this->num[this->length++] = temp % base; temp /= base; } } HighPrecision(const string& a){ string s = a; while(s.length() > digit){ this->num[this->length - 1] = atoi(s.substr(s.length() - 4).c_str()); s.erase(s.length() - 4); this->length++; } this->num[this->length - 1] = atoi(s.c_str()); } /** * @brief 高精度加法 * @param a 加数 * @return 和 */ HighPrecision operator+ (const HighPrecision& a) const{ HighPrecision ans; ans.length = max(this->length, a.length); int carry = 0; for(int i = 0; i < ans.length; i++){ ans.num[i] = this->num[i] + a.num[i] + carry; carry = ans.num[i] / base; ans.num[i] %= base; if(i == ans.length - 1 && carry)ans.length++; } return ans; } HighPrecision operator+= (const HighPrecision& a){ *this = *this + a; return *this; } /** * @brief 高精度减法 - 必须保证被减数大于等于减数 * @param a 减数 * @return 差 */ HighPrecision operator- (const HighPrecision& a) const{ HighPrecision ans = *this; int borrow = 0; for(int i = 0; i < this->length; i++){ ans.num[i] -= a.num[i] + borrow; borrow = 0; if(ans.num[i] < 0){ ans.num[i] += base; borrow = 1; } } while(!ans.num[ans.length - 1] && ans.length - 1)ans.length--;//去前导零 return ans; } HighPrecision operator-= (const HighPrecision& a){ *this = *this - a; return *this; } /** * @brief 高精度乘法 * @param a 乘数 * @return 积 */ HighPrecision operator* (const HighPrecision& a) const{ if(*this == 0 || a == 0)return HighPrecision(0); HighPrecision ans; int carry = 0; ans.length = this->length + a.length - 1; for(int i = 0; i < this->length; i++){ carry = 0; for(int j = 0; j < a.length; j++){ ans.num[i + j] += this->num[i] * a.num[j] + carry; carry = ans.num[i + j] / base; ans.num[i + j] %= base; } if(carry)ans.num[i + a.length] += carry; } if(carry)ans.length++; return ans; } HighPrecision operator*= (const HighPrecision& a){ *this = *this * a; return *this; } /** * @brief 高精度除法, 请自觉检测除 0 的错误 * @param a 除数 * @return 商 */ HighPrecision operator/ (const HighPrecision& a) const{ if(*this < a)return 0; HighPrecision ans, dividend = *this, divisor = a; while(divisor * HighPrecision(base) <= dividend)divisor *= HighPrecision(base);//初次对齐 while(true){ int high = base, low = 1, mid = 0; if(dividend >= divisor){ while(high - low > 1){ mid = (high + low) / 2; if(divisor * HighPrecision(mid) > dividend)high = mid; else low = mid; } dividend -= divisor * HighPrecision(low); ans.num[ans.length - 1] += low; } if(divisor == a)break; ans.length++; for(int i = 1; i < divisor.length; i++)divisor.num[i - 1] = divisor.num[i]; divisor.num[divisor.length - 1] = 0; divisor.length--; } reverse(ans.num, ans.num + ans.length); while(!ans.num[ans.length - 1])ans.length--; return ans; } HighPrecision operator/= (const HighPrecision& a){ *this = *this / a; return *this; } HighPrecision operator% (const HighPrecision& a) const{ return *this - (*this / a * a); } HighPrecision operator%= (const HighPrecision& a){ *this = *this % a; return *this; } bool operator== (const HighPrecision& a) const{ if(this->length != a.length)return false; for(int i = this->length - 1; i >= 0; i--){ if(this->num[i] != a.num[i])return false; } return true; } bool operator> (const HighPrecision& a) const{ if(this->length != a.length)return this->length > a.length; for(int i = this->length - 1; i >= 0; i--){ if(this->num[i] != a.num[i])return this->num[i] > a.num[i]; } return false; } bool operator< (const HighPrecision& a) const{ return a > *this; } bool operator>= (const HighPrecision& a) const{ return *this > a || *this == a; } bool operator<= (const HighPrecision& a) const{ return *this < a || *this == a; } bool operator!= (const HighPrecision& a) const{ return !(*this == a); } HighPrecision operator= (const HighPrecision& a){ this->length = a.length; for(int i = 0; i < this->length; i++)this->num[i] = a.num[i]; return *this; } friend ostream& operator<< (ostream&, const HighPrecision&); }; ostream& operator <<(ostream& os, const HighPrecision& a){ os << a.num[a.length - 1]; for(int i = a.length - 2; i >= 0; i--){ int temp_base = base / 10; while(a.num[i] < temp_base){ cout << 0; temp_base /= 10; } if(a.num[i])cout << a.num[i]; } return os; }