Recent posts on quadratics and root finding, especially in a
demonstrational manner, provoked me to post the following.  It's
rather long and demonstrates techniques of another programming
language – a smooth mix of symbolic and numeric computations.  The
reason I am posting is that it seems rather useful to me but I don't
believe to have seen this kind of treatment of the subject in the J
literature or the forums.  Perhaps someone will be provoked, in turn,
to search for a way of doing something similar in J.


Define a quadratic polynomial:

   q0 = 2*x^2+(-3)*x+1;

It can be simplified using the Horner's rule, ax²+bx+c ≡ (ax+b)x+c:

   q1 = reduce q0 with a*x^2+b*x+c = (a*x+b)*x+c end;

It is worth defining the rule separately, as a transforming function
applicable to any quadratic polynomial and not just q0:

   qhorner = reduce with a*x^2+b*x+c = (a*x+b)*x+c end;

Now the definition of q1 is:

   q1 = qhorner q0;

Let's check what we have so far (comments after // show the output):

   q0;     // 2*x^2+(-3)*x+1
   q1;     // (2*x+-3)*x+1

(Both these are symbolic expressions with x a variable.)

As promised, qhorner can be applied to other quadratics, including
ones whose coefficients are expressions:

   q2 = qhorner ((R+S)*x^2+(T+4)*x+W);
   q2;     //  ((R+S)*x+(T+4))*x+W

Now let's define a rule that substitutes whatever we would wish
for the 'x' in an expression (quadratic or other):

   subst e t = reduce e with x = t end;

subst can be used to evaluate an expression to a number, but that
is only one of its uses.  Check how it works:

   subst q0 5;   // 36.0
   subst q1 5;   // 36
   subst q2 5;   // ((R+S)*5+(T+4))*5+W

Expressions also can be substituted for x:

   q01 = subst q0 (5*u+7);
   q11 = subst q1 (5*u+7);
   q21 = subst q2 (5*u+7);

Checking these definitions:

   q01;   // 2*(5*u+7)^2+(-3)*(5*u+7)+1
   q11;   // (2*(5*u+7)+-3)*(5*u+7)+1
   q21;   // ((R+S)*(5*u+7)+(T+4))*(5*u+7)+W

Any of these can be simplified (or the opposite) by substituting for
the 'u'.  Here is how this works with u = -2:

   reduce q01 with u = -2 end;    // 28.0
   reduce q11 with u = -2 end;    // 28
   reduce q21 with u = -2 end;    // ((R+S)*(-3)+(T+4))*(-3)+W

And now let's devise a new transforming function, which finds the
roots of a quadratic:

   roots = reduce with a*x^2+b*x+c = a*(x-x1)*(x-x2)
                       when d = sqrt (b^2-4*a*c);
                            x1 = (-b-d)/(2*a);
                            x2 = (-b+d)/(2*a) end end;

Using roots we obtain:

   roots q0;    // 2*(x-0.5)*(x-1.0)

Again, we can evaluate by substituting for the 'x':

   subst q0 1;  // 0.0
   subst q0 5;  // 36.0

Time for generalization!  We shall use the Horner's rule to transform
and evaluate polynomials of arbitrary degree, not just quadratic ones.
First, define the core operation:

   horner v c = v*x+c;

Next, a function that, given a list of coefficients, creates a
polynomial in x (foldl1 is similar to J's '/' but works from left
to right):

   poly cs = foldl1 horner cs;

Worth noting is that it is due to the horner function that the
polynomials created by poly are defined in x.

Now, e.g.,

   p0 = poly [a,b,c];
   p0;    // (a*x+b)*x+c

is the 'hornered' version of a general quadratic polynomial, and

   p1 = poly [2,-3,1];
   p1;    // (2*x+-3)*x+1

is the same as q1 above.

But we can indeed define polynomials of higher degrees, e.g.:

   p2 = poly [1,-2,3,5];
   p2;    // ((1*x+-2)*x+3)*x+5

representing a 'hornered' version of x³-2x²+3x+5.

Let's do some evaluations, again using subst from above:

   subst p0 5;    // (a*5+b)*5+c
   subst p1 5;    // 36
   subst p2 5;    // 95

We may wish to tabulate a polynomial over lists of arguments.  Here is
a helper function to simplify this:

   tab p args = map (subst p) args;

Thus:

   tab p1 (-5..5);    // [66,45,28,15,6,1,0,3,10,21,36]
   tab p2 (-5..5);    // [-185,-103,-49,-17,-1,5,7,11,23,49,95]

Actually, we can do more than what poly does.  With the following
definition:

   polyhorn cs = init p, last p
                 when p = scanl1 horner cs end;

the Horner's rule is fully applied, because the partial results from
applying it along the list of coefficients cs are collected in a list
rather than thrown out as with poly (which is due to replacing foldl1
with scanl1).  Thus, e.g.:

   polyhorn [a,b,c];      // [a,a*x+b],(a*x+b)*x+c

or

   polyhorn [a,b,c,d];    // [a,a*x+b,(a*x+b)*x+c],((a*x+b)*x+c)*x+d

In general, if cs is a list of coefficients representing a polynomial
p and v is any numeric value, then the expression

   subst (polyhorn cs) v

is a pair of values: a list of coefficients representing a polynomial
q, and a number r=p(v), such that  p(x) = (x-v).q(x)+r  holds.  In other
words, we can now divide a polynomial (p) by a linear binomial (x-v)
obtaining a quotient (q) and a remainder r, the latter being the value
of p at v.  For example,

   subst (polyhorn [1,-2,3,5]) 5;    // [1,3,18],95

shows that  x³-2x²+3x+5 = (x-5)*(x²+3x+18)+95,  and that, in
particular,  (x³-2x²+3x+5)(5) is 95.
----------------------------------------------------------------------
For information about J forums see http://www.jsoftware.com/forums.htm

Reply via email to