You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/others/stern_brocot_tree_farey_sequences.md
+63-9Lines changed: 63 additions & 9 deletions
Original file line number
Diff line number
Diff line change
@@ -135,22 +135,76 @@ void build(int a = 0, int b = 1, int c = 1, int d = 0, int level = 1) {
135
135
136
136
The search algorithm was already described in the proof that all fractions appear in the tree, but we will repeat it here. The algorithm is a binary search algorithm. Initially we stand at the root of the tree and we compare our target with the current fraction. If they are the same we are done and stop the process. If our target is smaller we move to the left child, otherwise we move to the right child.
137
137
138
-
Here is an implementation that returns the path to a given fraction $\frac{x}{y}$ as a sequence of `'L'` and `'R'` characters, meaning traversal to the left and right child respectively. This sequence of characters uniquely defines all positive fractions and is called the Stern-Brocot number system.
138
+
### Naive search
139
+
140
+
Here is an implementation that returns the path to a given fraction $\frac{p}{q}$ as a sequence of `'L'` and `'R'` characters, meaning traversal to the left and right child respectively. This sequence of characters uniquely defines all positive fractions and is called the Stern-Brocot number system.
139
141
140
142
```cpp
141
-
string find(int x, int y, int a = 0, int b = 1, int c = 1, int d = 0) {
142
-
int m = a + c, n = b + d;
143
-
if (x == m && y == n)
144
-
return "";
145
-
if (x*n < y*m)
146
-
return 'L' + find(x, y, a, b, m, n);
147
-
else
148
-
return 'R' + find(x, y, m, n, c, d);
143
+
string find(int p, int q) {
144
+
int pL = 0, qL = 1;
145
+
int pR = 1, qR = 0;
146
+
int pM = 1, qM = 1;
147
+
string res;
148
+
while(pM != p || qM != q) {
149
+
if(p * qM < pM * q) {
150
+
res += 'L';
151
+
tie(pR, qR) = {pM, qM};
152
+
} else {
153
+
res += 'R';
154
+
tie(pL, qL) = {pM, qM};
155
+
}
156
+
tie(pM, qM) = pair{pL + pR, qL + qR};
157
+
}
158
+
return res;
149
159
}
150
160
```
151
161
152
162
Irrational numbers in the Stern-Brocot number system corresponds to infinite sequences of characters. Along the endless path towards the irrational number the algorithm will find reduced fractions with gradually increasing denominators that provides increasingly better approximations of the irrational number. So by taking a prefix of the infinite sequence approximations with any desired precision can be achieved. This application is important in watch-making, which explains why the tree was discovered in that domain.
153
163
164
+
Note that for a fraction $\frac{p}{q}$, the length of the resulting sequence could be as large as $O(p+q)$, for example when the fraction is of form $\frac{p}{1}$. This means that the algorithm above **should not be used, unless this is an acceptable complexity**!
165
+
166
+
### Logarithmic search
167
+
168
+
Fortunately, it is possible to enhance the algorithm above to guarantee $O(\log (p+q))$ complexity. For this we should note that if the current boundary fractions are $\frac{p_L}{q_L}$ and $\frac{p_R}{q_R}$, then by doing $a$ steps to the right we move to the fraction $\frac{p_L + a p_R}{q_L + a q_R}$, and by doing $a$ steps to the left, we move to the fraction $\frac{a p_L + p_R}{a q_L + q_R}$.
169
+
170
+
Therefore, instead of doing steps of `L` or `R` one by one, we can do $k$ steps in the same direction at once, after which we would switch to going into other direction, and so on. In this way, we can find the path to the fraction $\frac{p}{q}$ as its run-length encoding.
171
+
172
+
As the directions alternate this way, we will always know which one to take. So, for convenience we may represent a path to a fraction $\frac{p}{q}$ as a sequence of fractions
such that $\frac{p_{k-1}}{q_{k-1}}$ and $\frac{p_k}{q_k}$ are the boundaries of the search interval on the $k$-th step, starting with $\frac{p_0}{q_0} = \frac{0}{1}$ and $\frac{p_1}{q_1} = \frac{1}{0}$. Then, after the $k$-th step we move to a fraction
where $a_k$ is a positive integer number. If you're familiar with [continued fractions](), you would recognize that the sequence $\frac{p_i}{q_i}$ is the sequence of the convergent fractions of $\frac{p}{q}$ and the sequence $[a_1; a_2, \dots, a_{n}, 1]$ represents the continued fraction of $\frac{p}{q}$.
185
+
186
+
This allows to find the run-length encoding of the path to $\frac{p}{q}$ in the manner which follows the algorithm for computing continued fraction representation of the fraction $\frac{p}{q}$:
187
+
188
+
```cpp
189
+
autofind(int p, int q) {
190
+
bool right = true;
191
+
vector<pair<int, char>> res;
192
+
while(q) {
193
+
res.emplace_back(p / q, right ? 'R' : 'L');
194
+
tie(p, q) = pair{q, p % q};
195
+
right ^= 1;
196
+
}
197
+
res.back().first--;
198
+
return res;
199
+
}
200
+
```
201
+
202
+
However, this approach only works if we already know $\frac{p}{q}$ and want to find its place in the Stern-Brocot tree.
203
+
204
+
On practice, it is often the case that $\frac{p}{q}$ is not known in advance, but we are able to check for specific $\frac{x}{y}$ whether $\frac{x}{y} < \frac{p}{q}$.
205
+
206
+
Knowing this, we can emulate the search on Stern-Brocot tree by maintaining the current boundaries $\frac{p_{k-1}}{q_{k-1}}$ and $\frac{p_k}{q_k}$, and finding each $a_k$ via binary search. The algorithm then is a bit more technical and potentially have a complexity of $O(\log^2(x+y))$, unless the problem formulation allows you to find $a_k$ faster (for example, using `floor` of some known expression).
207
+
154
208
## Farey Sequence
155
209
156
210
The Farey sequence of order $n$ is the sorted sequence of fractions between $0$ and $1$ whose denominators do not exceed $n$.
0 commit comments