270 lines
3.9 KiB
C++
270 lines
3.9 KiB
C++
// find the roots of a polynomial numerically
|
|
|
|
#include "stdafx.h"
|
|
#include "defs.h"
|
|
|
|
#define YMAX 101
|
|
#define DELTA 1.0e-6
|
|
#define EPSILON 1.0e-9
|
|
#define ABS(z) sqrt((z).r * (z).r + (z).i * (z).i)
|
|
#define RANDOM (4.0 * (double) rand() / (double) RAND_MAX - 2.0)
|
|
|
|
static struct {
|
|
double r, i;
|
|
} a, b, x, y, fa, fb, dx, df, c[YMAX];
|
|
|
|
void
|
|
eval_nroots(void)
|
|
{
|
|
int h, i, k, n;
|
|
|
|
push(cadr(p1));
|
|
eval();
|
|
|
|
push(caddr(p1));
|
|
eval();
|
|
p2 = pop();
|
|
if (p2 == symbol(NIL))
|
|
guess();
|
|
else
|
|
push(p2);
|
|
|
|
p2 = pop();
|
|
p1 = pop();
|
|
|
|
if (!ispoly(p1, p2))
|
|
stop("nroots: polynomial?");
|
|
|
|
// mark the stack
|
|
|
|
h = tos;
|
|
|
|
// get the coefficients
|
|
|
|
push(p1);
|
|
push(p2);
|
|
n = coeff();
|
|
if (n > YMAX)
|
|
stop("nroots: degree?");
|
|
|
|
// convert the coefficients to real and imaginary doubles
|
|
|
|
for (i = 0; i < n; i++) {
|
|
push(stack[h + i]);
|
|
real();
|
|
yyfloat();
|
|
eval();
|
|
p1 = pop();
|
|
push(stack[h + i]);
|
|
imag();
|
|
yyfloat();
|
|
eval();
|
|
p2 = pop();
|
|
if (!isdouble(p1) || !isdouble(p2))
|
|
stop("nroots: coefficients?");
|
|
c[i].r = p1->u.d;
|
|
c[i].i = p2->u.d;
|
|
}
|
|
|
|
// pop the coefficients
|
|
|
|
tos = h;
|
|
|
|
// n is the number of coefficients, n = deg(p) + 1
|
|
|
|
monic(n);
|
|
|
|
for (k = n; k > 1; k--) {
|
|
findroot(k);
|
|
if (fabs(a.r) < DELTA)
|
|
a.r = 0.0;
|
|
if (fabs(a.i) < DELTA)
|
|
a.i = 0.0;
|
|
push_double(a.r);
|
|
push_double(a.i);
|
|
push(imaginaryunit);
|
|
multiply();
|
|
add();
|
|
divpoly(k);
|
|
}
|
|
|
|
// now make n equal to the number of roots
|
|
|
|
n = tos - h;
|
|
|
|
if (n > 1) {
|
|
sort_stack(n);
|
|
p1 = alloc_tensor(n);
|
|
p1->u.tensor->ndim = 1;
|
|
p1->u.tensor->dim[0] = n;
|
|
for (i = 0; i < n; i++)
|
|
p1->u.tensor->elem[i] = stack[h + i];
|
|
tos = h;
|
|
push(p1);
|
|
}
|
|
}
|
|
|
|
// divide the polynomial by its leading coefficient
|
|
|
|
void
|
|
monic(int n)
|
|
{
|
|
int k;
|
|
double t;
|
|
y = c[n - 1];
|
|
t = y.r * y.r + y.i * y.i;
|
|
for (k = 0; k < n - 1; k++) {
|
|
c[k].r = (c[k].r * y.r + c[k].i * y.i) / t;
|
|
c[k].i = (c[k].i * y.r - c[k].r * y.i) / t;
|
|
}
|
|
c[n - 1].r = 1.0;
|
|
c[n - 1].i = 0.0;
|
|
}
|
|
|
|
// uses the secant method
|
|
|
|
void
|
|
findroot(int n)
|
|
{
|
|
int j, k;
|
|
double t;
|
|
|
|
if (ABS(c[0]) < DELTA) {
|
|
a.r = 0.0;
|
|
a.i = 0.0;
|
|
return;
|
|
}
|
|
|
|
for (j = 0; j < 100; j++) {
|
|
|
|
a.r = RANDOM;
|
|
a.i = RANDOM;
|
|
|
|
compute_fa(n);
|
|
|
|
b = a;
|
|
fb = fa;
|
|
|
|
a.r = RANDOM;
|
|
a.i = RANDOM;
|
|
|
|
for (k = 0; k < 1000; k++) {
|
|
|
|
compute_fa(n);
|
|
|
|
if (ABS(fa) < EPSILON)
|
|
return;
|
|
|
|
if (ABS(fa) < ABS(fb)) {
|
|
x = a;
|
|
a = b;
|
|
b = x;
|
|
x = fa;
|
|
fa = fb;
|
|
fb = x;
|
|
}
|
|
|
|
// dx = b - a
|
|
|
|
dx.r = b.r - a.r;
|
|
dx.i = b.i - a.i;
|
|
|
|
// df = fb - fa
|
|
|
|
df.r = fb.r - fa.r;
|
|
df.i = fb.i - fa.i;
|
|
|
|
// y = dx / df
|
|
|
|
t = df.r * df.r + df.i * df.i;
|
|
|
|
if (t == 0.0)
|
|
break;
|
|
|
|
y.r = (dx.r * df.r + dx.i * df.i) / t;
|
|
y.i = (dx.i * df.r - dx.r * df.i) / t;
|
|
|
|
// a = b - y * fb
|
|
|
|
a.r = b.r - (y.r * fb.r - y.i * fb.i);
|
|
a.i = b.i - (y.r * fb.i + y.i * fb.r);
|
|
}
|
|
}
|
|
|
|
stop("nroots: convergence error");
|
|
}
|
|
|
|
void
|
|
compute_fa(int n)
|
|
{
|
|
int k;
|
|
double t;
|
|
|
|
// x = a
|
|
|
|
x.r = a.r;
|
|
x.i = a.i;
|
|
|
|
// fa = c0 + c1 * x
|
|
|
|
fa.r = c[0].r + c[1].r * x.r - c[1].i * x.i;
|
|
fa.i = c[0].i + c[1].r * x.i + c[1].i * x.r;
|
|
|
|
for (k = 2; k < n; k++) {
|
|
|
|
// x = a * x
|
|
|
|
t = a.r * x.r - a.i * x.i;
|
|
x.i = a.r * x.i + a.i * x.r;
|
|
x.r = t;
|
|
|
|
// fa += c[k] * x
|
|
|
|
fa.r += c[k].r * x.r - c[k].i * x.i;
|
|
fa.i += c[k].r * x.i + c[k].i * x.r;
|
|
}
|
|
}
|
|
|
|
// divide the polynomial by x - a
|
|
|
|
void
|
|
divpoly(int n)
|
|
{
|
|
int k;
|
|
for (k = n - 1; k > 0; k--) {
|
|
c[k - 1].r += c[k].r * a.r - c[k].i * a.i;
|
|
c[k - 1].i += c[k].i * a.r + c[k].r * a.i;
|
|
}
|
|
if (ABS(c[0]) > DELTA)
|
|
stop("nroots: residual error");
|
|
for (k = 0; k < n - 1; k++) {
|
|
c[k].r = c[k + 1].r;
|
|
c[k].i = c[k + 1].i;
|
|
}
|
|
}
|
|
|
|
#if SELFTEST
|
|
|
|
static char *s[] = {
|
|
|
|
"nroots(x)",
|
|
"0",
|
|
|
|
"nroots((1+i)*x^2+1)",
|
|
"(-0.17178-0.727673*i,0.17178+0.727673*i)",
|
|
|
|
"nroots(sqrt(2)*exp(i*pi/4)*x^2+1)",
|
|
"(-0.17178-0.727673*i,0.17178+0.727673*i)",
|
|
|
|
// "nroots(x^4+1)",
|
|
// "(-0.707107+0.707107*i,-0.707107-0.707107*i,0.707107+0.707107*i,0.707107-0.707107*i)",
|
|
};
|
|
|
|
void
|
|
test_nroots(void)
|
|
{
|
|
test(__FILE__, s, sizeof s / sizeof (char *));
|
|
}
|
|
|
|
#endif
|