0%

高精度模板

最近发现了一个很有意思的网站:codewars, 上面有很多有趣的'kata', 通过kata可以提升你的段位。

codewars上的题目和传统的竞赛题目的风格不太一样, 而且用户是可以创建kata的, 这一点我觉得很有意思。

在codewars中老是遇到高精度的题目(高精度在普通OJ中基本只有模板题), 每次都要手写及处理细节十分的腻,于是决定把高精度好好的写一遍记录下来, 供未来copy。

高模板模板类

包含:

  • 高精度加高精度
  • 高精度减高精度
  • 高精度乘低精度
  • 高精度乘高精度
  • 高精度除以低精度
  • 高精度除以高精度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
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 if S 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 given Number.

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] string Number

number in decimal form.

0 < Number < 10100

  • [output] a string

integer squareroot of Number.

显然是一道高精度二分, 调用模板完成即可。

1
2
3
4
5
6
7
8
9
10
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++的重载运算符功能, 我们可以做到和原生几乎没有使用区别。

以下是尝试性的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
//
// 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};
}
};

用此模板, 上面那题的代码可改成

1
2
3
4
5
6
7
8
9
10
11
12
13
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();
}
-------------本文结束感谢您的阅读-------------

欢迎关注我的其它发布渠道