Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

387 рядки
9.8 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 HAVE_CONFIG_H
  11. # include "config.h"
  12. #endif
  13. #include <cstring>
  14. #include <cstdio>
  15. #include "core.h"
  16. using namespace lol;
  17. /* The order of the approximation we're looking for */
  18. static int const ORDER = 4;
  19. /* The function we want to approximate */
  20. static real myfun(real const &x)
  21. {
  22. return exp(x);
  23. //if (!x)
  24. // return real::R_PI_2;
  25. //return sin(x * real::R_PI_2) / x;
  26. }
  27. static real myerror(real const &x)
  28. {
  29. return myfun(x);
  30. //return real::R_1;
  31. }
  32. /* Naive matrix inversion */
  33. template<int N> struct Matrix
  34. {
  35. inline Matrix() {}
  36. Matrix(real x)
  37. {
  38. for (int j = 0; j < N; j++)
  39. for (int i = 0; i < N; i++)
  40. if (i == j)
  41. m[i][j] = x;
  42. else
  43. m[i][j] = 0;
  44. }
  45. Matrix<N> inv() const
  46. {
  47. Matrix a = *this, b((real)1.0);
  48. /* Inversion method: iterate through all columns and make sure
  49. * all the terms are 1 on the diagonal and 0 everywhere else */
  50. for (int i = 0; i < N; i++)
  51. {
  52. /* If the expected coefficient is zero, add one of
  53. * the other lines. The first we meet will do. */
  54. if ((double)a.m[i][i] == 0.0)
  55. {
  56. for (int j = i + 1; j < N; j++)
  57. {
  58. if ((double)a.m[i][j] == 0.0)
  59. continue;
  60. /* Add row j to row i */
  61. for (int n = 0; n < N; n++)
  62. {
  63. a.m[n][i] += a.m[n][j];
  64. b.m[n][i] += b.m[n][j];
  65. }
  66. break;
  67. }
  68. }
  69. /* Now we know the diagonal term is non-zero. Get its inverse
  70. * and use that to nullify all other terms in the column */
  71. real x = (real)1.0 / a.m[i][i];
  72. for (int j = 0; j < N; j++)
  73. {
  74. if (j == i)
  75. continue;
  76. real mul = x * a.m[i][j];
  77. for (int n = 0; n < N; n++)
  78. {
  79. a.m[n][j] -= mul * a.m[n][i];
  80. b.m[n][j] -= mul * b.m[n][i];
  81. }
  82. }
  83. /* Finally, ensure the diagonal term is 1 */
  84. for (int n = 0; n < N; n++)
  85. {
  86. a.m[n][i] *= x;
  87. b.m[n][i] *= x;
  88. }
  89. }
  90. return b;
  91. }
  92. void print() const
  93. {
  94. for (int j = 0; j < N; j++)
  95. {
  96. for (int i = 0; i < N; i++)
  97. printf("%9.5f ", (double)m[j][i]);
  98. printf("\n");
  99. }
  100. }
  101. real m[N][N];
  102. };
  103. static int cheby[ORDER + 1][ORDER + 1];
  104. /* Fill the Chebyshev tables */
  105. static void cheby_init()
  106. {
  107. memset(cheby, 0, sizeof(cheby));
  108. cheby[0][0] = 1;
  109. cheby[1][1] = 1;
  110. for (int i = 2; i < ORDER + 1; i++)
  111. {
  112. cheby[i][0] = -cheby[i - 2][0];
  113. for (int j = 1; j < ORDER + 1; j++)
  114. cheby[i][j] = 2 * cheby[i - 1][j - 1] - cheby[i - 2][j];
  115. }
  116. }
  117. static void cheby_coeff(real *coeff, real *bn)
  118. {
  119. for (int i = 0; i < ORDER + 1; i++)
  120. {
  121. bn[i] = 0;
  122. for (int j = 0; j < ORDER + 1; j++)
  123. if (cheby[j][i])
  124. bn[i] += coeff[j] * (real)cheby[j][i];
  125. }
  126. }
  127. static real cheby_eval(real *coeff, real const &x)
  128. {
  129. real ret = 0.0, xn = 1.0;
  130. for (int i = 0; i < ORDER + 1; i++)
  131. {
  132. real mul = 0;
  133. for (int j = 0; j < ORDER + 1; j++)
  134. if (cheby[j][i])
  135. mul += coeff[j] * (real)cheby[j][i];
  136. ret += mul * xn;
  137. xn *= x;
  138. }
  139. return ret;
  140. }
  141. static void remez_init(real *coeff, real *zeroes)
  142. {
  143. /* Pick up x_i where error will be 0 and compute f(x_i) */
  144. real fxn[ORDER + 1];
  145. for (int i = 0; i < ORDER + 1; i++)
  146. {
  147. zeroes[i] = (real)(2 * i - ORDER) / (real)(ORDER + 1);
  148. fxn[i] = myfun(zeroes[i]);
  149. }
  150. /* We build a matrix of Chebishev evaluations: row i contains the
  151. * evaluations of x_i for polynomial order n = 0, 1, ... */
  152. Matrix<ORDER + 1> mat;
  153. for (int i = 0; i < ORDER + 1; i++)
  154. {
  155. /* Compute the powers of x_i */
  156. real powers[ORDER + 1];
  157. powers[0] = 1.0;
  158. for (int n = 1; n < ORDER + 1; n++)
  159. powers[n] = powers[n - 1] * zeroes[i];
  160. /* Compute the Chebishev evaluations at x_i */
  161. for (int n = 0; n < ORDER + 1; n++)
  162. {
  163. real sum = 0.0;
  164. for (int k = 0; k < ORDER + 1; k++)
  165. if (cheby[n][k])
  166. sum += (real)cheby[n][k] * powers[k];
  167. mat.m[i][n] = sum;
  168. }
  169. }
  170. /* Solve the system */
  171. mat = mat.inv();
  172. /* Compute interpolation coefficients */
  173. for (int j = 0; j < ORDER + 1; j++)
  174. {
  175. coeff[j] = 0;
  176. for (int i = 0; i < ORDER + 1; i++)
  177. coeff[j] += mat.m[j][i] * fxn[i];
  178. }
  179. }
  180. static void remez_findzeroes(real *coeff, real *zeroes, real *control)
  181. {
  182. for (int i = 0; i < ORDER + 1; i++)
  183. {
  184. real a = control[i];
  185. real ea = cheby_eval(coeff, a) - myfun(a);
  186. real b = control[i + 1];
  187. real eb = cheby_eval(coeff, b) - myfun(b);
  188. while (fabs(a - b) > (real)1e-140)
  189. {
  190. real c = (a + b) * (real)0.5;
  191. real ec = cheby_eval(coeff, c) - myfun(c);
  192. if ((ea < (real)0 && ec < (real)0)
  193. || (ea > (real)0 && ec > (real)0))
  194. {
  195. a = c;
  196. ea = ec;
  197. }
  198. else
  199. {
  200. b = c;
  201. eb = ec;
  202. }
  203. }
  204. zeroes[i] = a;
  205. }
  206. }
  207. static void remez_finderror(real *coeff, real *zeroes, real *control)
  208. {
  209. real final = 0;
  210. for (int i = 0; i < ORDER + 2; i++)
  211. {
  212. real a = -1, b = 1;
  213. if (i > 0)
  214. a = zeroes[i - 1];
  215. if (i < ORDER + 1)
  216. b = zeroes[i];
  217. printf("Error for [%g..%g]: ", (double)a, (double)b);
  218. for (;;)
  219. {
  220. real c = a, delta = (b - a) / (real)10.0;
  221. real maxerror = 0;
  222. int best = -1;
  223. for (int k = 0; k <= 10; k++)
  224. {
  225. real e = fabs(cheby_eval(coeff, c) - myfun(c));
  226. if (e > maxerror)
  227. {
  228. maxerror = e;
  229. best = k;
  230. }
  231. c += delta;
  232. }
  233. if (best == 0)
  234. best = 1;
  235. if (best == 10)
  236. best = 9;
  237. b = a + (real)(best + 1) * delta;
  238. a = a + (real)(best - 1) * delta;
  239. if (b - a < (real)1e-15)
  240. {
  241. if (maxerror > final)
  242. final = maxerror;
  243. control[i] = (a + b) * (real)0.5;
  244. printf("%g (at %g)\n", (double)maxerror, (double)control[i]);
  245. break;
  246. }
  247. }
  248. }
  249. printf("Final error: %g\n", (double)final);
  250. }
  251. static void remez_step(real *coeff, real *control)
  252. {
  253. /* Pick up x_i where error will be 0 and compute f(x_i) */
  254. real fxn[ORDER + 2];
  255. for (int i = 0; i < ORDER + 2; i++)
  256. fxn[i] = myfun(control[i]);
  257. /* We build a matrix of Chebishev evaluations: row i contains the
  258. * evaluations of x_i for polynomial order n = 0, 1, ... */
  259. Matrix<ORDER + 2> mat;
  260. for (int i = 0; i < ORDER + 2; i++)
  261. {
  262. /* Compute the powers of x_i */
  263. real powers[ORDER + 1];
  264. powers[0] = 1.0;
  265. for (int n = 1; n < ORDER + 1; n++)
  266. powers[n] = powers[n - 1] * control[i];
  267. /* Compute the Chebishev evaluations at x_i */
  268. for (int n = 0; n < ORDER + 1; n++)
  269. {
  270. real sum = 0.0;
  271. for (int k = 0; k < ORDER + 1; k++)
  272. if (cheby[n][k])
  273. sum += (real)cheby[n][k] * powers[k];
  274. mat.m[i][n] = sum;
  275. }
  276. if (i & 1)
  277. mat.m[i][ORDER + 1] = fabs(myerror(control[i]));
  278. else
  279. mat.m[i][ORDER + 1] = -fabs(myerror(control[i]));
  280. }
  281. /* Solve the system */
  282. mat = mat.inv();
  283. /* Compute interpolation coefficients */
  284. for (int j = 0; j < ORDER + 1; j++)
  285. {
  286. coeff[j] = 0;
  287. for (int i = 0; i < ORDER + 2; i++)
  288. coeff[j] += mat.m[j][i] * fxn[i];
  289. }
  290. /* Compute the error */
  291. real error = 0;
  292. for (int i = 0; i < ORDER + 2; i++)
  293. error += mat.m[ORDER + 1][i] * fxn[i];
  294. }
  295. int main(int argc, char **argv)
  296. {
  297. cheby_init();
  298. /* ORDER + 1 chebyshev coefficients and 1 error value */
  299. real coeff[ORDER + 2];
  300. /* ORDER + 1 zeroes of the error function */
  301. real zeroes[ORDER + 1];
  302. /* ORDER + 2 control points */
  303. real control[ORDER + 2];
  304. real bn[ORDER + 1];
  305. remez_init(coeff, zeroes);
  306. cheby_coeff(coeff, bn);
  307. for (int j = 0; j < ORDER + 1; j++)
  308. printf("%s%12.10gx^%i", j ? "+" : "", (double)bn[j], j);
  309. printf("\n");
  310. for (int n = 0; n < 200; n++)
  311. {
  312. remez_finderror(coeff, zeroes, control);
  313. remez_step(coeff, control);
  314. cheby_coeff(coeff, bn);
  315. for (int j = 0; j < ORDER + 1; j++)
  316. printf("%s%12.10gx^%i", j ? "+" : "", (double)bn[j], j);
  317. printf("\n");
  318. remez_findzeroes(coeff, zeroes, control);
  319. }
  320. remez_finderror(coeff, zeroes, control);
  321. remez_step(coeff, control);
  322. cheby_coeff(coeff, bn);
  323. for (int j = 0; j < ORDER + 1; j++)
  324. printf("%s%12.10gx^%i", j ? "+" : "", (double)bn[j], j);
  325. printf("\n");
  326. return EXIT_SUCCESS;
  327. }