nroots.cpp 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. // find the roots of a polynomial numerically
  2. #include "stdafx.h"
  3. #include "defs.h"
  4. #define YMAX 101
  5. #define DELTA 1.0e-6
  6. #define EPSILON 1.0e-9
  7. #define ABS(z) sqrt((z).r * (z).r + (z).i * (z).i)
  8. #define RANDOM (4.0 * (double) rand() / (double) RAND_MAX - 2.0)
  9. static struct {
  10. double r, i;
  11. } a, b, x, y, fa, fb, dx, df, c[YMAX];
  12. void
  13. eval_nroots(void)
  14. {
  15. int h, i, k, n;
  16. push(cadr(p1));
  17. eval();
  18. push(caddr(p1));
  19. eval();
  20. p2 = pop();
  21. if (p2 == symbol(NIL))
  22. guess();
  23. else
  24. push(p2);
  25. p2 = pop();
  26. p1 = pop();
  27. if (!ispoly(p1, p2))
  28. stop("nroots: polynomial?");
  29. // mark the stack
  30. h = tos;
  31. // get the coefficients
  32. push(p1);
  33. push(p2);
  34. n = coeff();
  35. if (n > YMAX)
  36. stop("nroots: degree?");
  37. // convert the coefficients to real and imaginary doubles
  38. for (i = 0; i < n; i++) {
  39. push(stack[h + i]);
  40. real();
  41. yyfloat();
  42. eval();
  43. p1 = pop();
  44. push(stack[h + i]);
  45. imag();
  46. yyfloat();
  47. eval();
  48. p2 = pop();
  49. if (!isdouble(p1) || !isdouble(p2))
  50. stop("nroots: coefficients?");
  51. c[i].r = p1->u.d;
  52. c[i].i = p2->u.d;
  53. }
  54. // pop the coefficients
  55. tos = h;
  56. // n is the number of coefficients, n = deg(p) + 1
  57. monic(n);
  58. for (k = n; k > 1; k--) {
  59. findroot(k);
  60. if (fabs(a.r) < DELTA)
  61. a.r = 0.0;
  62. if (fabs(a.i) < DELTA)
  63. a.i = 0.0;
  64. push_double(a.r);
  65. push_double(a.i);
  66. push(imaginaryunit);
  67. multiply();
  68. add();
  69. divpoly(k);
  70. }
  71. // now make n equal to the number of roots
  72. n = tos - h;
  73. if (n > 1) {
  74. sort_stack(n);
  75. p1 = alloc_tensor(n);
  76. p1->u.tensor->ndim = 1;
  77. p1->u.tensor->dim[0] = n;
  78. for (i = 0; i < n; i++)
  79. p1->u.tensor->elem[i] = stack[h + i];
  80. tos = h;
  81. push(p1);
  82. }
  83. }
  84. // divide the polynomial by its leading coefficient
  85. void
  86. monic(int n)
  87. {
  88. int k;
  89. double t;
  90. y = c[n - 1];
  91. t = y.r * y.r + y.i * y.i;
  92. for (k = 0; k < n - 1; k++) {
  93. c[k].r = (c[k].r * y.r + c[k].i * y.i) / t;
  94. c[k].i = (c[k].i * y.r - c[k].r * y.i) / t;
  95. }
  96. c[n - 1].r = 1.0;
  97. c[n - 1].i = 0.0;
  98. }
  99. // uses the secant method
  100. void
  101. findroot(int n)
  102. {
  103. int j, k;
  104. double t;
  105. if (ABS(c[0]) < DELTA) {
  106. a.r = 0.0;
  107. a.i = 0.0;
  108. return;
  109. }
  110. for (j = 0; j < 100; j++) {
  111. a.r = RANDOM;
  112. a.i = RANDOM;
  113. compute_fa(n);
  114. b = a;
  115. fb = fa;
  116. a.r = RANDOM;
  117. a.i = RANDOM;
  118. for (k = 0; k < 1000; k++) {
  119. compute_fa(n);
  120. if (ABS(fa) < EPSILON)
  121. return;
  122. if (ABS(fa) < ABS(fb)) {
  123. x = a;
  124. a = b;
  125. b = x;
  126. x = fa;
  127. fa = fb;
  128. fb = x;
  129. }
  130. // dx = b - a
  131. dx.r = b.r - a.r;
  132. dx.i = b.i - a.i;
  133. // df = fb - fa
  134. df.r = fb.r - fa.r;
  135. df.i = fb.i - fa.i;
  136. // y = dx / df
  137. t = df.r * df.r + df.i * df.i;
  138. if (t == 0.0)
  139. break;
  140. y.r = (dx.r * df.r + dx.i * df.i) / t;
  141. y.i = (dx.i * df.r - dx.r * df.i) / t;
  142. // a = b - y * fb
  143. a.r = b.r - (y.r * fb.r - y.i * fb.i);
  144. a.i = b.i - (y.r * fb.i + y.i * fb.r);
  145. }
  146. }
  147. stop("nroots: convergence error");
  148. }
  149. void
  150. compute_fa(int n)
  151. {
  152. int k;
  153. double t;
  154. // x = a
  155. x.r = a.r;
  156. x.i = a.i;
  157. // fa = c0 + c1 * x
  158. fa.r = c[0].r + c[1].r * x.r - c[1].i * x.i;
  159. fa.i = c[0].i + c[1].r * x.i + c[1].i * x.r;
  160. for (k = 2; k < n; k++) {
  161. // x = a * x
  162. t = a.r * x.r - a.i * x.i;
  163. x.i = a.r * x.i + a.i * x.r;
  164. x.r = t;
  165. // fa += c[k] * x
  166. fa.r += c[k].r * x.r - c[k].i * x.i;
  167. fa.i += c[k].r * x.i + c[k].i * x.r;
  168. }
  169. }
  170. // divide the polynomial by x - a
  171. void
  172. divpoly(int n)
  173. {
  174. int k;
  175. for (k = n - 1; k > 0; k--) {
  176. c[k - 1].r += c[k].r * a.r - c[k].i * a.i;
  177. c[k - 1].i += c[k].i * a.r + c[k].r * a.i;
  178. }
  179. if (ABS(c[0]) > DELTA)
  180. stop("nroots: residual error");
  181. for (k = 0; k < n - 1; k++) {
  182. c[k].r = c[k + 1].r;
  183. c[k].i = c[k + 1].i;
  184. }
  185. }
  186. #if SELFTEST
  187. static char *s[] = {
  188. "nroots(x)",
  189. "0",
  190. "nroots((1+i)*x^2+1)",
  191. "(-0.17178-0.727673*i,0.17178+0.727673*i)",
  192. "nroots(sqrt(2)*exp(i*pi/4)*x^2+1)",
  193. "(-0.17178-0.727673*i,0.17178+0.727673*i)",
  194. // "nroots(x^4+1)",
  195. // "(-0.707107+0.707107*i,-0.707107-0.707107*i,0.707107+0.707107*i,0.707107-0.707107*i)",
  196. };
  197. void
  198. test_nroots(void)
  199. {
  200. test(__FILE__, s, sizeof s / sizeof (char *));
  201. }
  202. #endif