Polynomial.hx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. package h2d.col;
  2. private class Matrix {
  3. public var data : haxe.ds.Vector<haxe.ds.Vector<Float>>;
  4. public var m : Int;
  5. public var n : Int;
  6. public function new(m, n) {
  7. this.m = m;
  8. this.n = n;
  9. this.data = new haxe.ds.Vector(m);
  10. for( i in 0...m )
  11. data[i] = new haxe.ds.Vector(n);
  12. }
  13. public function clone() {
  14. var m2 = new Matrix(m, n);
  15. for( i in 0...m )
  16. for( j in 0...n )
  17. m2.data[i][j] = data[i][j];
  18. return m2;
  19. }
  20. function toString() {
  21. return "[" + [for( k in data ) "\n" + Std.string(k)].join("") + "\n]";
  22. }
  23. }
  24. private class QR {
  25. var qr : haxe.ds.Vector<haxe.ds.Vector<Float>>;
  26. var rDiag : haxe.ds.Vector<Float>;
  27. var m : Int;
  28. var n : Int;
  29. public function new( mat : Matrix ) {
  30. this.m = mat.m;
  31. this.n = mat.n;
  32. qr = mat.clone().data;
  33. rDiag = new haxe.ds.Vector(n);
  34. for( k in 0...n ) {
  35. var nrm = 0.;
  36. for( i in k...m )
  37. nrm = hypot(nrm, qr[i][k]);
  38. if( nrm != 0 ) {
  39. if( qr[k][k] < 0 ) nrm = -nrm;
  40. for( i in k...m )
  41. qr[i][k] /= nrm;
  42. qr[k][k] += 1.0;
  43. for( j in k + 1...n ) {
  44. var s = 0.;
  45. for( i in k...m )
  46. s += qr[i][k] * qr[i][j];
  47. s = -s / qr[k][k];
  48. for( i in k...m )
  49. qr[i][j] += s * qr[i][k];
  50. }
  51. }
  52. rDiag[k] = -nrm;
  53. }
  54. }
  55. function isFullRank() {
  56. for( j in 0...n )
  57. if( rDiag[j] == 0 )
  58. return false;
  59. return true;
  60. }
  61. public function solve( b : Matrix ) {
  62. if( b.m != m ) throw "Invalid matrix size";
  63. if( !isFullRank() ) return null;
  64. var nx = b.n;
  65. var X = b.clone().data;
  66. for( k in 0...n )
  67. for( j in 0...nx ) {
  68. var s = 0.;
  69. for( i in k...m )
  70. s += qr[i][k] * X[i][j];
  71. s = -s / qr[k][k];
  72. for( i in k...m )
  73. X[i][j] += s * qr[i][k];
  74. }
  75. var k = n - 1;
  76. while( k >= 0 ) {
  77. for( j in 0...nx )
  78. X[k][j] /= rDiag[k];
  79. for( i in 0...k )
  80. for( j in 0...nx )
  81. X[i][j] -= X[k][j] * qr[i][k];
  82. k--;
  83. }
  84. var beta = new Array();
  85. for( i in 0...n )
  86. beta.push(X[i][0]);
  87. return beta;
  88. }
  89. function hypot( x : Float, y : Float ) {
  90. if( x < 0 ) x = -x;
  91. if( y < 0 ) y = -y;
  92. var t;
  93. if( x < y ) {
  94. t = x;
  95. x = y;
  96. } else
  97. t = y;
  98. t = t/x;
  99. return x * Math.sqrt(1+t*t);
  100. }
  101. }
  102. class Polynomial {
  103. /**
  104. Calculate the best fit curve of given degree that match the input values. Returns the polynomial exponents. For instance [2,8,-5] will represent 2 + 8 x - 5 x^2
  105. **/
  106. public static function regress( xVals : Array<Float>, yVals : Array<Float>, degree : Int ) : Array<Float> {
  107. var n = xVals.length;
  108. // fix bug with 4 values
  109. if( n == 4 ) {
  110. xVals.unshift(xVals[0]);
  111. yVals.unshift(yVals[0]);
  112. n++;
  113. }
  114. var x = new Matrix(n, degree + 1);
  115. for( i in 0...n )
  116. for( j in 0...degree+1 )
  117. x.data[i][j] = Math.pow(xVals[i], j);
  118. var y = new Matrix(yVals.length, n);
  119. for( i in 0...yVals.length )
  120. y.data[i][0] = yVals[i];
  121. var qr = new QR(x);
  122. var beta = qr.solve(y);
  123. if( beta == null ) {
  124. beta = regress(xVals, yVals, degree-1);
  125. beta.push(0);
  126. }
  127. return beta;
  128. }
  129. }