annotate maths/Polyfit.h @ 473:19be28a3350b

Another style note
author Chris Cannam <cannam@all-day-breakfast.com>
date Thu, 30 May 2019 18:26:11 +0100
parents 7b7691b49197
children fa407c1d9923
rev   line source
c@241 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
c@241 2 //---------------------------------------------------------------------------
c@241 3
c@241 4 #ifndef PolyfitHPP
c@241 5 #define PolyfitHPP
c@241 6 //---------------------------------------------------------------------------
c@241 7 // Least-squares curve fitting class for arbitrary data types
c@241 8 /*
c@241 9
c@241 10 { ******************************************
c@241 11 **** Scientific Subroutine Library ****
c@241 12 **** for C++ Builder ****
c@241 13 ******************************************
c@241 14
c@241 15 The following programs were written by Allen Miller and appear in the
c@241 16 book entitled "Pascal Programs For Scientists And Engineers" which is
c@241 17 published by Sybex, 1981.
c@241 18 They were originally typed and submitted to MTPUG in Oct. 1982
c@241 19 Juergen Loewner
c@241 20 Hoher Heckenweg 3
c@241 21 D-4400 Muenster
c@241 22 They have had minor corrections and adaptations for Turbo Pascal by
c@241 23 Jeff Weiss
c@241 24 1572 Peacock Ave.
c@241 25 Sunnyvale, CA 94087.
c@241 26
c@241 27
c@241 28 2000 Oct 28 Updated for Delphi 4, open array parameters.
c@241 29 This allows the routine to be generalised so that it is no longer
c@241 30 hard-coded to make a specific order of best fit or work with a
c@241 31 specific number of points.
c@241 32 2001 Jan 07 Update Web site address
c@241 33
c@241 34 Copyright © David J Taylor, Edinburgh and others listed above
c@241 35 Web site: www.satsignal.net
c@241 36 E-mail: davidtaylor@writeme.com
c@241 37 }*/
c@241 38
c@241 39 ///////////////////////////////////////////////////////////////////////////////
c@241 40 // Modified by CLandone for VC6 Aug 2004
c@241 41 ///////////////////////////////////////////////////////////////////////////////
c@241 42
c@268 43 #include <iostream>
c@241 44
c@241 45 using std::vector;
c@241 46
c@241 47 class TPolyFit
c@241 48 {
c@241 49 typedef vector<vector<double> > Matrix;
c@241 50 public:
c@241 51
c@241 52 static double PolyFit2 (const vector<double> &x, // does the work
c@241 53 const vector<double> &y,
c@241 54 vector<double> &coef);
c@241 55
c@241 56
c@241 57 private:
c@241 58 TPolyFit &operator = (const TPolyFit &); // disable assignment
c@241 59 TPolyFit(); // and instantiation
c@241 60 TPolyFit(const TPolyFit&); // and copying
c@241 61
c@241 62
c@241 63 static void Square (const Matrix &x, // Matrix multiplication routine
c@241 64 const vector<double> &y,
c@241 65 Matrix &a, // A = transpose X times X
c@241 66 vector<double> &g, // G = Y times X
c@241 67 const int nrow, const int ncol);
c@241 68 // Forms square coefficient matrix
c@241 69
c@241 70 static bool GaussJordan (Matrix &b, // square matrix of coefficients
c@241 71 const vector<double> &y, // constant vector
c@241 72 vector<double> &coef); // solution vector
c@241 73 // returns false if matrix singular
c@241 74
c@241 75 static bool GaussJordan2(Matrix &b,
c@241 76 const vector<double> &y,
c@241 77 Matrix &w,
c@241 78 vector<vector<int> > &index);
c@241 79 };
c@241 80
c@241 81 // some utility functions
c@241 82
c@421 83 struct NSUtility
c@241 84 {
c@421 85 static void swap(double &a, double &b) {double t = a; a = b; b = t;}
c@421 86 // fills a vector with zeros.
c@421 87 static void zeroise(vector<double> &array, int n) {
c@421 88 array.clear();
c@421 89 for(int j = 0; j < n; ++j) array.push_back(0);
c@421 90 }
c@421 91 // fills a vector with zeros.
c@421 92 static void zeroise(vector<int> &array, int n) {
c@421 93 array.clear();
c@421 94 for(int j = 0; j < n; ++j) array.push_back(0);
c@421 95 }
c@421 96 // fills a (m by n) matrix with zeros.
c@421 97 static void zeroise(vector<vector<double> > &matrix, int m, int n) {
c@421 98 vector<double> zero;
c@421 99 zeroise(zero, n);
c@421 100 matrix.clear();
c@421 101 for(int j = 0; j < m; ++j) matrix.push_back(zero);
c@421 102 }
c@421 103 // fills a (m by n) matrix with zeros.
c@421 104 static void zeroise(vector<vector<int> > &matrix, int m, int n) {
c@421 105 vector<int> zero;
c@421 106 zeroise(zero, n);
c@421 107 matrix.clear();
c@421 108 for(int j = 0; j < m; ++j) matrix.push_back(zero);
c@421 109 }
c@421 110 static double sqr(const double &x) {return x * x;}
c@241 111 };
c@241 112
c@241 113
c@241 114 // main PolyFit routine
c@241 115
c@241 116 double TPolyFit::PolyFit2 (const vector<double> &x,
c@241 117 const vector<double> &y,
c@241 118 vector<double> &coefs)
c@241 119 // nterms = coefs.size()
c@241 120 // npoints = x.size()
c@241 121 {
c@241 122 int i, j;
c@241 123 double xi, yi, yc, srs, sum_y, sum_y2;
c@241 124 Matrix xmatr; // Data matrix
c@241 125 Matrix a;
c@241 126 vector<double> g; // Constant vector
c@241 127 const int npoints(x.size());
c@241 128 const int nterms(coefs.size());
c@241 129 double correl_coef;
c@421 130 NSUtility::zeroise(g, nterms);
c@421 131 NSUtility::zeroise(a, nterms, nterms);
c@421 132 NSUtility::zeroise(xmatr, npoints, nterms);
c@268 133 if (nterms < 1) {
c@268 134 std::cerr << "ERROR: PolyFit called with less than one term" << std::endl;
c@268 135 return 0;
c@268 136 }
c@268 137 if(npoints < 2) {
c@268 138 std::cerr << "ERROR: PolyFit called with less than two points" << std::endl;
c@268 139 return 0;
c@268 140 }
c@325 141 if(npoints != (int)y.size()) {
c@268 142 std::cerr << "ERROR: PolyFit called with x and y of unequal size" << std::endl;
c@268 143 return 0;
c@268 144 }
c@241 145 for(i = 0; i < npoints; ++i)
c@241 146 {
c@241 147 // { setup x matrix }
c@241 148 xi = x[i];
c@241 149 xmatr[i][0] = 1.0; // { first column }
c@241 150 for(j = 1; j < nterms; ++j)
c@241 151 xmatr[i][j] = xmatr [i][j - 1] * xi;
c@241 152 }
c@241 153 Square (xmatr, y, a, g, npoints, nterms);
c@241 154 if(!GaussJordan (a, g, coefs))
c@241 155 return -1;
c@241 156 sum_y = 0.0;
c@241 157 sum_y2 = 0.0;
c@241 158 srs = 0.0;
c@241 159 for(i = 0; i < npoints; ++i)
c@241 160 {
c@241 161 yi = y[i];
c@241 162 yc = 0.0;
c@241 163 for(j = 0; j < nterms; ++j)
c@241 164 yc += coefs [j] * xmatr [i][j];
c@421 165 srs += NSUtility::sqr (yc - yi);
c@241 166 sum_y += yi;
c@241 167 sum_y2 += yi * yi;
c@241 168 }
c@241 169
c@241 170 // If all Y values are the same, avoid dividing by zero
c@421 171 correl_coef = sum_y2 - NSUtility::sqr (sum_y) / npoints;
c@241 172 // Either return 0 or the correct value of correlation coefficient
c@241 173 if (correl_coef != 0)
c@241 174 correl_coef = srs / correl_coef;
c@241 175 if (correl_coef >= 1)
c@241 176 correl_coef = 0.0;
c@241 177 else
c@241 178 correl_coef = sqrt (1.0 - correl_coef);
c@241 179 return correl_coef;
c@241 180 }
c@241 181
c@241 182
c@241 183 //------------------------------------------------------------------------
c@241 184
c@241 185 // Matrix multiplication routine
c@241 186 // A = transpose X times X
c@241 187 // G = Y times X
c@241 188
c@241 189 // Form square coefficient matrix
c@241 190
c@241 191 void TPolyFit::Square (const Matrix &x,
c@241 192 const vector<double> &y,
c@241 193 Matrix &a,
c@241 194 vector<double> &g,
c@241 195 const int nrow,
c@241 196 const int ncol)
c@241 197 {
c@241 198 int i, k, l;
c@241 199 for(k = 0; k < ncol; ++k)
c@241 200 {
c@241 201 for(l = 0; l < k + 1; ++l)
c@241 202 {
c@241 203 a [k][l] = 0.0;
c@241 204 for(i = 0; i < nrow; ++i)
c@241 205 {
c@241 206 a[k][l] += x[i][l] * x [i][k];
c@241 207 if(k != l)
c@241 208 a[l][k] = a[k][l];
c@241 209 }
c@241 210 }
c@241 211 g[k] = 0.0;
c@241 212 for(i = 0; i < nrow; ++i)
c@241 213 g[k] += y[i] * x[i][k];
c@241 214 }
c@241 215 }
c@241 216 //---------------------------------------------------------------------------------
c@241 217
c@241 218
c@241 219 bool TPolyFit::GaussJordan (Matrix &b,
c@241 220 const vector<double> &y,
c@241 221 vector<double> &coef)
c@241 222 //b square matrix of coefficients
c@241 223 //y constant vector
c@241 224 //coef solution vector
c@241 225 //ncol order of matrix got from b.size()
c@241 226
c@241 227
c@241 228 {
c@241 229 /*
c@241 230 { Gauss Jordan matrix inversion and solution }
c@241 231
c@241 232 { B (n, n) coefficient matrix becomes inverse }
c@241 233 { Y (n) original constant vector }
c@241 234 { W (n, m) constant vector(s) become solution vector }
c@241 235 { DETERM is the determinant }
c@241 236 { ERROR = 1 if singular }
c@241 237 { INDEX (n, 3) }
c@241 238 { NV is number of constant vectors }
c@241 239 */
c@241 240
c@241 241 int ncol(b.size());
c@241 242 int irow, icol;
c@241 243 vector<vector<int> >index;
c@241 244 Matrix w;
c@241 245
c@421 246 NSUtility::zeroise(w, ncol, ncol);
c@421 247 NSUtility::zeroise(index, ncol, 3);
c@241 248
c@241 249 if(!GaussJordan2(b, y, w, index))
c@241 250 return false;
c@241 251
c@241 252 // Interchange columns
c@241 253 int m;
c@241 254 for (int i = 0; i < ncol; ++i)
c@241 255 {
c@241 256 m = ncol - i - 1;
c@241 257 if(index [m][0] != index [m][1])
c@241 258 {
c@241 259 irow = index [m][0];
c@241 260 icol = index [m][1];
c@241 261 for(int k = 0; k < ncol; ++k)
c@241 262 swap (b[k][irow], b[k][icol]);
c@241 263 }
c@241 264 }
c@241 265
c@241 266 for(int k = 0; k < ncol; ++k)
c@241 267 {
c@241 268 if(index [k][2] != 0)
c@241 269 {
c@268 270 std::cerr << "ERROR: Error in PolyFit::GaussJordan: matrix is singular" << std::endl;
c@268 271 return false;
c@241 272 }
c@241 273 }
c@241 274
c@241 275 for( int i = 0; i < ncol; ++i)
c@241 276 coef[i] = w[i][0];
c@241 277
c@241 278
c@241 279 return true;
c@241 280 } // end; { procedure GaussJordan }
c@241 281 //----------------------------------------------------------------------------------------------
c@241 282
c@241 283
c@241 284 bool TPolyFit::GaussJordan2(Matrix &b,
c@241 285 const vector<double> &y,
c@241 286 Matrix &w,
c@241 287 vector<vector<int> > &index)
c@241 288 {
c@241 289 //GaussJordan2; // first half of GaussJordan
c@241 290 // actual start of gaussj
c@241 291
c@241 292 double big, t;
c@241 293 double pivot;
c@241 294 double determ;
c@429 295 int irow = 0, icol = 0;
c@241 296 int ncol(b.size());
c@241 297 int nv = 1; // single constant vector
c@241 298 for(int i = 0; i < ncol; ++i)
c@241 299 {
c@241 300 w[i][0] = y[i]; // copy constant vector
c@241 301 index[i][2] = -1;
c@241 302 }
c@241 303 determ = 1.0;
c@241 304 for(int i = 0; i < ncol; ++i)
c@241 305 {
c@241 306 // Search for largest element
c@241 307 big = 0.0;
c@241 308 for(int j = 0; j < ncol; ++j)
c@241 309 {
c@241 310 if(index[j][2] != 0)
c@241 311 {
c@241 312 for(int k = 0; k < ncol; ++k)
c@241 313 {
c@268 314 if(index[k][2] > 0) {
c@268 315 std::cerr << "ERROR: Error in PolyFit::GaussJordan2: matrix is singular" << std::endl;
c@268 316 return false;
c@268 317 }
c@241 318
c@241 319 if(index[k][2] < 0 && fabs(b[j][k]) > big)
c@241 320 {
c@241 321 irow = j;
c@241 322 icol = k;
c@241 323 big = fabs(b[j][k]);
c@241 324 }
c@241 325 } // { k-loop }
c@241 326 }
c@241 327 } // { j-loop }
c@241 328 index [icol][2] = index [icol][2] + 1;
c@241 329 index [i][0] = irow;
c@241 330 index [i][1] = icol;
c@241 331
c@241 332 // Interchange rows to put pivot on diagonal
c@241 333 // GJ3
c@241 334 if(irow != icol)
c@241 335 {
c@241 336 determ = -determ;
c@241 337 for(int m = 0; m < ncol; ++m)
c@241 338 swap (b [irow][m], b[icol][m]);
c@241 339 if (nv > 0)
c@241 340 for (int m = 0; m < nv; ++m)
c@241 341 swap (w[irow][m], w[icol][m]);
c@241 342 } // end GJ3
c@241 343
c@241 344 // divide pivot row by pivot column
c@241 345 pivot = b[icol][icol];
c@241 346 determ *= pivot;
c@241 347 b[icol][icol] = 1.0;
c@241 348
c@241 349 for(int m = 0; m < ncol; ++m)
c@241 350 b[icol][m] /= pivot;
c@241 351 if(nv > 0)
c@241 352 for(int m = 0; m < nv; ++m)
c@241 353 w[icol][m] /= pivot;
c@241 354
c@241 355 // Reduce nonpivot rows
c@241 356 for(int n = 0; n < ncol; ++n)
c@241 357 {
c@241 358 if(n != icol)
c@241 359 {
c@241 360 t = b[n][icol];
c@241 361 b[n][icol] = 0.0;
c@241 362 for(int m = 0; m < ncol; ++m)
c@241 363 b[n][m] -= b[icol][m] * t;
c@241 364 if(nv > 0)
c@241 365 for(int m = 0; m < nv; ++m)
c@241 366 w[n][m] -= w[icol][m] * t;
c@241 367 }
c@241 368 }
c@241 369 } // { i-loop }
c@241 370 return true;
c@241 371 }
c@241 372
c@241 373 #endif
c@241 374