You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

330 lines
8.9 KiB

  1. //
  2. // Lol Engine - Sample math program: Chebyshev polynomials
  3. //
  4. // Copyright: (c) 2005-2011 Sam Hocevar <sam@hocevar.net>
  5. // This program is free software; you can redistribute it and/or
  6. // modify it under the terms of the Do What The Fuck You Want To
  7. // Public License, Version 2, as published by Sam Hocevar. See
  8. // http://sam.zoy.org/projects/COPYING.WTFPL for more details.
  9. //
  10. #if !defined __REMEZ_SOLVER_H__
  11. #define __REMEZ_SOLVER_H__
  12. template<int ORDER> class RemezSolver
  13. {
  14. public:
  15. typedef real RealFunc(real const &x);
  16. RemezSolver()
  17. {
  18. }
  19. void Run(real a, real b, RealFunc *func, RealFunc *weight, int steps)
  20. {
  21. m_func = func;
  22. m_weight = weight;
  23. m_k1 = (b + a) >> 1;
  24. m_k2 = (b - a) >> 1;
  25. m_invk2 = re(m_k2);
  26. m_invk1 = -m_k1 * m_invk2;
  27. Init();
  28. PrintPoly();
  29. for (int n = 0; n < steps; n++)
  30. {
  31. FindExtrema();
  32. Step();
  33. PrintPoly();
  34. FindZeroes();
  35. }
  36. FindExtrema();
  37. Step();
  38. PrintPoly();
  39. }
  40. real ChebyEval(real const &x)
  41. {
  42. real ret = 0.0, xn = 1.0;
  43. for (int i = 0; i < ORDER + 1; i++)
  44. {
  45. real mul = 0;
  46. for (int j = 0; j < ORDER + 1; j++)
  47. mul += coeff[j] * (real)Cheby(j, i);
  48. ret += mul * xn;
  49. xn *= x;
  50. }
  51. return ret;
  52. }
  53. void Init()
  54. {
  55. /* Pick up x_i where error will be 0 and compute f(x_i) */
  56. real fxn[ORDER + 1];
  57. for (int i = 0; i < ORDER + 1; i++)
  58. {
  59. zeroes[i] = (real)(2 * i - ORDER) / (real)(ORDER + 1);
  60. fxn[i] = Value(zeroes[i]);
  61. }
  62. /* We build a matrix of Chebishev evaluations: row i contains the
  63. * evaluations of x_i for polynomial order n = 0, 1, ... */
  64. Matrix<ORDER + 1> mat;
  65. for (int i = 0; i < ORDER + 1; i++)
  66. {
  67. /* Compute the powers of x_i */
  68. real powers[ORDER + 1];
  69. powers[0] = 1.0;
  70. for (int n = 1; n < ORDER + 1; n++)
  71. powers[n] = powers[n - 1] * zeroes[i];
  72. /* Compute the Chebishev evaluations at x_i */
  73. for (int n = 0; n < ORDER + 1; n++)
  74. {
  75. real sum = 0.0;
  76. for (int k = 0; k < ORDER + 1; k++)
  77. sum += (real)Cheby(n, k) * powers[k];
  78. mat.m[i][n] = sum;
  79. }
  80. }
  81. /* Solve the system */
  82. mat = mat.inv();
  83. /* Compute interpolation coefficients */
  84. for (int j = 0; j < ORDER + 1; j++)
  85. {
  86. coeff[j] = 0;
  87. for (int i = 0; i < ORDER + 1; i++)
  88. coeff[j] += mat.m[j][i] * fxn[i];
  89. }
  90. }
  91. void FindZeroes()
  92. {
  93. /* Find ORDER + 1 zeroes of the error function. No need to
  94. * compute the relative error: its zeroes are at the same
  95. * place as the absolute error! */
  96. for (int i = 0; i < ORDER + 1; i++)
  97. {
  98. struct { real value, error; } left, right, mid;
  99. left.value = control[i];
  100. left.error = ChebyEval(left.value) - Value(left.value);
  101. right.value = control[i + 1];
  102. right.error = ChebyEval(right.value) - Value(right.value);
  103. static real limit = real::R_1 >> 500;
  104. while (fabs(left.value - right.value) > limit)
  105. {
  106. mid.value = (left.value + right.value) >> 1;
  107. mid.error = ChebyEval(mid.value) - Value(mid.value);
  108. if ((left.error < real::R_0 && mid.error < real::R_0)
  109. || (left.error > real::R_0 && mid.error > real::R_0))
  110. left = mid;
  111. else
  112. right = mid;
  113. }
  114. zeroes[i] = mid.value;
  115. }
  116. }
  117. void FindExtrema()
  118. {
  119. using std::printf;
  120. /* Find ORDER + 2 extrema of the error function. We need to
  121. * compute the relative error, since its extrema are at slightly
  122. * different locations than the absolute error’s. */
  123. real final = 0;
  124. for (int i = 0; i < ORDER + 2; i++)
  125. {
  126. real a = -1, b = 1;
  127. if (i > 0)
  128. a = zeroes[i - 1];
  129. if (i < ORDER + 1)
  130. b = zeroes[i];
  131. for (;;)
  132. {
  133. real c = a, delta = (b - a) >> 3;
  134. real maxerror = 0;
  135. real maxweight = 0;
  136. int best = -1;
  137. for (int k = 1; k <= 7; k++)
  138. {
  139. real error = ChebyEval(c) - Value(c);
  140. real weight = Weight(c);
  141. if (fabs(error * maxweight) >= fabs(maxerror * weight))
  142. {
  143. maxerror = error;
  144. maxweight = weight;
  145. best = k;
  146. }
  147. c += delta;
  148. }
  149. b = a + (real)(best + 1) * delta;
  150. a = a + (real)(best - 1) * delta;
  151. if (b - a < (real)1e-18)
  152. {
  153. real e = maxerror / maxweight;
  154. if (e > final)
  155. final = e;
  156. control[i] = (a + b) >> 1;
  157. break;
  158. }
  159. }
  160. }
  161. printf("Final error: ");
  162. final.print(40);
  163. }
  164. void Step()
  165. {
  166. /* Pick up x_i where error will be 0 and compute f(x_i) */
  167. real fxn[ORDER + 2];
  168. for (int i = 0; i < ORDER + 2; i++)
  169. fxn[i] = Value(control[i]);
  170. /* We build a matrix of Chebishev evaluations: row i contains the
  171. * evaluations of x_i for polynomial order n = 0, 1, ... */
  172. Matrix<ORDER + 2> mat;
  173. for (int i = 0; i < ORDER + 2; i++)
  174. {
  175. /* Compute the powers of x_i */
  176. real powers[ORDER + 1];
  177. powers[0] = 1.0;
  178. for (int n = 1; n < ORDER + 1; n++)
  179. powers[n] = powers[n - 1] * control[i];
  180. /* Compute the Chebishev evaluations at x_i */
  181. for (int n = 0; n < ORDER + 1; n++)
  182. {
  183. real sum = 0.0;
  184. for (int k = 0; k < ORDER + 1; k++)
  185. sum += (real)Cheby(n, k) * powers[k];
  186. mat.m[i][n] = sum;
  187. }
  188. if (i & 1)
  189. mat.m[i][ORDER + 1] = fabs(Weight(control[i]));
  190. else
  191. mat.m[i][ORDER + 1] = -fabs(Weight(control[i]));
  192. }
  193. /* Solve the system */
  194. mat = mat.inv();
  195. /* Compute interpolation coefficients */
  196. for (int j = 0; j < ORDER + 1; j++)
  197. {
  198. coeff[j] = 0;
  199. for (int i = 0; i < ORDER + 2; i++)
  200. coeff[j] += mat.m[j][i] * fxn[i];
  201. }
  202. /* Compute the error */
  203. real error = 0;
  204. for (int i = 0; i < ORDER + 2; i++)
  205. error += mat.m[ORDER + 1][i] * fxn[i];
  206. }
  207. int Cheby(int n, int k)
  208. {
  209. if (k > n || k < 0)
  210. return 0;
  211. if (n <= 1)
  212. return (n ^ k ^ 1) & 1;
  213. return 2 * Cheby(n - 1, k - 1) - Cheby(n - 2, k);
  214. }
  215. int Comb(int n, int k)
  216. {
  217. if (k == 0 || k == n)
  218. return 1;
  219. return Comb(n - 1, k - 1) + Comb(n - 1, k);
  220. }
  221. void PrintPoly()
  222. {
  223. using std::printf;
  224. /* Transform Chebyshev polynomial weights into powers of X^i
  225. * in the [-1..1] range. */
  226. real bn[ORDER + 1];
  227. for (int i = 0; i < ORDER + 1; i++)
  228. {
  229. bn[i] = 0;
  230. for (int j = 0; j < ORDER + 1; j++)
  231. bn[i] += coeff[j] * (real)Cheby(j, i);
  232. }
  233. /* Transform a polynomial in the [-1..1] range into a polynomial
  234. * in the [a..b] range. */
  235. real k1p[ORDER + 1], k2p[ORDER + 1];
  236. real an[ORDER + 1];
  237. for (int i = 0; i < ORDER + 1; i++)
  238. {
  239. k1p[i] = i ? k1p[i - 1] * m_invk1 : real::R_1;
  240. k2p[i] = i ? k2p[i - 1] * m_invk2 : real::R_1;
  241. }
  242. for (int i = 0; i < ORDER + 1; i++)
  243. {
  244. an[i] = 0;
  245. for (int j = i; j < ORDER + 1; j++)
  246. an[i] += (real)Comb(j, i) * k1p[j - i] * bn[j];
  247. an[i] *= k2p[i];
  248. }
  249. printf("Polynomial estimate:\n");
  250. for (int j = 0; j < ORDER + 1; j++)
  251. {
  252. if (j)
  253. printf("+");
  254. printf("x^%i*", j);
  255. an[j].print(40);
  256. }
  257. printf("\n");
  258. }
  259. real Value(real const &x)
  260. {
  261. return m_func(x * m_k2 + m_k1);
  262. }
  263. real Weight(real const &x)
  264. {
  265. return m_weight(x * m_k2 + m_k1);
  266. }
  267. /* ORDER + 1 Chebyshev coefficients and 1 error value */
  268. real coeff[ORDER + 2];
  269. /* ORDER + 1 zeroes of the error function */
  270. real zeroes[ORDER + 1];
  271. /* ORDER + 2 control points */
  272. real control[ORDER + 2];
  273. private:
  274. RealFunc *m_func, *m_weight;
  275. real m_k1, m_k2, m_invk1, m_invk2;
  276. };
  277. #endif /* __REMEZ_SOLVER_H__ */