Computable Continued Fractions
Summary
We formalise the standard computation of (regular) continued fractions for linear ordered floor fields. The algorithm is rather simple. Here is an outline of the procedure adapted from Wikipedia:
Take a value v
. We call ⌊v⌋
the integer part of v
and v − ⌊v⌋
the fractional part of v
.
A continued fraction representation of v
can then be given by [⌊v⌋; b₀, b₁, b₂,...]
,
where [b₀; b₁, b₂,...]
recursively is the continued fraction representation of 1 / (v − ⌊v⌋)
.
This process stops when the fractional part hits 0.
In other words: to calculate a continued fraction representation of a number v
, write down the
integer part (i.e. the floor) of v
. Subtract this integer part from v
. If the difference is 0,
stop; otherwise find the reciprocal of the difference and repeat. The procedure will terminate if
and only if v
is rational.
For an example, refer to int_fract_pair.stream
.
Main definitions
generalized_continued_fraction.int_fract_pair.stream
: computes the stream of integer and fractional parts of a given value as described in the summary.generalized_continued_fraction.of
: computes the generalised continued fraction of a valuev
. In fact, it computes a regular continued fraction that terminates if and only ifv
is rational (those proofs will be added in a future commit).
Implementation Notes
There is an intermediate definition generalized_continued_fraction.int_fract_pair.seq1
between
generalized_continued_fraction.int_fract_pair.stream
and generalized_continued_fraction.of
to wire up things. User should not (need to) directly interact with it.
The computation of the integer and fractional pairs of a value can elegantly be
captured by a recursive computation of a stream of option pairs. This is done in
int_fract_pair.stream
. However, the type then does not guarantee the first pair to always be
some
value, as expected by a continued fraction.
To separate concerns, we first compute a single head term that always exists in
generalized_continued_fraction.int_fract_pair.seq1
followed by the remaining stream of option
pairs. This sequence with a head term (seq1
) is then transformed to a generalized continued
fraction in generalized_continued_fraction.of
by extracting the wanted integer parts of the
head term and the stream.
References
- https://en.wikipedia.org/wiki/Continued_fraction
Tags
numerics, number theory, approximations, fractions
- b : ℤ
- fr : K
We collect an integer part b = ⌊v⌋
and fractional part fr = v - ⌊v⌋
of a value v
in a pair
⟨b, fr⟩
.
Interlude: define some expected coercions and instances.
Make an int_fract_pair
printable.
Equations
- generalized_continued_fraction.int_fract_pair.inhabited K = {default := {b := 0, fr := inhabited.default K _inst_1}}
Interlude: define some expected coercions.
Coerce a pair by coercing the fractional component.
Equations
- generalized_continued_fraction.int_fract_pair.has_coe_to_int_fract_pair = {coe := λ (_x : generalized_continued_fraction.int_fract_pair K), generalized_continued_fraction.int_fract_pair.has_coe_to_int_fract_pair._match_1 _x}
- generalized_continued_fraction.int_fract_pair.has_coe_to_int_fract_pair._match_1 {b := b, fr := fr} = {b := b, fr := ↑fr}
Creates the integer and fractional part of a value v
, i.e. ⟨⌊v⌋, v - ⌊v⌋⟩
.
Creates the stream of integer and fractional parts of a value v
needed to obtain the continued
fraction representation of v
in generalized_continued_fraction.of
. More precisely, given a value
v : K
, it recursively computes a stream of option ℤ × K
pairs as follows:
stream v 0 = some ⟨⌊v⌋, v - ⌊v⌋⟩
stream v (n + 1) = some ⟨⌊frₙ⁻¹⌋, frₙ⁻¹ - ⌊frₙ⁻¹⌋⟩
, ifstream v n = some ⟨_, frₙ⟩
andfrₙ ≠ 0
stream v (n + 1) = none
, otherwise
For example, let (v : ℚ) := 3.4
. The process goes as follows:
stream v 0 = some ⟨⌊v⌋, v - ⌊v⌋⟩ = some ⟨3, 0.4⟩
stream v 1 = some ⟨⌊0.4⁻¹⌋, 0.4⁻¹ - ⌊0.4⁻¹⌋⟩ = some ⟨⌊2.5⌋, 2.5 - ⌊2.5⌋⟩ = some ⟨2, 0.5⟩
stream v 2 = some ⟨⌊0.5⁻¹⌋, 0.5⁻¹ - ⌊0.5⁻¹⌋⟩ = some ⟨⌊2⌋, 2 - ⌊2⌋⟩ = some ⟨2, 0⟩
stream v n = none
, forn ≥ 3
Equations
- generalized_continued_fraction.int_fract_pair.stream v (n + 1) = generalized_continued_fraction.int_fract_pair.stream v n >>= λ (ap_n : generalized_continued_fraction.int_fract_pair K), ite (ap_n.fr = 0) option.none ↑(generalized_continued_fraction.int_fract_pair.of (ap_n.fr)⁻¹)
- generalized_continued_fraction.int_fract_pair.stream v 0 = option.some (generalized_continued_fraction.int_fract_pair.of v)
Shows that int_fract_pair.stream
has the sequence property, that is once we return none
at
position n
, we also return none
at n + 1
.
Uses int_fract_pair.stream
to create a sequence with head (i.e. seq1
) of integer and fractional
parts of a value v
. The first value of int_fract_pair.stream
is never none
, so we can safely
extract it and put the tail of the stream in the sequence part.
This is just an intermediate representation and users should not (need to) directly interact with it.
The setup of rewriting/simplification lemmas that make the definitions easy to use is done in
algebra.continued_fractions.computation.translations
.
Returns the generalized_continued_fraction
of a value. In fact, the returned gcf is also
a continued_fraction
that terminates if and only if v
is rational (those proofs will be
added in a future commit).
The continued fraction representation of v
is given by [⌊v⌋; b₀, b₁, b₂,...]
, where
[b₀; b₁, b₂,...]
recursively is the continued fraction representation of 1 / (v − ⌊v⌋)
. This
process stops when the fractional part v- ⌊v⌋
hits 0 at some step.
The implementation uses int_fract_pair.stream
to obtain the partial denominators of the continued
fraction. Refer to said function for more details about the computation process.