Skip to content

Commit 03f3a93

Browse files
committed
Add dyadic rational
1 parent 59e0272 commit 03f3a93

File tree

2 files changed

+142
-0
lines changed

2 files changed

+142
-0
lines changed

number/dyadic_rational.hpp

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#include <cassert>
2+
#include <limits>
3+
#include <type_traits>
4+
5+
// Dyadic rational, surreal numbers (超現実数)
6+
// https://atcoder.jp/contests/abc229/submissions/28887690
7+
template <class Int, class Uint = unsigned long long> struct DyadicRational {
8+
Int integ; // 整数部分
9+
Uint frac; // 小数部分の分子
10+
11+
static constexpr int FracLen = std::numeric_limits<Uint>::digits - 1; // 2^63
12+
static constexpr Uint denom = Uint(1) << FracLen; // 小数部分の分母
13+
14+
DyadicRational(Int x = 0) : integ(x), frac(0) {
15+
static_assert(
16+
0 < FracLen and FracLen < std::numeric_limits<Uint>::digits, "FracLen value error");
17+
static_assert(std::is_signed<Int>::value == true, "Int must be signed");
18+
}
19+
static DyadicRational _construct(Int x, Uint frac_) {
20+
DyadicRational ret(x);
21+
ret.frac = frac_;
22+
return ret;
23+
}
24+
double to_double() const { return integ + double(frac) / denom; }
25+
26+
// static DyadicRational from_rational(Int num, int lg_den);
27+
bool operator==(const DyadicRational &r) const { return integ == r.integ and frac == r.frac; }
28+
bool operator!=(const DyadicRational &r) const { return integ != r.integ or frac != r.frac; }
29+
bool operator<(const DyadicRational &r) const {
30+
return integ != r.integ ? integ < r.integ : frac < r.frac;
31+
}
32+
bool operator<=(const DyadicRational &r) const { return (*this == r) or (*this < r); }
33+
bool operator>(const DyadicRational &r) const { return r < *this; }
34+
bool operator>=(const DyadicRational &r) const { return r <= *this; }
35+
36+
DyadicRational operator+(const DyadicRational &r) const {
37+
auto i = integ + r.integ;
38+
auto f = frac + r.frac;
39+
if (f >= denom) ++i, f -= denom; // overflow
40+
return DyadicRational::_construct(i, f);
41+
}
42+
DyadicRational operator-(const DyadicRational &r) const {
43+
auto i = integ - r.integ;
44+
auto f = frac - r.frac;
45+
if (f > denom) --i, f += denom; // overflow
46+
return DyadicRational::_construct(i, f);
47+
}
48+
DyadicRational operator-() const { return DyadicRational() - *this; }
49+
DyadicRational &operator+=(const DyadicRational &r) { return *this = *this + r; }
50+
51+
DyadicRational right() const {
52+
if (frac == 0) {
53+
if (integ >= 0) {
54+
return DyadicRational(integ + 1);
55+
} else {
56+
return DyadicRational::_construct(integ, Uint(1) << (FracLen - 1));
57+
}
58+
}
59+
int d = __builtin_ctzll(frac);
60+
return DyadicRational::_construct(integ, frac ^ (Uint(1) << (d - 1)));
61+
}
62+
DyadicRational left() const {
63+
if (frac == 0) {
64+
if (integ <= 0) {
65+
return DyadicRational(integ - 1);
66+
} else {
67+
return DyadicRational::_construct(integ - 1, Uint(1) << (FracLen - 1));
68+
}
69+
}
70+
int d = __builtin_ctzll(frac);
71+
return DyadicRational::_construct(integ, frac ^ (Uint(3) << (d - 1)));
72+
}
73+
74+
// Surreal number { l | r }
75+
static DyadicRational surreal(const DyadicRational &l, const DyadicRational &r) {
76+
assert(l < r);
77+
DyadicRational x(0);
78+
if (l.integ > 0) x = DyadicRational(l.integ);
79+
if (r.integ < 0) x = DyadicRational(r.integ);
80+
while (true) {
81+
if (x <= l) {
82+
x = x.right();
83+
} else if (x >= r) {
84+
x = x.left();
85+
} else {
86+
break;
87+
}
88+
}
89+
return x;
90+
}
91+
template <class OStream> friend OStream &operator<<(OStream &os, const DyadicRational &x) {
92+
return os << x.to_double();
93+
}
94+
};
95+
// using dyrational = DyadicRational<long long>;

