Skip to content

Commit 38e5819

Browse files
committed
add rational approximation
1 parent 735d548 commit 38e5819

File tree

3 files changed

+109
-0
lines changed

3 files changed

+109
-0
lines changed

number/rational_approximation.hpp

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#pragma once
2+
3+
#include <cassert>
4+
#include <utility>
5+
#include <vector>
6+
7+
// Rational approximation based on Stern–Brocot tree
8+
// Input: N > 0, num >= 0, den >= 0 (num > 0 or den > 0)
9+
// return {{p, q}, {r, s}} where p/q <= num/den <= r/s. Strictly,
10+
// - p/q = max(n / d | n / d <= num / den, 0 <= n <= N, 1 <= d <= N)
11+
// - r/s = min(n / d | num / den <= n / d, 0 <= n <= N, 1 <= d <= N)
12+
template <class Int>
13+
std::pair<std::pair<Int, Int>, std::pair<Int, Int>> rational_approximation(Int N, Int num, Int den) {
14+
assert(N >= 1);
15+
assert(num >= 0);
16+
assert(den >= 0);
17+
assert(num > 0 or den > 0);
18+
19+
if (num == Int(0)) return {{Int(0), Int(1)}, {Int(0), Int(1)}}; // 0
20+
if (den == Int(0)) return {{Int(1), Int(0)}, {Int(1), Int(0)}}; // inf
21+
22+
// p/q <= num/den <= r/s
23+
Int p = 0, q = 1, r = 1, s = 0;
24+
25+
bool is_left = false;
26+
while (true) {
27+
Int max_steps = num / den;
28+
29+
if (is_left) {
30+
// r + steps * p <= N
31+
// s + steps * q <= N
32+
33+
if (p > Int(0)) max_steps = std::min(max_steps, (N - r) / p);
34+
max_steps = std::min(max_steps, (N - s) / q);
35+
36+
r += max_steps * p;
37+
s += max_steps * q;
38+
} else {
39+
// p + steps * r <= N
40+
// q + steps * s <= N
41+
42+
max_steps = std::min(max_steps, (N - p) / r);
43+
if (s > Int(0)) max_steps = std::min(max_steps, (N - q) / s);
44+
45+
p += max_steps * r;
46+
q += max_steps * s;
47+
}
48+
49+
if (is_left and !max_steps) break;
50+
51+
num -= max_steps * den;
52+
53+
if (num == Int(0)) {
54+
if (is_left) {
55+
return {{r, s}, {r, s}};
56+
} else {
57+
return {{p, q}, {p, q}};
58+
}
59+
}
60+
61+
std::swap(num, den);
62+
is_left = !is_left;
63+
}
64+
65+
return {{p, q}, {r, s}};
66+
}

number/rational_approximation.md

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
title: Rational approximation (有理数近似)
3+
documentation_of: ./rational_approximation.hpp
4+
---
5+
6+
与えられた非負の有理数 `num / den` に対して,分子・分母がともに $N$ 以下の分数で両側から近似する.
7+
8+
## 使用方法
9+
10+
```cpp
11+
int N, num, den;
12+
const auto [l, r] = rational_approximation<int>(N, num, den);
13+
14+
// lnum / lden <= num / den <= rnum / rden
15+
// max(lnum, lden, rnum, rden) <= N
16+
const auto [lnum, lden] = l;
17+
const auto [rnum, rden] = r;
18+
```
19+
20+
## 問題例
21+
22+
- [Library Checker: Rational Approximation](https://judge.yosupo.jp/problem/rational_approximation)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#define PROBLEM "https://judge.yosupo.jp/problem/rational_approximation"
2+
#include "../rational_approximation.hpp"
3+
4+
#include <cassert>
5+
#include <iostream>
6+
using namespace std;
7+
8+
int main() {
9+
cin.tie(nullptr), ios::sync_with_stdio(false);
10+
11+
int T;
12+
cin >> T;
13+
while (T--) {
14+
int N, x, y;
15+
cin >> N >> x >> y;
16+
const auto [l, r] = rational_approximation<int>(N, x, y);
17+
const auto [lnum, lden] = l;
18+
const auto [rnum, rden] = r;
19+
cout << lnum << ' ' << lden << ' ' << rnum << ' ' << rden << '\n';
20+
}
21+
}

0 commit comments

Comments
 (0)