diff --git a/math.scad b/math.scad index 73659ca..4db010d 100644 --- a/math.scad +++ b/math.scad @@ -1631,8 +1631,6 @@ function c_ident(n) = [for (i = [0:1:n-1]) [for (j = [0:1:n-1]) (i==j)?[1,0]:[0, // Compute the norm of a complex number or vector. function c_norm(z) = norm_fro(z); - - // Section: Polynomials // Function: quadratic_roots() @@ -1840,12 +1838,19 @@ function _poly_roots(p, pderiv, s, z, tol, i=0) = // parts are zero. You can specify eps, in which case the test is // z.y/(1+norm(z)) < eps. Because // of poor convergence and higher error for repeated roots, such roots may -// be missed by the algorithm because their imaginary part is large. +// be missed by the algorithm because their imaginary part is large. // Arguments: // p = polynomial to solve as coefficient list, highest power term first // eps = used to determine whether imaginary parts of roots are zero // tol = tolerance for the complex polynomial root finder +// The algorithm is based on Brent's method and is a combination of +// bisection and inverse quadratic approximation, where bisection occurs +// at every step, with refinement using inverse quadratic approximation +// only when that approximation gives a good result. The detail +// of how to decide when to use the quadratic came from an article +// by Crenshaw on "The World's Best Root Finder". +// https://www.embedded.com/worlds-best-root-finder/ function real_roots(p,eps=undef,tol=1e-14) = assert( is_vector(p), "Invalid polynomial." ) let( p = _poly_trim(p,eps=0) ) @@ -1859,4 +1864,76 @@ function real_roots(p,eps=undef,tol=1e-14) = ? [for(z=roots) if (abs(z.y)/(1+norm(z))