number/dyadic_rational.md

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
---
2+
title: Dyadic rational, surreal number (二進分数・固定小数点数,Conway の構成)
3+
documentation_of: ./dyadic_rational.hpp
4+
---
5+
6+
分母が二冪の有理数 `DyadicRational<Int, Uint = unsigned long long>` の実装,また超現実数の構成法に基づく $\\{ l \mid r \\}$ の計算.組合せゲーム理論などへの応用がある.
7+
8+
`Int` が整数部分を表すための符号付整数型で `int``long long` の利用を想定,`Uint` が小数部分を表すための符号なし整数型で `unsigned``unsigned long long` の利用を想定(`__uint128_t` も動くかもしれない).メンバ変数 `integ`, `frac` がそれぞれ整数部分と小数部分に対応し,
9+
$$
10+
x = \mathrm{integ} + \frac{\mathrm{frac}}{2^\mathrm{FracLen}}
11+
$$
12+
がこのクラスのインスタンスが表す有理数 $x$ である.
13+
実装の制約上分母のオーダーは(`Uint = unsigned long long` の場合)$2^{63}$ が上限となる.
14+
15+
## 使用方法
16+
17+
### `DyadicRational<Int>(Int x)`
18+
19+
コンストラクタ.整数 `x` に対応する元を生成する.
20+
21+
### `double to_double()`
22+
23+
インスタンスが表す有理数 $x$ を `double` 型に変換.
24+
25+
### `DyadicRational<Int> right()`
26+
27+
[Surreal number (超現実数)の構成過程を表す木](https://en.wikipedia.org/wiki/Surreal_number#/media/File:Surreal_number_tree.svg) において,現在の値 $x$ の右側の子の値を返す.
28+
29+
### `DyadicRational<Int> left()`
30+
31+
`right()` とは逆に,現在の値 $x$ の左側の子の値を返す.
32+
33+
### `DyadicRational<Int> DyadicRational<Int>::surreal(DyadicRational<Int> l, DyadicRational<Int> r)`
34+
35+
$l < r$ を満たす二進分数 $l$, $r$ について,$l < x < r$ を満たし surreal number の構成過程を表す木で根($0$)に最も近い要素(Conway の記法に基づくと $\\{ l \mid r \\}$)の値を返す.$l < r$ を満たさない場合 runtime error となる.
36+
37+
現在は根から一歩ずつ `right()` または `left()` で探索していく実装になっているが,場合分けと bit 演算を頑張れば $O(1)$ になると思われる.
38+
39+
## 問題例
40+
41+
- [AtCoder Beginner Contest 229 H - Advance or Eat](https://atcoder.jp/contests/abc229/tasks/abc229_h)
42+
43+
### リンク
44+
45+
- [組合せゲーム理論入門(1)](http://www.ivis.co.jp/text/20111102.pdf) 組合せゲーム理論の観点からの解説.
46+
- [解説 - NECプログラミングコンテスト2021(AtCoder Beginner Contest 229)](https://atcoder.jp/contests/abc229/editorial/2977) 非不偏ゲーム(Partisan Game)の問題例とその解き方.
47+
- [提出 #27486895 - NECプログラミングコンテスト2021(AtCoder Beginner Contest 229)](https://atcoder.jp/contests/abc229/submissions/27486895) Nyaan さんの実装.

0 commit comments

Comments
 (0)