261 lines
5.5 KiB
CoffeeScript
261 lines
5.5 KiB
CoffeeScript
# find the roots of a polynomial numerically
|
|
|
|
|
|
|
|
NROOTS_YMAX = 101
|
|
NROOTS_DELTA = 1.0e-6
|
|
NROOTS_EPSILON = 1.0e-9
|
|
NROOTS_ABS = (z) ->
|
|
return Math.sqrt((z).r * (z).r + (z).i * (z).i)
|
|
|
|
# random between -2 and 2
|
|
theRandom = 0.0
|
|
|
|
NROOTS_RANDOM = ->
|
|
#theRandom += 0.2
|
|
#return theRandom
|
|
return 4.0 * Math.random() - 2.0
|
|
|
|
class numericRootOfPolynomial
|
|
r: 0.0
|
|
i: 0.0
|
|
|
|
nroots_a = new numericRootOfPolynomial()
|
|
nroots_b = new numericRootOfPolynomial()
|
|
nroots_x = new numericRootOfPolynomial()
|
|
nroots_y = new numericRootOfPolynomial()
|
|
nroots_fa = new numericRootOfPolynomial()
|
|
nroots_fb = new numericRootOfPolynomial()
|
|
nroots_dx = new numericRootOfPolynomial()
|
|
nroots_df = new numericRootOfPolynomial()
|
|
nroots_c = []
|
|
for initNRoots in [0...NROOTS_YMAX]
|
|
nroots_c[initNRoots] = new numericRootOfPolynomial()
|
|
|
|
Eval_nroots = ->
|
|
h = 0
|
|
i = 0
|
|
k = 0
|
|
n = 0
|
|
|
|
push(cadr(p1))
|
|
Eval()
|
|
|
|
push(caddr(p1))
|
|
Eval()
|
|
p2 = pop()
|
|
if (p2 == symbol(NIL))
|
|
guess()
|
|
else
|
|
push(p2)
|
|
|
|
p2 = pop()
|
|
p1 = pop()
|
|
|
|
if (!ispolyexpandedform(p1, p2))
|
|
stop("nroots: polynomial?")
|
|
|
|
# mark the stack
|
|
|
|
h = tos
|
|
|
|
# get the coefficients
|
|
|
|
n = coeff(p2, p1)
|
|
if (n > NROOTS_YMAX)
|
|
stop("nroots: degree?")
|
|
|
|
# convert the coefficients to real and imaginary doubles
|
|
|
|
for i in [0...n]
|
|
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?")
|
|
nroots_c[i].r = p1.d
|
|
nroots_c[i].i = p2.d
|
|
|
|
# pop the coefficients
|
|
|
|
moveTos h
|
|
|
|
# n is the number of coefficients, n = deg(p) + 1
|
|
|
|
monic(n)
|
|
|
|
for k in [n...1] by -1
|
|
findroot(k)
|
|
if (Math.abs(nroots_a.r) < NROOTS_DELTA)
|
|
nroots_a.r = 0.0
|
|
if (Math.abs(nroots_a.i) < NROOTS_DELTA)
|
|
nroots_a.i = 0.0
|
|
push_double(nroots_a.r)
|
|
push_double(nroots_a.i)
|
|
push(imaginaryunit)
|
|
multiply()
|
|
add()
|
|
NROOTS_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.tensor.ndim = 1
|
|
p1.tensor.dim[0] = n
|
|
for i in [0...n]
|
|
p1.tensor.elem[i] = stack[h + i]
|
|
moveTos h
|
|
push(p1)
|
|
|
|
# divide the polynomial by its leading coefficient
|
|
|
|
monic = (n) ->
|
|
k = 0
|
|
t = 0.0
|
|
nroots_y.r = nroots_c[n - 1].r
|
|
nroots_y.i = nroots_c[n - 1].i
|
|
t = nroots_y.r * nroots_y.r + nroots_y.i * nroots_y.i
|
|
for k in [0...(n - 1)]
|
|
nroots_c[k].r = (nroots_c[k].r * nroots_y.r + nroots_c[k].i * nroots_y.i) / t
|
|
nroots_c[k].i = (nroots_c[k].i * nroots_y.r - nroots_c[k].r * nroots_y.i) / t
|
|
nroots_c[n - 1].r = 1.0
|
|
nroots_c[n - 1].i = 0.0
|
|
|
|
# uses the secant method
|
|
|
|
findroot = (n) ->
|
|
j = 0
|
|
k = 0
|
|
t = 0.0
|
|
|
|
if (NROOTS_ABS(nroots_c[0]) < NROOTS_DELTA)
|
|
nroots_a.r = 0.0
|
|
nroots_a.i = 0.0
|
|
return
|
|
|
|
for j in [0...100]
|
|
|
|
nroots_a.r = NROOTS_RANDOM()
|
|
nroots_a.i = NROOTS_RANDOM()
|
|
|
|
compute_fa(n)
|
|
|
|
nroots_b.r = nroots_a.r
|
|
nroots_b.i = nroots_a.i
|
|
|
|
nroots_fb.r = nroots_fa.r
|
|
nroots_fb.i = nroots_fa.i
|
|
|
|
nroots_a.r = NROOTS_RANDOM()
|
|
nroots_a.i = NROOTS_RANDOM()
|
|
|
|
for k in [0...1000]
|
|
|
|
compute_fa(n)
|
|
|
|
nrabs = NROOTS_ABS(nroots_fa)
|
|
if DEBUG then console.log "nrabs: " + nrabs
|
|
if ( nrabs < NROOTS_EPSILON)
|
|
return
|
|
|
|
if (NROOTS_ABS(nroots_fa) < NROOTS_ABS(nroots_fb))
|
|
nroots_x.r = nroots_a.r
|
|
nroots_x.i = nroots_a.i
|
|
|
|
nroots_a.r = nroots_b.r
|
|
nroots_a.i = nroots_b.i
|
|
|
|
nroots_b.r = nroots_x.r
|
|
nroots_b.i = nroots_x.i
|
|
|
|
nroots_x.r = nroots_fa.r
|
|
nroots_x.i = nroots_fa.i
|
|
|
|
nroots_fa.r = nroots_fb.r
|
|
nroots_fa.i = nroots_fb.i
|
|
|
|
nroots_fb.r = nroots_x.r
|
|
nroots_fb.i = nroots_x.i
|
|
|
|
# dx = nroots_b - nroots_a
|
|
|
|
nroots_dx.r = nroots_b.r - nroots_a.r
|
|
nroots_dx.i = nroots_b.i - nroots_a.i
|
|
|
|
# df = fb - fa
|
|
|
|
nroots_df.r = nroots_fb.r - nroots_fa.r
|
|
nroots_df.i = nroots_fb.i - nroots_fa.i
|
|
|
|
# y = dx / df
|
|
|
|
t = nroots_df.r * nroots_df.r + nroots_df.i * nroots_df.i
|
|
|
|
if (t == 0.0)
|
|
break
|
|
|
|
nroots_y.r = (nroots_dx.r * nroots_df.r + nroots_dx.i * nroots_df.i) / t
|
|
nroots_y.i = (nroots_dx.i * nroots_df.r - nroots_dx.r * nroots_df.i) / t
|
|
|
|
# a = b - y * fb
|
|
|
|
nroots_a.r = nroots_b.r - (nroots_y.r * nroots_fb.r - nroots_y.i * nroots_fb.i)
|
|
nroots_a.i = nroots_b.i - (nroots_y.r * nroots_fb.i + nroots_y.i * nroots_fb.r)
|
|
|
|
stop("nroots: convergence error")
|
|
|
|
compute_fa = (n) ->
|
|
k = 0
|
|
t = 0.0
|
|
|
|
# x = a
|
|
|
|
nroots_x.r = nroots_a.r
|
|
nroots_x.i = nroots_a.i
|
|
|
|
# fa = c0 + c1 * x
|
|
|
|
nroots_fa.r = nroots_c[0].r + nroots_c[1].r * nroots_x.r - nroots_c[1].i * nroots_x.i
|
|
nroots_fa.i = nroots_c[0].i + nroots_c[1].r * nroots_x.i + nroots_c[1].i * nroots_x.r
|
|
|
|
for k in [2...n]
|
|
|
|
# x = a * x
|
|
|
|
t = nroots_a.r * nroots_x.r - nroots_a.i * nroots_x.i
|
|
nroots_x.i = nroots_a.r * nroots_x.i + nroots_a.i * nroots_x.r
|
|
nroots_x.r = t
|
|
|
|
# fa += c[k] * x
|
|
|
|
nroots_fa.r += nroots_c[k].r * nroots_x.r - nroots_c[k].i * nroots_x.i
|
|
nroots_fa.i += nroots_c[k].r * nroots_x.i + nroots_c[k].i * nroots_x.r
|
|
|
|
# divide the polynomial by x - a
|
|
|
|
NROOTS_divpoly = (n) ->
|
|
k = 0
|
|
for k in [(n - 1)...0]
|
|
nroots_c[k - 1].r += nroots_c[k].r * nroots_a.r - nroots_c[k].i * nroots_a.i
|
|
nroots_c[k - 1].i += nroots_c[k].i * nroots_a.r + nroots_c[k].r * nroots_a.i
|
|
|
|
if (NROOTS_ABS(nroots_c[0]) > NROOTS_DELTA)
|
|
stop("nroots: residual error")
|
|
|
|
for k in [0...(n - 1)]
|
|
nroots_c[k].r = nroots_c[k + 1].r
|
|
nroots_c[k].i = nroots_c[k + 1].i
|
|
|
|
|