Commit 15ea57a24b3e05b75d9e6250bdefc6000807162b

Authored by Georg Hopp
1 parent 5cd265a6

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

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