summaryrefslogtreecommitdiff
path: root/appl/math/fit.b
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /appl/math/fit.b
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'appl/math/fit.b')
-rw-r--r--appl/math/fit.b264
1 files changed, 264 insertions, 0 deletions
diff --git a/appl/math/fit.b b/appl/math/fit.b
new file mode 100644
index 00000000..b512c13b
--- /dev/null
+++ b/appl/math/fit.b
@@ -0,0 +1,264 @@
+# fit a polynomial to a set of points
+# fit -dn [-v]
+# where n is the degree of the polynomial
+
+implement Fit;
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "math.m";
+ maths: Math;
+include "bufio.m";
+ bufio: Bufio;
+include "arg.m";
+
+Fit: module
+{
+ init: fn(nil: ref Draw->Context, argv: list of string);
+};
+
+MAXPTS: con 512;
+MAXDEG: con 16;
+EPS: con 0.0000005;
+
+init(nil: ref Draw->Context, argv: list of string)
+{
+ sys = load Sys Sys->PATH;
+ maths = load Math Math->PATH;
+ if(maths == nil)
+ fatal(sys->sprint("cannot load maths library"));
+ bufio = load Bufio Bufio->PATH;
+ if(bufio == nil)
+ fatal(sys->sprint("cannot load bufio"));
+ main(argv);
+}
+
+isn(r: real, n: int): int
+{
+ s := r - real n;
+ if(s < 0.0)
+ s = -s;
+ return s < EPS;
+}
+
+fact(n: int): real
+{
+ f := 1.0;
+ for(i := 1; i <= n; i++)
+ f *= real i;
+ return f;
+}
+
+comb(n: int, r: int): real
+{
+ f := 1.0;
+ for(i := 0; i < r; i++)
+ f *= real (n-i);
+ return f/fact(r);
+}
+
+matalloc(n: int): array of array of real
+{
+ mat := array[n] of array of real;
+ for(i := 0; i < n; i++)
+ mat[i] = array[n] of real;
+ return mat;
+}
+
+matsalloc(n: int): array of array of array of real
+{
+ mats := array[n+1] of array of array of real;
+ for(i := 0; i <= n; i++)
+ mats[i] = matalloc(i);
+ return mats;
+}
+
+det(mat: array of array of real, n: int, mats: array of array of array of real): real
+{
+ # easy cases first
+ if(n == 0)
+ return 1.0;
+ if(n == 1)
+ return mat[0][0];
+ if(n == 2)
+ return mat[0][0]*mat[1][1]-mat[0][1]*mat[1][0];
+ d := 0.0;
+ s := 1;
+ m := mats[n-1];
+ for(k := 0; k < n; k++){
+ for(i := 0; i < n-1; i++){
+ for(j := 0; j < n-1; j++){
+ if(j < k)
+ m[i][j] = mat[i+1][j];
+ else
+ m[i][j] = mat[i+1][j+1];
+ }
+ }
+ d += (real s)*mat[0][k]*det(m, n-1, mats);
+ s = -s;
+ }
+ return d;
+}
+
+main(argv: list of string)
+{
+ i, j: int;
+ x, y, z: real;
+ fb: ref Bufio->Iobuf;
+
+ n := 0;
+ p := 1;
+ arg := load Arg Arg->PATH;
+ if(arg == nil)
+ fatal(sys->sprint("cannot load %s: %r", Arg->PATH));
+ arg->init(argv);
+ verbose := 0;
+ while((o := arg->opt()) != 0)
+ case o{
+ 'd' =>
+ p = int arg->arg();
+ 'v' =>
+ verbose = 1;
+ * =>
+ fatal(sys->sprint("bad option %c", o));
+ }
+ args := arg->argv();
+ arg = nil;
+ if(args != nil){
+ s := hd args;
+ fb = bufio->open(s, bufio->OREAD);
+ if(fb == nil)
+ fatal(sys->sprint("cannot open %s", s));
+ }
+ else{
+ fb = bufio->open("/dev/cons", bufio->OREAD);
+ if(fb == nil)
+ fatal(sys->sprint("missing data file name"));
+ }
+ a := array[p+1] of real;
+ b := array[p+1] of real;
+ sx := array[2*p+1] of real;
+ sxy := array[p+1] of real;
+ xd := array[MAXPTS] of real;
+ yd := array[MAXPTS] of real;
+ while(1){
+ xs := ss(bufio->fb.gett(" \t\r\n"));
+ if(xs == nil)
+ break;
+ ys := ss(bufio->fb.gett(" \t\r\n"));
+ if(ys == nil)
+ fatal(sys->sprint("missing value"));
+ if(n >= MAXPTS)
+ fatal(sys->sprint("too many points"));
+ xd[n] = real xs;
+ yd[n] = real ys;
+ n++;
+ }
+ if(p < 0)
+ fatal(sys->sprint("negative power"));
+ if(p > MAXDEG)
+ fatal(sys->sprint("power too large"));
+ if(n < p+1)
+ fatal(sys->sprint("not enough points"));
+ # use x-xbar, y-ybar to avoid overflow
+ for(i = 0; i <= p; i++)
+ sxy[i] = 0.0;
+ for(i = 0; i <= 2*p; i++)
+ sx[i] = 0.0;
+ xbar := ybar := 0.0;
+ for(i = 0; i < n; i++){
+ xbar += xd[i];
+ ybar += yd[i];
+ }
+ xbar = xbar/(real n);
+ ybar = ybar/(real n);
+ for(i = 0; i < n; i++){
+ x = xd[i]-xbar;
+ y = yd[i]-ybar;
+ for(j = 0; j <= p; j++)
+ sxy[j] += y*x**j;
+ for(j = 0; j <= 2*p; j++)
+ sx[j] += x**j;
+ }
+ mats := matsalloc(p+1);
+ mat := mats[p+1];
+ for(i = 0; i <= p; i++)
+ for(j = 0; j <= p; j++)
+ mat[i][j] = sx[i+j];
+ d := det(mat, p+1, mats);
+ if(isn(d, 0))
+ fatal(sys->sprint("points not independent"));
+ for(j = 0; j <= p; j++){
+ for(i = 0; i <= p; i++)
+ mat[i][j] = sxy[i];
+ a[j] = det(mat, p+1, mats)/d;
+ for(i = 0; i <= p; i++)
+ mat[i][j] = sx[i+j];
+ }
+ if(verbose)
+ sys->print("\npt actual x actual y predicted y\n");
+ e := 0.0;
+ for(i = 0; i < n; i++){
+ x = xd[i]-xbar;
+ y = yd[i]-ybar;
+ z = 0.0;
+ for(j = 0; j <= p; j++)
+ z += a[j]*x**j;
+ z += ybar;
+ e += (z-yd[i])*(z-yd[i]);
+ if(verbose)
+ sys->print("%d. %f %f %f\n", i+1, xd[i], yd[i], z);
+ }
+ if(verbose)
+ sys->print("root mean squared error = %f\n", maths->sqrt(e/(real n)));
+ for(i = 0; i <= p; i++)
+ b[i] = 0.0;
+ b[0] += ybar;
+ for(i = 0; i <= p; i++)
+ for(j = 0; j <= i; j++)
+ b[j] += a[i]*comb(i, j)*(-xbar)**(i-j);
+ pr := 0;
+ sys->print("y = ");
+ for(i = p; i >= 0; i--){
+ if(!isn(b[i], 0) || (i == 0 && pr == 0)){
+ if(b[i] < 0.0){
+ sys->print("-");
+ b[i] = -b[i];
+ }
+ else if(pr)
+ sys->print("+");
+ pr = 1;
+ if(i == 0)
+ sys->print("%f", b[i]);
+ else{
+ if(!isn(b[i], 1))
+ sys->print("%f*", b[i]);
+ sys->print("x");
+ if(i > 1)
+ sys->print("^%d", i);
+ }
+ }
+ }
+ sys->print("\n");
+}
+
+ss(s: string): string
+{
+ l := len s;
+ while(l > 0 && (s[0] == ' ' || s[0] == '\t' || s[0] == '\r' || s[0] == '\n')){
+ s = s[1: ];
+ l--;
+ }
+ while(l > 0 && (s[l-1] == ' ' || s[l-1] == '\t' || s[l-1] == '\r' || s[l-1] == '\n')){
+ s = s[0: l-1];
+ l--;
+ }
+ return s;
+}
+
+fatal(s: string)
+{
+ sys->fprint(sys->fildes(2), "fit: %s\n", s);
+ exit;
+}