Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

412 строки
11 KiB

  1. //
  2. // Lol Engine
  3. //
  4. // Copyright: (c) 2010-2014 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://www.wtfpl.net/ for more details.
  9. //
  10. #pragma once
  11. //
  12. // The polynomial class
  13. // --------------------
  14. //
  15. // The data structure is a simple dynamic array of scalars, with the
  16. // added guarantee that the leading coefficient is always non-zero.
  17. //
  18. #include <functional>
  19. namespace lol
  20. {
  21. template<typename T>
  22. struct polynomial
  23. {
  24. /* The zero polynomial */
  25. explicit inline polynomial() {}
  26. /* A constant polynomial */
  27. explicit inline polynomial(T const &a)
  28. {
  29. m_coefficients.push(a);
  30. reduce_degree();
  31. }
  32. /* Create a polynomial from a list of coefficients */
  33. explicit polynomial(std::initializer_list<T> const &init)
  34. {
  35. for (auto a : init)
  36. m_coefficients.push(a);
  37. reduce_degree();
  38. }
  39. /* Factory for Chebyshev polynomials */
  40. static polynomial<T> chebyshev(int n)
  41. {
  42. /* Use T0(x) = 1, T1(x) = x, Tn(x) = 2 x Tn-1(x) - Tn-2(x) */
  43. std::function<int64_t (int, int)> coeff = [&](int i, int j)
  44. {
  45. if (i > j || i < 0 || ((j ^ i) & 1))
  46. return (int64_t)0;
  47. if (j < 2)
  48. return (int64_t)1;
  49. return 2 * coeff(i - 1, j - 1) - coeff(i, j - 2);
  50. };
  51. polynomial<T> ret;
  52. for (int k = 0; k <= n; ++k)
  53. ret.m_coefficients.push(T(coeff(k, n)));
  54. return ret;
  55. }
  56. /* We define the zero polynomial (with no coefficients) as having
  57. * degree -1 on purpose. */
  58. inline int degree() const
  59. {
  60. return (int)m_coefficients.count() - 1;
  61. }
  62. /* Set one of the polynomial’s coefficients */
  63. void set(int n, T const &a)
  64. {
  65. ASSERT(n >= 0);
  66. if (n > degree() && !a)
  67. return;
  68. while (n > degree())
  69. m_coefficients.push(T(0));
  70. m_coefficients[n] = a;
  71. reduce_degree();
  72. }
  73. /* Evaluate polynomial at a given value. This method can also
  74. * be used to compose polynomials, i.e. using another polynomial
  75. * as the value instead of a scalar. */
  76. template<typename U> U eval(U x) const
  77. {
  78. U ret(leading());
  79. for (int i = degree() - 1; i >= 0; --i)
  80. ret = ret * x + U(m_coefficients[i]);
  81. return ret;
  82. }
  83. polynomial<T> derive() const
  84. {
  85. /* No need to reduce the degree after deriving. */
  86. polynomial<T> ret;
  87. for (int i = 1; i <= degree(); ++i)
  88. ret.m_coefficients.push(m_coefficients[i] * T(i));
  89. return ret;
  90. }
  91. array<T> roots() const
  92. {
  93. /* For now we can only solve polynomials of degrees 0, 1, 2 or 3. */
  94. ASSERT(degree() >= 0 && degree() <= 3,
  95. "roots() called on polynomial of degree %d", degree());
  96. if (degree() == 0)
  97. {
  98. /* p(x) = a > 0 */
  99. return array<T> {};
  100. }
  101. else if (degree() == 1)
  102. {
  103. /* p(x) = ax + b */
  104. T const &a = m_coefficients[1];
  105. T const &b = m_coefficients[0];
  106. return array<T> { -b / a };
  107. }
  108. else if (degree() == 2)
  109. {
  110. /* p(x) = ax² + bx + c */
  111. T const &a = m_coefficients[2];
  112. T const &b = m_coefficients[1];
  113. T const &c = m_coefficients[0];
  114. T const k = b / (a + a);
  115. T const delta = k * k - c / a;
  116. if (delta < T(0))
  117. {
  118. return array<T> {};
  119. }
  120. else if (delta > T(0))
  121. {
  122. T const sqrt_delta = sqrt(delta);
  123. return array<T> { -k - sqrt_delta, -k + sqrt_delta };
  124. }
  125. else
  126. {
  127. return array<T> { -k };
  128. }
  129. }
  130. else if (degree() == 3)
  131. {
  132. static T const pi = acos(T(-1));
  133. /* p(x) = ax³ + bx² + cx + d */
  134. T const &a = m_coefficients[3];
  135. T const &b = m_coefficients[2];
  136. T const &c = m_coefficients[1];
  137. T const &d = m_coefficients[0];
  138. /* Find k, m, and n such that p(x - k) / a = x³ + mx + n
  139. * q(x) = p(x - k) / a
  140. * = x³ + (b/a-3k) x² + ((c-2bk)/a+3k²) x + (d-ck+bk²)/a-k³
  141. *
  142. * So we want k = b/3a and thus:
  143. * q(x) = x³ + (c-bk)/a x + (d-ck+2bk²/3)/a
  144. *
  145. * k = b / 3a
  146. * m = (c - bk) / a
  147. * n = (d - ck + 2bk²/3) / a */
  148. T const k = b / (T(3) * a);
  149. T const m = (c - b * k) / a;
  150. T const n = ((T(2) / T(3) * b * k - c) * k + d) / a;
  151. /* Let x = u + v, such that 3uv = -m, then:
  152. * q(u + v) = u³ + v³ + n
  153. *
  154. * We then need to solve the following system:
  155. * u³v³ = -m³/27
  156. * u³ + v³ = -n
  157. *
  158. * which gives :
  159. * Δ = n² + 4m³/27
  160. * u³ - v³ = √Δ
  161. * u³ + v³ = -n
  162. *
  163. * u³,v³ = (-n ± √Δ) / 2
  164. */
  165. T const delta = n * n + T(4) / T(27) * m * m * m;
  166. /* Because 3uv = -m and m is not complex
  167. * angle(u³) + angle(v³) must equal 0.
  168. *
  169. * This is why we compute u³ and v³ by norm and angle separately
  170. * instead of using a std::complex class */
  171. T u_norm, u3_angle;
  172. T v_norm, v3_angle;
  173. if (delta < 0)
  174. {
  175. v_norm = u_norm = sqrt(m / T(-3));
  176. u3_angle = atan2(sqrt(-delta), -n);
  177. v3_angle = -u3_angle;
  178. }
  179. else
  180. {
  181. T const sqrt_delta = sqrt(delta);
  182. u_norm = cbrt(abs(n - sqrt_delta) / T(2));
  183. v_norm = cbrt(abs(n + sqrt_delta) / T(2));
  184. u3_angle = (n >= sqrt_delta) ? pi : 0;
  185. v3_angle = (n <= -sqrt_delta) ? 0 : -pi;
  186. }
  187. T solutions[3];
  188. for (int i : { 0, 1, 2 })
  189. {
  190. T u_angle = (u3_angle + T(2 * i) * pi) / T(3);
  191. T v_angle = (v3_angle - T(2 * i) * pi) / T(3);
  192. solutions[i] = u_norm * cos(u_angle) + v_norm * cos(v_angle);
  193. }
  194. if (a == d && b == c && b == T(3)*a) // triple solution
  195. return array<T> { solutions[0] - k };
  196. // if root of the derivative is also root of the current polynomial, we have a double root.
  197. for (auto root : polynomial<T>{c, T(2)*b, T(3)*a}.roots())
  198. if (eval(root) == T(0))
  199. return array<T> { (solutions[0] + solutions[2]) / T(2) - k,
  200. solutions[1] - k };
  201. // we have 3 or 1 root depending on delta sign
  202. if (delta > 0)
  203. return array<T> { solutions[0] - k };
  204. else // if (delta < 0) 3 real solutions
  205. return array<T> { solutions[0] - k,
  206. solutions[1] - k,
  207. solutions[2] - k };
  208. }
  209. /* It is an error to reach this point. */
  210. ASSERT(false);
  211. return array<T> {};
  212. }
  213. /* Access individual coefficients. This is read-only and returns a
  214. * copy because we cannot let the user mess with the integrity of
  215. * the structure (i.e. the guarantee that the leading coefficient
  216. * remains non-zero). */
  217. inline T operator[](ptrdiff_t n) const
  218. {
  219. if (n < 0 || n > degree())
  220. return T(0);
  221. return m_coefficients[n];
  222. }
  223. /* Return the leading coefficient */
  224. inline T leading() const
  225. {
  226. return (*this)[degree()];
  227. }
  228. /* Unary plus */
  229. polynomial<T> operator +() const
  230. {
  231. return *this;
  232. }
  233. /* Unary minus */
  234. polynomial<T> operator -() const
  235. {
  236. polynomial<T> ret;
  237. for (auto a : m_coefficients)
  238. ret.m_coefficients.push(-a);
  239. return ret;
  240. }
  241. /* Add two polynomials */
  242. polynomial<T> &operator +=(polynomial<T> const &p)
  243. {
  244. int min_degree = lol::min(p.degree(), degree());
  245. for (int i = 0; i <= min_degree; ++i)
  246. m_coefficients[i] += p[i];
  247. for (int i = min_degree + 1; i <= p.degree(); ++i)
  248. m_coefficients.push(p[i]);
  249. reduce_degree();
  250. return *this;
  251. }
  252. polynomial<T> operator +(polynomial<T> const &p) const
  253. {
  254. return polynomial<T>(*this) += p;
  255. }
  256. /* Subtract two polynomials */
  257. polynomial<T> &operator -=(polynomial<T> const &p)
  258. {
  259. return *this += -p;
  260. }
  261. polynomial<T> operator -(polynomial<T> const &p) const
  262. {
  263. return polynomial<T>(*this) += -p;
  264. }
  265. /* Multiply polynomial by a scalar */
  266. polynomial<T> &operator *=(T const &k)
  267. {
  268. for (auto &a : m_coefficients)
  269. a *= k;
  270. reduce_degree();
  271. return *this;
  272. }
  273. polynomial<T> operator *(T const &k) const
  274. {
  275. return polynomial<T>(*this) *= k;
  276. }
  277. /* Divide polynomial by a scalar */
  278. polynomial<T> &operator /=(T const &k)
  279. {
  280. return *this *= (T(1) / k);
  281. }
  282. polynomial<T> operator /(T const &k) const
  283. {
  284. return *this * (T(1) / k);
  285. }
  286. /* Multiply polynomial by another polynomial */
  287. polynomial<T> &operator *=(polynomial<T> const &p)
  288. {
  289. return *this = *this * p;
  290. }
  291. polynomial<T> operator *(polynomial<T> const &p) const
  292. {
  293. polynomial<T> ret;
  294. polynomial<T> const &q = *this;
  295. if (p.degree() >= 0 && q.degree() >= 0)
  296. {
  297. int n = p.degree() + q.degree();
  298. for (int i = 0; i <= n; ++i)
  299. ret.m_coefficients.push(T(0));
  300. for (int i = 0; i <= p.degree(); ++i)
  301. for (int j = 0; j <= q.degree(); ++j)
  302. ret.m_coefficients[i + j] += p[i] * q[j];
  303. ret.reduce_degree();
  304. }
  305. return ret;
  306. }
  307. /* Divide a polynomial by another one. There is no /= variant because
  308. * the return value contains both the quotient and the remainder. */
  309. tuple<polynomial<T>, polynomial<T>> operator /(polynomial<T> p) const
  310. {
  311. ASSERT(p.degree() >= 0);
  312. tuple<polynomial<T>, polynomial<T>> ret;
  313. polynomial<T> &quotient = ret.m1;
  314. polynomial<T> &remainder = ret.m2;
  315. remainder = *this / p.leading();
  316. p /= p.leading();
  317. for (int n = remainder.degree() - p.degree(); n >= 0; --n)
  318. {
  319. quotient.set(n, remainder.leading());
  320. for (int i = 0; i < p.degree(); ++i)
  321. remainder.m_coefficients[n + i] -= remainder.leading() * p[i];
  322. remainder.m_coefficients.pop();
  323. }
  324. return ret;
  325. }
  326. private:
  327. /* Enforce the non-zero leading coefficient rule. */
  328. void reduce_degree()
  329. {
  330. while (m_coefficients.count() && m_coefficients.last() == T(0))
  331. m_coefficients.pop();
  332. }
  333. /* The polynomial coefficients */
  334. array<T> m_coefficients;
  335. };
  336. template<typename T>
  337. polynomial<T> operator *(T const &k, polynomial<T> const &p)
  338. {
  339. /* We assume k * p is the same as p * k */
  340. return p * k;
  341. }
  342. } /* namespace lol */