Commit 15ea57a24b3e05b75d9e6250bdefc6000807162b
1 parent
5cd265a6
Add continued fraction and use them to get a better sqrt approximation
Showing
3 changed files
with
204 additions
and
97 deletions
1 | 1 | // |
2 | 2 | // Some code to support fractional numbers for full precision rational number |
3 | -// calculations. | |
4 | -// TODO | |
5 | -// - maybe this could be build as a generic for all integral numbers. | |
6 | -// (Question, how can I assure that it is build from integral numbers? | |
3 | +// calculations. (At least for the standard operations.) | |
4 | +// This also implements a sqrt on fractional numbers, which can not be precise | |
5 | +// because of the irrational nature of most sqare roots. | |
6 | +// Fractions can only represent rational numbers precise. | |
7 | 7 | // |
8 | 8 | // Georg Hopp <georg@steffers.org> |
9 | 9 | // |
... | ... | @@ -31,6 +31,10 @@ use std::num::TryFromIntError; |
31 | 31 | #[derive(Debug, Eq, Clone, Copy)] |
32 | 32 | pub struct Fractional (pub i64, pub i64); |
33 | 33 | |
34 | +pub type Continuous = Vec<i64>; | |
35 | + | |
36 | +pub type Error = &'static str; | |
37 | + | |
34 | 38 | #[inline] |
35 | 39 | fn hcf(x :i64, y :i64) -> i64 { |
36 | 40 | match y { |
... | ... | @@ -39,6 +43,38 @@ fn hcf(x :i64, y :i64) -> i64 { |
39 | 43 | } |
40 | 44 | } |
41 | 45 | |
46 | +// calculate a sqrt as continued fraction sequence. Taken from: | |
47 | +// https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Continued_fraction_expansion | |
48 | +fn sqrt_cfrac(x :i64, a0 :i64) -> Continuous { | |
49 | + let v :Continuous = Vec::new(); | |
50 | + | |
51 | + fn inner(mut v :Continuous, | |
52 | + x :i64, | |
53 | + a0 :i64, | |
54 | + mn :i64, | |
55 | + dn :i64, | |
56 | + an :i64) -> Continuous { | |
57 | + let mn_1 = dn * an - mn; | |
58 | + let dn_1 = (x - mn_1 * mn_1) / dn; | |
59 | + let an_1 = (a0 + mn_1) / dn_1; | |
60 | + | |
61 | + v.push(an); | |
62 | + match v.len() { | |
63 | + 10 => v, | |
64 | + _ => inner(v, x, a0, mn_1, dn_1, an_1), | |
65 | + } | |
66 | + | |
67 | + // This convergence criterium is not good for very small x | |
68 | + // thus I decided to break the iteration at constant time. | |
69 | +// match an_1 == 2 * a0 { | |
70 | +// true => v, | |
71 | +// _ => inner(v, x, a0, mn_1, dn_1, an_1), | |
72 | +// } | |
73 | + } | |
74 | + | |
75 | + inner(v, x, a0, 0, 1, a0) | |
76 | +} | |
77 | + | |
42 | 78 | impl Fractional { |
43 | 79 | #[inline] |
44 | 80 | pub fn gcd(self, other: Self) -> i64 { |
... | ... | @@ -63,31 +99,54 @@ impl Fractional { |
63 | 99 | self.1 |
64 | 100 | } |
65 | 101 | |
66 | - pub fn sqrt(self) -> Self { | |
102 | + // This is a really bad approximation of sqrt for a fractional... | |
103 | + // for (9/3) it will result 3 which if way to far from the truth, | |
104 | + // which is ~1.7320508075 | |
105 | + // BUT we can use this value as starting guess for creating a | |
106 | + // continous fraction for the sqrt... and create a much better | |
107 | + // fractional representation of the sqrt. | |
108 | + // So, if inner converges, but is not a perfect square (does not | |
109 | + // end up in an Ordering::Equal - which is the l > h case) | |
110 | + // we use the l - 1 as starting guess for sqrt_cfrac. | |
111 | + // taken from: | |
112 | + // https://www.geeksforgeeks.org/square-root-of-an-integer/ | |
113 | + pub fn sqrt(self) -> Result<Self, Error> { | |
67 | 114 | // find the sqrt of x in O(log x/2). |
68 | - fn floor_sqrt(x :i64) -> i64 { | |
69 | - fn inner(l :i64, h :i64, x :i64) -> i64 { | |
115 | + // This stops if a perfect sqare was found. Else it passes | |
116 | + // the found value as starting guess to the continous fraction | |
117 | + // sqrt function. | |
118 | + fn floor_sqrt(x :i64) -> Fractional { | |
119 | + fn inner(l :i64, h :i64, x :i64) -> Fractional { | |
70 | 120 | if l > h { |
71 | - l - 1 | |
121 | + (&sqrt_cfrac(x, l - 1)).into() | |
72 | 122 | } else { |
73 | 123 | let m = (l + h) / 2; |
74 | 124 | match x.cmp(&(m * m)) { |
75 | - Ordering::Equal => m, | |
76 | - Ordering::Less => inner(l, m, x), | |
125 | + Ordering::Equal => m.into(), | |
126 | + Ordering::Less => inner(l, m - 1, x), | |
77 | 127 | Ordering::Greater => inner(m + 1, h, x), |
78 | 128 | } |
79 | 129 | } |
80 | 130 | } |
81 | 131 | |
82 | - match x.cmp(&0) { | |
83 | - Ordering::Equal => 0, | |
84 | - Ordering::Less => -inner(1, -x / 2, -x), | |
85 | - Ordering::Greater => inner(1, x / 2, x), | |
86 | - } | |
132 | + inner(1, x / 2, x) | |
87 | 133 | } |
88 | 134 | |
89 | 135 | let Fractional(n, d) = self; |
90 | - Fractional(floor_sqrt(n), floor_sqrt(d)).reduce() | |
136 | + | |
137 | + let n = match n.cmp(&0) { | |
138 | + Ordering::Equal => 0.into(), | |
139 | + Ordering::Less => return Err("sqrt on negative undefined"), | |
140 | + Ordering::Greater => floor_sqrt(n), | |
141 | + }; | |
142 | + | |
143 | + let d = match d.cmp(&0) { | |
144 | + Ordering::Equal => return Err("division by zero"), | |
145 | + Ordering::Less => return Err("sqrt on negative undefined"), | |
146 | + Ordering::Greater => floor_sqrt(d), | |
147 | + }; | |
148 | + | |
149 | + Ok(n / d) | |
91 | 150 | } |
92 | 151 | } |
93 | 152 | |
... | ... | @@ -139,6 +198,37 @@ impl TryInto<(i32, i32)> for Fractional { |
139 | 198 | } |
140 | 199 | } |
141 | 200 | |
201 | +impl Into<Continuous> for Fractional { | |
202 | + // general continous fraction form of a fractional... | |
203 | + fn into(self) -> Continuous { | |
204 | + let v :Continuous = Vec::new(); | |
205 | + | |
206 | + fn inner(mut v :Continuous, f :Fractional) -> Continuous { | |
207 | + let Fractional(n, d) = f; | |
208 | + let a = n / d; | |
209 | + let Fractional(_n, _d) = f - a.into(); | |
210 | + | |
211 | + v.push(a); | |
212 | + match _n { | |
213 | + 1 => { v.push(_d); v }, | |
214 | + _ => inner(v, Fractional(_d, _n)), | |
215 | + } | |
216 | + } | |
217 | + | |
218 | + inner(v, self) | |
219 | + } | |
220 | +} | |
221 | + | |
222 | +impl From<&Continuous> for Fractional { | |
223 | + fn from(x :&Continuous) -> Self { | |
224 | + let Self(n, d) = x.iter().rev().fold(Fractional(0, 1), |acc, x| { | |
225 | + let Self(an, ad) = acc + (*x).into(); | |
226 | + Self(ad, an) | |
227 | + }); | |
228 | + Self(d, n) | |
229 | + } | |
230 | +} | |
231 | + | |
142 | 232 | impl PartialEq for Fractional { |
143 | 233 | fn eq(&self, other: &Self) -> bool { |
144 | 234 | let Fractional(n1, d1) = self; | ... | ... |
... | ... | @@ -18,11 +18,11 @@ |
18 | 18 | // You should have received a copy of the GNU General Public License |
19 | 19 | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
20 | 20 | // |
21 | -use std::convert::{TryFrom, TryInto}; | |
21 | +use std::convert::{TryFrom, TryInto, Into}; | |
22 | 22 | use std::num::TryFromIntError; |
23 | 23 | use std::f64::consts::PI as FPI; |
24 | 24 | |
25 | -use fractional::fractional::{Fractional, from_vector}; | |
25 | +use fractional::fractional::{Fractional, from_vector, Continuous}; | |
26 | 26 | use fractional::trigonometry::{sin, cos, PI}; |
27 | 27 | |
28 | 28 | struct Vector { |
... | ... | @@ -37,84 +37,96 @@ fn mean(v: &Vec<i64>) -> Result<Fractional, TryFromIntError> { |
37 | 37 | Ok(Fractional(r, l)) |
38 | 38 | } |
39 | 39 | |
40 | -fn main() { | |
41 | - let a = vec![3, 6, 1, 9]; | |
42 | - let b = from_vector(&a); | |
43 | - let c = mean(&a).unwrap(); // This might fail if the len of the | |
44 | - // vector (usize) does not fit into i32. | |
45 | - let d :f64 = c.try_into().unwrap(); | |
46 | - let e :f64 = Fractional::try_into(c).unwrap(); | |
47 | - | |
48 | - let f = Fractional(9, 4); | |
49 | - let g = Fractional(-9, 4); | |
50 | - | |
51 | - println!(" [i32] : {:?}" , a); | |
52 | - println!(" [Fractional] : {:?}" , b); | |
53 | - println!(" mean of [i32] : {}" , c); | |
54 | - println!(" as f64 : {}" , d); | |
55 | - println!(" and as f64 : {}" , e); | |
56 | - println!(" again as f64 : {}" , TryInto::<f64>::try_into(c).unwrap()); | |
57 | - println!(" sqrt f : {}" , f.sqrt()); | |
58 | - println!(" sqrt f as f64 : {}" , TryInto::<f64>::try_into(f.sqrt()).unwrap()); | |
59 | - println!(" sqrt g : {}" , g.sqrt()); | |
60 | - println!(" sqrt g as f64 : {}" , TryInto::<f64>::try_into(g.sqrt()).unwrap()); | |
40 | +fn common_fractional() { | |
41 | + let a = vec![3, 6, 1, 9]; | |
42 | + let b = from_vector(&a); | |
43 | + let c = mean(&a).unwrap(); // This might fail if the len of the | |
44 | + // vector (usize) does not fit into i32. | |
45 | + let cr :f64 = c.try_into().unwrap(); | |
46 | + | |
47 | + println!(" [i32] : {:?}", a); | |
48 | + println!(" [Fractional] : {:?}", b); | |
49 | + println!(" mean of [i32] : {}" , c); | |
50 | + println!(" as f64 : {}" , cr); | |
51 | + println!(" again as f64 : {}" , TryInto::<f64>::try_into(c).unwrap()); | |
52 | +} | |
53 | + | |
54 | +fn continuous() { | |
55 | + let d = Fractional(45, 16); | |
56 | + let e = Fractional(16, 45); | |
57 | + | |
58 | + let dc :Continuous = d.into(); | |
59 | + let ec :Continuous = e.into(); | |
60 | + | |
61 | + println!("cont frac of d : {} => {:?}", d, dc); | |
62 | + println!("cont frac of e : {} => {:?}", e, ec); | |
63 | + println!(" reverted dc : {:?} {}", dc, Into::<Fractional>::into(&dc)); | |
64 | + println!(" reverted ec : {:?} {}", ec, Into::<Fractional>::into(&ec)); | |
65 | +} | |
66 | + | |
67 | +fn sqrt() { | |
68 | + let f = Fractional(-9, 4); | |
69 | + let fr :f64 = f.try_into().unwrap(); | |
70 | + let sq = f.sqrt(); | |
71 | + let _sq = f64::sqrt(fr); | |
72 | + | |
73 | + println!("{:>14} : {:?} / {}", format!("sqrt {}", f), sq, _sq); | |
74 | + | |
75 | + for f in [ Fractional(9, 4) | |
76 | + , Fractional(45, 16) | |
77 | + , Fractional(16, 45) | |
78 | + , Fractional(9, 3) ].iter() { | |
79 | + let fr :f64 = (*f).try_into().unwrap(); | |
80 | + let sq = f.sqrt().unwrap(); | |
81 | + let sqr :f64 = sq.try_into().unwrap(); | |
82 | + let _sqr = f64::sqrt(fr); | |
83 | + | |
84 | + println!("{:>14} : {} {} / {}", format!("sqrt {}", f), sq, sqr, _sqr); | |
85 | + } | |
86 | +} | |
87 | + | |
88 | +fn pi() { | |
89 | + let pir :f64 = PI.try_into().unwrap(); | |
90 | + let pit :(i32, i32) = PI.try_into().unwrap(); | |
91 | + let pi2r :f64 = (PI * PI).try_into().unwrap(); | |
92 | + | |
61 | 93 | println!(" Rust π : {}" , FPI); |
62 | - println!(" π : {} {}" , TryInto::<f64>::try_into(PI).unwrap(), PI); | |
63 | - println!(" π as tuple : {:?}" , TryInto::<(i32, i32)>::try_into(PI).unwrap()); | |
94 | + println!(" π : {} {}" , PI, pir); | |
95 | + println!(" π as tuple : {:?}" , pit); | |
64 | 96 | println!(" Rust π² : {}" , FPI * FPI); |
65 | - println!(" π² : {} {}" , TryInto::<f64>::try_into(PI * PI).unwrap(), PI * PI); | |
66 | - println!(" sin 9 : {}" , sin(9)); | |
67 | - println!(" sin 9 : {}" , TryInto::<f64>::try_into(sin(9)).unwrap()); | |
68 | - println!(" Rust sin 9 : {}" , f64::sin(9.0 * FPI / 180.0)); | |
69 | - println!(" sin 17 : {}" , sin(17)); | |
70 | - println!(" sin 17 : {}" , TryInto::<f64>::try_into(sin(17)).unwrap()); | |
71 | - println!(" Rust sin 17 : {}" , f64::sin(17.0 * FPI / 180.0)); | |
72 | - println!(" sin 31 : {}" , sin(31)); | |
73 | - println!(" sin 31 : {}" , TryInto::<f64>::try_into(sin(31)).unwrap()); | |
74 | - println!(" Rust sin 31 : {}" , f64::sin(31.0 * FPI / 180.0)); | |
75 | - println!(" sin 45 : {}" , sin(45)); | |
76 | - println!(" sin 45 : {}" , TryInto::<f64>::try_into(sin(45)).unwrap()); | |
77 | - println!(" Rust sin 45 : {}" , f64::sin(45.0 * FPI / 180.0)); | |
78 | - println!(" sin 73 : {}" , sin(73)); | |
79 | - println!(" sin 73 : {}" , TryInto::<f64>::try_into(sin(73)).unwrap()); | |
80 | - println!(" Rust sin 73 : {}" , f64::sin(73.0 * FPI / 180.0)); | |
81 | - println!(" sin 123 : {}" , sin(123)); | |
82 | - println!(" sin 123 : {}" , TryInto::<f64>::try_into(sin(123)).unwrap()); | |
83 | - println!(" Rust sin 123 : {}" , f64::sin(123.0 * FPI / 180.0)); | |
84 | - println!(" sin 213 : {}" , sin(213)); | |
85 | - println!(" sin 213 : {}" , TryInto::<f64>::try_into(sin(213)).unwrap()); | |
86 | - println!(" Rust sin 213 : {}" , f64::sin(213.0 * FPI / 180.0)); | |
87 | - println!(" sin 312 : {}" , sin(312)); | |
88 | - println!(" sin 312 : {}" , TryInto::<f64>::try_into(sin(312)).unwrap()); | |
89 | - println!(" Rust sin 312 : {}" , f64::sin(312.0 * FPI / 180.0)); | |
90 | - println!(" sin 876 : {}" , sin(876)); | |
91 | - println!(" sin 876 : {}" , TryInto::<f64>::try_into(sin(876)).unwrap()); | |
92 | - println!(" Rust sin 876 : {}" , f64::sin(876.0 * FPI / 180.0)); | |
93 | - println!(" cos 9 : {}" , cos(9)); | |
94 | - println!(" cos 9 : {}" , TryInto::<f64>::try_into(cos(9)).unwrap()); | |
95 | - println!(" Rust cos 9 : {}" , f64::cos(9.0 * FPI / 180.0)); | |
96 | - println!(" cos 17 : {}" , cos(17)); | |
97 | - println!(" cos 17 : {}" , TryInto::<f64>::try_into(cos(17)).unwrap()); | |
98 | - println!(" Rust cos 17 : {}" , f64::cos(17.0 * FPI / 180.0)); | |
99 | - println!(" cos 31 : {}" , cos(31)); | |
100 | - println!(" cos 31 : {}" , TryInto::<f64>::try_into(cos(31)).unwrap()); | |
101 | - println!(" Rust cos 31 : {}" , f64::cos(31.0 * FPI / 180.0)); | |
102 | - println!(" cos 45 : {}" , cos(45)); | |
103 | - println!(" cos 45 : {}" , TryInto::<f64>::try_into(cos(45)).unwrap()); | |
104 | - println!(" Rust cos 45 : {}" , f64::cos(45.0 * FPI / 180.0)); | |
105 | - println!(" cos 73 : {}" , cos(73)); | |
106 | - println!(" cos 73 : {}" , TryInto::<f64>::try_into(cos(73)).unwrap()); | |
107 | - println!(" Rust cos 73 : {}" , f64::cos(73.0 * FPI / 180.0)); | |
108 | - println!(" cos 123 : {}" , cos(123)); | |
109 | - println!(" cos 123 : {}" , TryInto::<f64>::try_into(cos(123)).unwrap()); | |
110 | - println!(" Rust cos 123 : {}" , f64::cos(123.0 * FPI / 180.0)); | |
111 | - println!(" cos 213 : {}" , cos(213)); | |
112 | - println!(" cos 213 : {}" , TryInto::<f64>::try_into(cos(213)).unwrap()); | |
113 | - println!(" Rust cos 213 : {}" , f64::cos(213.0 * FPI / 180.0)); | |
114 | - println!(" cos 312 : {}" , cos(312)); | |
115 | - println!(" cos 312 : {}" , TryInto::<f64>::try_into(cos(312)).unwrap()); | |
116 | - println!(" Rust cos 312 : {}" , f64::cos(312.0 * FPI / 180.0)); | |
117 | - println!(" cos 876 : {}" , cos(876)); | |
118 | - println!(" cos 876 : {}" , TryInto::<f64>::try_into(cos(876)).unwrap()); | |
119 | - println!(" Rust cos 876 : {}" , f64::cos(876.0 * FPI / 180.0)); | |
97 | + println!(" π² : {} {}" , PI * PI, pi2r); | |
98 | +} | |
99 | + | |
100 | +fn _sin() { | |
101 | + for d in [9, 17, 31, 45, 73, 123, 213, 312, 876].iter() { | |
102 | + let s = sin(*d as i32); | |
103 | + let sr :f64 = s.try_into().unwrap(); | |
104 | + let _s = f64::sin(*d as f64 * FPI / 180.0); | |
105 | + | |
106 | + println!("{:>14} : {} {} / {}", format!("sin {}", d), s, sr, _s); | |
107 | + } | |
108 | +} | |
109 | + | |
110 | +fn _cos() { | |
111 | + for d in [9, 17, 31, 45, 73, 123, 213, 312, 876].iter() { | |
112 | + let s = cos(*d as i32); | |
113 | + let sr :f64 = s.try_into().unwrap(); | |
114 | + let _s = f64::cos(*d as f64 * FPI / 180.0); | |
115 | + | |
116 | + println!("{:>14} : {} {} / {}", format!("cos {}", d), s, sr, _s); | |
117 | + } | |
118 | +} | |
119 | + | |
120 | +fn main() { | |
121 | + common_fractional(); | |
122 | + println!(); | |
123 | + continuous(); | |
124 | + println!(); | |
125 | + sqrt(); | |
126 | + println!(); | |
127 | + pi(); | |
128 | + println!(); | |
129 | + _sin(); | |
130 | + println!(); | |
131 | + _cos(); | |
120 | 132 | } | ... | ... |
1 | 1 | // |
2 | -// Test our fractional crate / module... | |
2 | +// Some trigonometic functions with Fractions results. | |
3 | +// Currently only sin and cos are implemented. As I was unable | |
4 | +// to find a really good integral approximation for them I | |
5 | +// implement them as tables which are predefined using the | |
6 | +// floating point function f64::sin and then transformed into | |
7 | +// a fraction of a given PRECISION. | |
3 | 8 | // |
4 | 9 | // Georg Hopp <georg@steffers.org> |
5 | 10 | // | ... | ... |
Please
register
or
login
to post a comment