C++ 高精度整型数四则运算
测试题目链接:https://www.luogu.com.cn/problem/P1932
注:用string去实现高精度的方法时间效率表现较差,若想进一步优化高精度的效率,可以参考我的下一篇博客:https://www.cnblogs.com/peichaoL/p/12520881.html
高精度运算的实现思路,就是模拟人们在进行运算时的方法步骤。
高精度的数数位比较多,用 int 数组实现的话会浪费很多空间,这里我们用 string 去实现。考虑到我们在手动做加减乘法的时候,都是从低位向高位做,只有除法是高位向低位做,为方便运算的实现,这里我们倒着去存一个数,例如,我们用 "65248" 来代表整型数 84256。
下面说一下大致思路,
加法:
记录一个进位值carry和当前位的和temp,从低位向高位,temp = 两数当前位相加 + carry,结果的当前位 = temp % 10,carry = temp / 10,如果此时当前位已经是较长的数的最高位且有进位,则结果上要进位,否则继续循环。
减法:
首先要保证被减数大于等于减数,记录一个借位值borrow,从低位向高位,如果被减数当前位 >= 减数当前位 + borrow,那就直接减去,借位置0,否则被减数当前位 + 10再去减减数当前位和借位,并将借位置1。由于减法的结果位数小于等于被减数位数,所以最后要消除一下结果的前导零。
乘法:
模拟乘法需要二重循环,记录进位值 carry, 记录 temp = 乘数1第 i 位 * 乘数2第 j 位 + carry,carry = (结果第 i + j 位 + temp) / 10,结果第 i + j 位 = (当前位 + temp) % 10。
乘法运算的结果位数可能是两数位数相加,也可能是两数位数相加再 + 1,要向加法一样考虑最终进位的情况。
除法:
手动模拟一下除法我们可以发现,除法是一个对齐->试商->减去当前积->再对齐->再试商....这样的过程,对齐,我们可以通过在除数低位补 0 来实现,试商,我们则通过一次次的减去除数,直到被除数小于除数来实现。
举个栗子:654178 / 25 = 26167 (整数除法舍去小数)
- 我们首先对齐,将 25 扩展成250000,用654178 - 250000,可以减 2 次,剩下154178;
- 我们再对齐,将 250000 去掉一个 0 变成25000,用154178 - 25000,可以减 6 次, 剩下4178;
- 我们再对齐,将 25000 去掉一个 0 变成2500,用4178 - 2500,可以减 1 次,剩下1678;
- 我们再对齐,将 2500 去掉一个 0 变成250, 用1678 - 250, 可以减6次,剩下178;
- 我们再对齐,将 250 去掉一个 0 变成25,用178 - 25可以减7次,剩下3;
- 这时候 3 已经小于除数25了,它就是余数,我们将每次减去的次数连起来,就得到了商 —— 26167。
除法的核心步骤 —— 不停地减,是比较耗费时间的,我们可以去二分找到应该减的次数,一次减去那么多,可以提高一些效率。
以上是核心的思路,具体实现时还需要注意一些边边角角的细节,可以参考代码。
为了方便使用,干脆写一个类,并且重载常用的运算符。
附上代码:
/** * @brief 整型数高精度 * 要求无负数, 无前导零 */ class HighPrecision{ private: string num; /** * @brief 高精度数乘一位 int 在除法运算中代替普通的乘法运算起加速作用 * @param a 一位int乘数 * @return 积 */ HighPrecision multiply_one_digit(const int& a) const{ HighPrecision ans = *this; int carry = 0, temp; for(int i = 0; i < num.length(); i++){ temp = (num[i] - '0') * a + carry; carry = temp / 10; ans.num[i] = temp % 10 + '0'; } if(carry)ans.num += char(carry + '0'); return ans; } public: HighPrecision(){ this->num = "0"; } HighPrecision(const HighPrecision& a){ this->num = a.num; } HighPrecision(const string& a){ for(int i = a.length() - 1; i >= 0; i--){ this->num += a[i]; } } HighPrecision(const int& a){ int temp1 = a; do{ this->num += char(temp1 % 10 + '0'); temp1 /= 10; }while(temp1); } bool operator== (const HighPrecision& a) const{ return this->num == a.num; } bool operator== (const int& a) const{ return *this == HighPrecision(a); } bool operator!= (const HighPrecision& a) const{ return !(*this == a); } bool operator> (const HighPrecision& a) const{ if(this->num.length() != a.num.length())return this->num.length() > a.num.length(); for(int i = this->num.length() - 1; i >= 0; i--){ if(this->num[i] != a.num[i])return this->num[i] > a.num[i]; } return false; } bool operator> (const int& a) const{ return *this > HighPrecision(a); } 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 a >= *this; } HighPrecision operator= (const HighPrecision& a){ this->num = a.num; return *this; } /** * @brief 高精度加法 * @param a 加数 * @return 和 */ HighPrecision operator+ (const HighPrecision& a) const{ string temp_short, temp_long; if(this->num.length() > a.num.length()){ temp_short = a.num; temp_long = this->num; } else{ temp_short = this->num; temp_long = a.num; } int carry = 0, temp1, max_len = max(temp_short.length(), temp_long.length()); for(int i = 0; i < temp_short.length(); i++){ if(i == temp_long.length()){ temp_short[temp_short.length() - 1] = '1'; break; } temp1 = int(temp_long[i] - '0') + int(temp_short[i] - '0') + carry; temp_short[i] = char(temp1 % 10 + '0'); carry = temp1 / 10; if(i == temp_short.length() - 1 && (carry || i < max_len - 1))temp_short += '0';//最高位需要进位的情况 } HighPrecision ans; ans.num = temp_short; return ans; } HighPrecision operator+ (const int& a) const{ return *this + HighPrecision(a); } HighPrecision operator+= (const HighPrecision& a){ *this = *this + a; return *this; } /** * @brief 高精度减法 - 必须保证被减数大于等于减数 * @param a 减数 * @return 差 */ HighPrecision operator- (const HighPrecision &a) const{ HighPrecision ans = HighPrecision(""); string temp = a.num; while(temp.length() < this->num.length())temp += '0'; int borrow = 0; for(int i = 0; i < this->num.length(); i++){ if(this->num[i] < temp[i] + borrow){ ans.num += this->num[i] + 10 - borrow - temp[i] + '0'; borrow = 1; } else{ ans.num += this->num[i] - temp[i] - borrow + '0'; borrow = 0; } } while(ans.num[ans.num.length() - 1] == '0' && ans.num.length() > 1)ans.num.erase(ans.num.length() - 1); return ans; } HighPrecision operator- (const int& a) const{ return *this - HighPrecision(a); } 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(); HighPrecision ans = HighPrecision(""); ans.num.assign(this->num.length() + a.num.length() - 1, '0'); int carry, temp1; for(int i = 0; i < a.num.length(); i++){ carry = 0; for(int j = 0; j < this->num.length(); j++){ temp1 = (a.num[i] - '0') * (this->num[j] - '0') + carry; carry = (ans.num[i + j] - '0' + temp1) / 10; ans.num[i + j] = (ans.num[i + j] - '0' + temp1) % 10 + '0'; if(i + j != ans.num.length() - 1 && j == this->num.length() - 1 && carry)ans.num[i + j + 1] += carry; if(i + j == ans.num.length() - 1 && carry)ans.num += carry + '0'; } } return ans; } HighPrecision operator* (const int& a) const{ return *this * HighPrecision(a); } 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 HighPrecision(0); HighPrecision temp = a, dividend = *this; string ans; temp.num.insert(0, dividend.num.length() - a.num.length(), '0'); do{ ans += '0'; if(dividend >= temp){ int high = 10, low = 1; while(high - low > 1){ int mid = (high + low) / 2; if(temp.multiply_one_digit(mid) > dividend)high = mid; else low = mid; } dividend -= temp.multiply_one_digit(low); ans[ans.length() - 1] += low; } if(temp.num.length() == a.num.length())break; temp.num = temp.num.substr(1); }while(temp.num.length() >= a.num.length()); while(ans[0] == '0')ans = ans.substr(1); return HighPrecision(ans); } HighPrecision operator/ (const int& a) const{ return *this / HighPrecision(a); } 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; } friend ostream& operator <<(ostream&, const HighPrecision&); }; ostream& operator <<(ostream& os, const HighPrecision& a){ for(int i = a.num.length() - 1; i >= 0; i--){ os << a.num[i]; } return os; }