Commit 15ea57a24b3e05b75d9e6250bdefc6000807162b

Authored by Georg Hopp
1 parent 5cd265a6

Add continued fraction and use them to get a better sqrt approximation

1 // 1 //
2 // Some code to support fractional numbers for full precision rational number 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 // Georg Hopp <georg@steffers.org> 8 // Georg Hopp <georg@steffers.org>
9 // 9 //
@@ -31,6 +31,10 @@ use std::num::TryFromIntError; @@ -31,6 +31,10 @@ use std::num::TryFromIntError;
31 #[derive(Debug, Eq, Clone, Copy)] 31 #[derive(Debug, Eq, Clone, Copy)]
32 pub struct Fractional (pub i64, pub i64); 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 #[inline] 38 #[inline]
35 fn hcf(x :i64, y :i64) -> i64 { 39 fn hcf(x :i64, y :i64) -> i64 {
36 match y { 40 match y {
@@ -39,6 +43,38 @@ fn hcf(x :i64, y :i64) -> i64 { @@ -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 impl Fractional { 78 impl Fractional {
43 #[inline] 79 #[inline]
44 pub fn gcd(self, other: Self) -> i64 { 80 pub fn gcd(self, other: Self) -> i64 {
@@ -63,31 +99,54 @@ impl Fractional { @@ -63,31 +99,54 @@ impl Fractional {
63 self.1 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 // find the sqrt of x in O(log x/2). 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 if l > h { 120 if l > h {
71 - l - 1 121 + (&sqrt_cfrac(x, l - 1)).into()
72 } else { 122 } else {
73 let m = (l + h) / 2; 123 let m = (l + h) / 2;
74 match x.cmp(&(m * m)) { 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 Ordering::Greater => inner(m + 1, h, x), 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 let Fractional(n, d) = self; 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,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 impl PartialEq for Fractional { 232 impl PartialEq for Fractional {
143 fn eq(&self, other: &Self) -> bool { 233 fn eq(&self, other: &Self) -> bool {
144 let Fractional(n1, d1) = self; 234 let Fractional(n1, d1) = self;
@@ -18,11 +18,11 @@ @@ -18,11 +18,11 @@
18 // You should have received a copy of the GNU General Public License 18 // You should have received a copy of the GNU General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>. 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 use std::num::TryFromIntError; 22 use std::num::TryFromIntError;
23 use std::f64::consts::PI as FPI; 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 use fractional::trigonometry::{sin, cos, PI}; 26 use fractional::trigonometry::{sin, cos, PI};
27 27
28 struct Vector { 28 struct Vector {
@@ -37,84 +37,96 @@ fn mean(v: &Vec<i64>) -> Result<Fractional, TryFromIntError> { @@ -37,84 +37,96 @@ fn mean(v: &Vec<i64>) -> Result<Fractional, TryFromIntError> {
37 Ok(Fractional(r, l)) 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 println!(" Rust π : {}" , FPI); 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 println!(" Rust π² : {}" , FPI * FPI); 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 // Georg Hopp <georg@steffers.org> 9 // Georg Hopp <georg@steffers.org>
5 // 10 //
Please register or login to post a comment