最近发现了一个很有意思的网站:codewars, 上面有很多有趣的'kata', 通过kata可以提升你的段位。
codewars上的题目和传统的竞赛题目的风格不太一样, 而且用户是可以创建kata的, 这一点我觉得很有意思。
在codewars中老是遇到高精度的题目(高精度在普通OJ中基本只有模板题), 每次都要手写及处理细节十分的腻,于是决定把高精度好好的写一遍记录下来, 供未来copy。
高模板模板类
包含:
- 高精度加高精度
- 高精度减高精度
- 高精度乘低精度
- 高精度乘高精度
- 高精度除以低精度
- 高精度除以高精度
namespace BigNumber {
static string reversed(string &s) {//反转字符串并返回
reverse(s.begin(), s.end());
return s;
}
static bool comp(string a, string b) {//判断a大于等于b
if (a.size() != b.size()) return a.size() > b.size();
return a > b || a == b;
}
static string add(string a, string b) {//高精度加高精度
string ans;
int i = a.size() - 1, j = b.size() - 1, carry = 0;
while (i >= 0 || j >= 0 || carry) {
if (i >= 0) carry += a[i--] - '0';
if (j >= 0) carry += b[j--] - '0';
ans.push_back(carry % 10 + '0');
carry /= 10;
}
return reversed(ans);
}
static string subtract(string a, string b) {//高精度减高精度
if (!comp(a, b)) return "-" + subtract(b, a);
string ans;
int n = a.size(), m = b.size(), carry = 0;
for (int i = n - 1, j = m - 1; i >= 0; i--, j--) {
carry += a[i] - '0';
if (j >= 0) carry -= b[j] - '0';
if (carry < 0) ans.push_back(carry + 10 + '0'), carry = -1;
else ans.push_back(carry + '0'), carry = 0;
}
while (ans.size() > 1 && ans.back() == '0') ans.pop_back();
return reversed(ans);
}
template<typename T>
static string multiply(string s, T b) {//高精度乘以低精度
if (b == 0) return "0";
reversed(s);
string ans;
for (T i = 0, carry = 0; i < s.size() || carry; i++) {
if (i < s.size()) carry += (s[i] - '0') * b;
ans.push_back(carry % 10 + '0');
carry /= 10;
}
return reversed(ans);
}
static string multiply(string a, string b) {//高精度乘以高精度
reversed(a), reversed(b);
vector<int> ans(a.size() + b.size(), 0);
for (int i = 0; i < a.size(); i ++)
for (int j = 0; j < b.size(); j ++)
ans[i + j] += (a[i] - '0') *(b[j] - '0');
int carry = 0; string res;
for (int i = 0; i < ans.size(); i ++) {
ans[i] += carry, carry = ans[i] / 10, ans[i] %= 10;
res.push_back(ans[i] + '0');
}
while(res.back() == '0' && res.size() > 1) res.pop_back();
return reversed(res);
}
template<typename T>
static pair<string, T> divide(string a, T b) {//高精度除以低精度
if (b == 0) return {"ERROR", 0};
T r = 0;
string ans;
for (int i = 0; i < a.size(); i ++) {
r = r * 10 + a[i] - '0';
ans.push_back(r / b + '0'), r %= b;
}
reversed(ans);
while(ans.size() > 1 && ans.back() == '0') ans.pop_back();
return {reversed(ans), r};
}
static pair<string, string> divide(string a, string b) {//高精度除以高精度, 复杂度较高
if (b == "0") return {"ERROR", "ERROR"};
if (!comp(a, b)) return {"0", a};
int t = a.size() - b.size();
string ans;
for (int i = 0; i < t; i ++) b.push_back('0');
while (t >= 0) {
int cnt = 0;
while(comp(a, b)) a = subtract(a, b), cnt++;
ans.push_back(cnt + '0');
b.pop_back();
t--;
}
reversed(ans);
if (ans.back() == '0') ans.pop_back();
return {reversed(ans), a};
}
}
实例 codewars 2kyu--Challenge Fun #10: Integer Square Root
Task
For each given a number N, the integer S is called
integer square root
of N ifS x S <= N
and(S+1) x (S+1) > N
.In other words,
S = Math.floor(Math.sqrt(N))
Your task is to calculate the
integer square root
of a givenNumber
.Note: Input is given in string format. That is, the
Number
may be very very large 😉Example
For:
Number = "4"
, result ="2"
.For:
Number = "17"
, result ="4"
.For:
Number = "101"
, result ="10"
.For:
Number = "23232328323215435345345345343458098856756556809400840980980980980809092343243243243243098799634"
, result ="152421548093487868711992623730429930751178496967"
.Input/Output
[input]
stringNumber
number in decimal form.
0 < Number < 10^100^
[output]
a stringinteger squareroot of
Number
.
显然是一道高精度二分, 调用模板完成即可。
using namespace BigNumber;
string integer_square_root(string n) {
string l = "0", r = n;
while (!comp(l, r)) {
auto [mid, rem] = divide(add(add(l, r), "1"), 2);
if (comp(n, multiply(mid, mid))) l = mid;
else r = subtract(mid, "1");
}
return l;
}
C++大数类模板
我们可以模仿java的BigInteger
类, 写一个c++类来模拟正常的整数的各种运算。
由于c++的重载运算符功能, 我们可以做到和原生几乎没有使用区别。
以下是尝试性的实现。
//
// Created by trudbot on 2023/1/19.
//
#include "iostream"
#include "string"
#include "algorithm"
#include "map"
class BigInteger {
public:
BigInteger() {}
BigInteger(long long num) {
if (num < 0) {
this->data = std::to_string(num).substr(1);
this->sign = -1;
} else {
this->data = std::to_string(num);
this->sign = 1;
}
}
BigInteger(std::string num) {
if (num[0] == '-') {
this->data = num.substr(1);
sign = -1;
} else {
this->data = num;
sign = 1;
}
}
BigInteger(const std::string& num, int sign) {
this->data = num, this->sign = sign;
}
BigInteger operator+(const BigInteger& b) const {
BigInteger ans;
if (this->sign == 1 && b.sign == 1) {
ans = BigInteger(add(this->data, b.data), 1);
} else if (this->sign == 1 && b.sign == -1) {
ans = BigInteger(subtract(this->data, b.data));
} else if (this->sign == -1 && b.sign == 1) {
ans = BigInteger(subtract(b.data, this->data));
} else {
ans = BigInteger(add(this->data, b.data), -1);
}
return ans;
}
BigInteger operator+(long long & b) const {
return this->operator+(BigInteger(b));
}
BigInteger operator-(const BigInteger &b) const {
BigInteger ans;
if (this->sign == 1 && b.sign == 1) {
ans = BigInteger(subtract(this->data, b.data));
} else if (this->sign == 1 && b.sign == -1) {
ans = BigInteger(add(this->data, b.data));
} else if (this->sign == -1 && b.sign == 1) {
ans = BigInteger(add(this->data, b.data), -1);
} else {
ans = BigInteger(subtract(b.data, this->data));
}
return ans;
}
BigInteger operator*(const BigInteger &b) {
return BigInteger(multiply(this->data, b.data), this->sign * b.sign);
}
BigInteger operator/(const BigInteger &b) {
if (lessEqual(b.data, limit)) {
return BigInteger(divide(this->data, std::stoll(b.data)).first, this->sign * b.sign);
}
return BigInteger(divide(this->data, b.data).first, this->sign * b.sign);
}
BigInteger operator%(const BigInteger &b) const {
return BigInteger(divide(this->data, b.data).second);
}
void operator*=(const BigInteger &b) {
this->sign *= b.sign;
this->data = multiply(this->data, b.data);
}
void operator/=(const BigInteger &b) {
this->sign *= b.sign;
if (lessEqual(b.data, limit)) {
this->data = divide(this->data, std::stoll(b.data)).first;
} else {
this->data = divide(this->data, b.data).first;
}
}
void operator++() {
*this = *this + 1;
}
void operator--() {
*this = *this - 1;
}
BigInteger& operator=(const BigInteger& b) {
this->data = b.data, this->sign = b.sign;
return *this;
}
template<typename T>
BigInteger& operator=(const T& b) {
*this = BigInteger(b);
return *this;
}
bool operator==(const BigInteger &b) {
return this->sign == b.sign && this->data == b.data;
}
bool operator<(const BigInteger &b) {
if (this->sign == 1 && b.sign == -1) {
return true;
} else if (this->sign == -1 && b.sign == 1) {
return true;
} else if (this->sign == 1 && b.sign == 1) {
return less(this->data, b.data);
} else {
return greater(this->data, b.data);
}
}
bool operator>(const BigInteger &b) {
if (this->sign == 1 && b.sign == -1) {
return true;
} else if (this->sign == -1 && b.sign == 1) {
return false;
} else if (this->sign == 1 && b.sign == 1) {
return greater(this->data, b.data);
} else {
return less(this->data, b.data);
}
}
bool operator<=(const BigInteger &b) {
if (this->sign == 1 && b.sign == -1) {
return false;
} else if (this->sign == -1 && b.sign == 1) {
return true;
} else if (this->sign == 1 && b.sign == 1) {
return lessEqual(this->data, b.data);
} else {
return greaterEqual(this->data, b.data);
}
}
bool operator>=(const BigInteger &b) {
if (this->sign == 1 && b.sign == -1) {
return true;
} else if (this->sign == -1 && b.sign == 1) {
return false;
} else if (this->sign == 1 && b.sign == 1) {
return greaterEqual(this->data, b.data);
} else {
return lessEqual(this->data, b.data);
}
}
bool operator!=(const BigInteger &b) {
return this->sign != b.sign || this->data != b.data;
}
friend std::ostream &operator<<(std::ostream &output, const BigInteger &integer) {
if (integer.sign == -1 && integer.data != "0") {
output << "-" << integer.data;
} else {
output << integer.data;
}
return output;
}
friend std::istream &operator>>(std::istream &input, BigInteger &integer) {
std::string s;
input >> s;
integer = BigInteger(s);
return input;
}
BigInteger pow(long long n) {
BigInteger res = 1, a = *this;
while (n) {
if (n & 1) res = res * a;
a = a * a;
n >>= 1;
}
return res;
}
std::string value() {
return this->data;
}
private:
std::string data;
int sign = 0;
const std::string limit = "9223372036854775807";
static std::string reversed(std::string &s) {//反转字符串并返回
std::reverse(s.begin(), s.end());
return s;
}
static bool greaterEqual(const std::string& a, const std::string& b) {//判断a大于等于b
return greater(a, b) || a == b;
}
static bool less(const std::string& a, const std::string& b) {
if (a.size() != b.size()) return a.size() < b.size();
return a < b;
}
static bool greater(const std::string& a, const std::string& b) {
if (a.size() != b.size()) return a.size() > b.size();
return a > b;
}
static bool lessEqual(const std::string& a, const std::string& b) {
return less(a, b) || a == b;
}
static std::string add(std::string a, std::string b) {//高精度加高精度
std::string ans;
int i = (int)a.size() - 1, j = (int)b.size() - 1, carry = 0;
while (i >= 0 || j >= 0 || carry) {
if (i >= 0) carry += a[i--] - '0';
if (j >= 0) carry += b[j--] - '0';
ans.push_back(carry % 10 + '0');
carry /= 10;
}
return reversed(ans);
}
static std::string subtract(std::string a, std::string b) {//高精度减高精度
if (!greaterEqual(a, b)) return "-" + subtract(b, a);
std::string ans;
int n = a.size(), m = b.size(), carry = 0;
for (int i = n - 1, j = m - 1; i >= 0; i--, j--) {
carry += a[i] - '0';
if (j >= 0) carry -= b[j] - '0';
if (carry < 0) ans.push_back(carry + 10 + '0'), carry = -1;
else ans.push_back(carry + '0'), carry = 0;
}
while (ans.size() > 1 && ans.back() == '0') ans.pop_back();
return reversed(ans);
}
static std::string multiply(std::string s, long long b) {//高精度乘以低精度
if (b == 0) return "0";
reversed(s);
std::string ans;
for (size_t i = 0, carry = 0; i < s.size() || carry; i++) {
if (i < s.size()) carry += (s[i] - '0') * b;
ans.push_back(carry % 10 + '0');
carry /= 10;
}
return reversed(ans);
}
static std::string multiply(std::string a, std::string b) {//高精度乘以高精度
reversed(a), reversed(b);
int n = a.size() + b.size(), ans[n];
for (int i = 0; i < n; i ++) ans[i] = 0;
for (int i = 0; i < a.size(); i ++)
for (int j = 0; j < b.size(); j ++)
ans[i + j] += (a[i] - '0') *(b[j] - '0');
int carry = 0; std::string res;
for (int i = 0; i < n; i ++) {
ans[i] += carry, carry = ans[i] / 10, ans[i] %= 10;
res.push_back(ans[i] + '0');
}
while(res.back() == '0' && res.size() > 1) res.pop_back();
return reversed(res);
}
static std::pair<std::string, long long> divide(const std::string& a, long long b) {//高精度除以低精度
if (b == 0) return {"ERROR", 0};
long long r = 0;
std::string ans;
for (char i : a) {
r = r * 10 + i - '0';
ans.push_back(r / b + '0'), r %= b;
}
reversed(ans);
while(ans.size() > 1 && ans.back() == '0') ans.pop_back();
return {reversed(ans), r};
}
static std::pair<std::string, std::string> divide(std::string a, std::string b) {//高精度除以高精度, 复杂度较高
if (b == "0") return {"ERROR", "ERROR"};
if (less(a, b)) return {"0", a};
int t = a.size() - b.size();
std::string ans;
for (int i = 0; i < t; i ++) b.push_back('0');
while (t >= 0) {
int cnt = 0;
while(greaterEqual(a, b)) a = subtract(a, b), cnt++;
ans.push_back(cnt + '0');
b.pop_back();
t--;
}
reversed(ans);
if (ans.back() == '0') ans.pop_back();
return {reversed(ans), a};
}
};
用此模板, 上面那题的代码可改成
using namespace std;
using Int = BigInteger;
string integer_square_root(string args) {
Int n = Int(args);
Int l = 0, r = n;
while (l < r) {
Int mid = (l + r + 1) / 2;
if (mid * mid <= n) l = mid;
else r = mid - 1;
}
return l.value();
}