Plan 9 from Bell Labs’s /usr/web/sources/extra/art/root.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


/*
 * Use Sturm sequences to isolate polynomial roots.
 * A Sturm sequence for a polynomial a(x) is any sequence of polynomials
 * a[0](x),...,a[n](x) with
 *	a[0](x)=a(x)
 *	a[1](x)=a'(x)
 *	a[i+2](x)=(a[i+1](x)*q[i+1](x)-b[i]*a[i](x))/c[i]
 * where b[i] and c[i] are positive real numbers, and degree(a[n](x))==0.
 *
 * Note:
 *	a[i+2](x)=-a[i](x) mod a[i+1](x)
 * (where p(x) mod q(x) is the remainder after `synthetic division')
 * works just fine and minimizes n.
 *
 * Now, we can define
 *	schange(a, x)
 * to be the number of times that the sign of the sequence a[i](x) changes from +
 * to - or vice versa (ignoring zero terms.)
 * Sturm's theorem is that if a(x) is square-free, the number of real zeros
 * of a(x) on the interval (x0,x1] is schange(a, x0)-schange(a, x1).
 */
#include "art.h"
#define	DEGREE	5
Flt rem[DEGREE+1][DEGREE+1];
int Drem[DEGREE+1];
int nrem;
Flt quo[DEGREE+1][DEGREE+1];
int Dquo[DEGREE+1];
Flt evalrem(), evalquo();
Flt lastroot=-1;
int simple;
int nroot, nhard;
#include <sys/types.h>
#include <sys/times.h>
time_t treg;
struct tms areg, breg;
/*
 * Find the all roots of a(x) on [0,1].
 * Return number of roots.
 * Method:
 *	1) compute a Sturm sequence for a(x)
 *	2) use Newton iteration starting at the root saved from the last
 *	   invocation to find a root.  The Sturm sequence can verify that
 *	   it's the correct root.
 *	3) If that doesn't get the root, use the Sturm sequence to direct
 *	   binary subdivision to isolate the smallest root on the interval.
 *	4) Having bracketed the root, use a hybrid Newton/Regula Falsi iteration
 *	   to home in on the root.
 */
Flt *root;
int roots(Flt a[], int adeg, Flt r[]){
	Flt x0, x1, x, y0, y1, y, d;
	register n0, n1, n;
	/*
	 * get the Sturm sequence of a
	 */
	for(n=0;n<=adeg;n++) rem[0][n]=a[n];
	Drem[0]=adeg;
	for(;;){
		for(n=1;n<=Drem[0];n++)
			rem[1][n-1]=n*rem[0][n];
		Drem[1]=Drem[0]-1;
		simple=1;
		for(nrem=1;Drem[nrem]!=0;nrem++){
			polydivnrem(rem[nrem-1], Drem[nrem-1],
				rem[nrem], Drem[nrem],
				quo[nrem], &Dquo[nrem],
				rem[nrem+1], &Drem[nrem+1]);
			if(Dquo[nrem]!=1) simple=0;
		}
		/*
		 * If the rem[n] is zero, a is not square-free, and
		 * rem[n-1] is gcd(a, a').  In that case, dividing
		 * rem[0] by rem[n-1] will reduce the multiplicities
		 * of all roots to 1, and we can go back and try again.
		 */
		if(rem[nrem][0]!=0) break;
		Drem[0]=polydiv(rem[0], Drem[0], rem[nrem-1], Drem[nrem-1],
				rem[0]);
	}
	/*
	 * Isolate all roots between 0 and 1
	 */
	root=r;
	isolate(0., schange(0.), 1., schange(1.));
	return root-r;
}
void isolate(Flt x0, int n0, Flt x1, int n1){
	Flt x;
	int n;
	while(n0-n1>1){
		x=.5*(x0+x1);
		n=schange(x);
		isolate(x0, n0, x, n);
		x0=x;
		n0=n;
	}
	if(n0==n1) return;
	/*
	 * iterate to the root
	 */
	y0=evalrem(0, x0);
	if(fabs(y0)<EPS) return;	/* this root counted in other subdivision */
	y1=evalrem(0, x1);
	if(fabs(y1)<EPS){
		*root++=x1;
		return;
	}
	if(y0*y1>=0){
		msg("Root not bracketed x0=%g, y0=%g, x1=%g, y1=%g", x0, y0, x1, y1);
		return;
	}
	/*
	 * Pegasus iteration
	 */
	x=x0;
	y=y0;
	while(fabs(x1-x0)>EPS){
		x=x0-y0*(x1-x0)/(y1-y0);
		y=evalrem(0, x);
		if(fabs(y)<EPS){
			*root++=x;
			return;
		}
		if(y*y0<=0.){
			x1=x0;
			y1=y0;
		}
		else
			y1 *= y0/(y0+y);
		x0=x;
		y0=y;
	}
	*root++=x0-y0*(x1-x0)/(y1-y0);
}
/*
 * r=-a mod b
 * q=a/b
 */
polydivnrem(a, adeg, b, bdeg, q, qdeg, r, rdeg)
Flt a[], b[], q[], r[];
int *rdeg, *qdeg;
{
	register Flt *rp, *erp;
	polydivrem(a, adeg, b, bdeg, q, qdeg, r, rdeg);
	erp=&r[*rdeg];
	for(rp=r;rp<=erp;rp++) *rp=-*rp;
}
/*
 * q=a/b
 */
polydiv(a, adeg, b, bdeg, q)
Flt a[], b[], q[];
{
	Flt r[DEGREE+1], tq[DEGREE+1];
	int rdeg, qdeg;
	register Flt *qp, *tqp, *eqp;
	polydivrem(a, adeg, b, bdeg, tq, &qdeg, r, &rdeg);
	eqp=&q[qdeg];
	for(qp=q,tqp=tq;qp<=eqp;) *qp++=*tqp++;
	return qdeg;
}
/*
 * polynomial division of a by b.
 * quotient in q, remainder in r.
 */
polydivrem(a, adeg, b, bdeg, q, qdeg, r, rdeg)
Flt a[], b[], q[], r[];
int adeg, bdeg, *qdeg, *rdeg;
{
	Flt quot;
	register i, qterm, rterm;
	while(adeg>0 && a[adeg]==0.) --adeg;
	while(bdeg>0 && b[bdeg]==0.) --bdeg;
	for(i=0;i<=adeg;i++) r[i]=a[i];
	rterm=adeg;
	qterm=adeg-bdeg;
	*qdeg=qterm;
	while(rterm>=bdeg){
		quot=q[qterm]=r[rterm]/b[bdeg];
		for(i=0;i<=bdeg;i++)
			r[qterm+i]-=b[i]*quot;
		--qterm;
		--rterm;
		while(rterm>0 && r[rterm]==0) --rterm;
	}
	*rdeg=rterm;
}
ppoly(poly, n)
Flt *poly;
{
	register plus=0, i;
	for(i=n;i>=0;--i) if(poly[i]!=0){
		if(plus){
			if(poly[i]>=0) putchar('+');
		}
		else plus++;
		if(poly[i]==-1 && i!=0) putchar('-');
		else if(poly[i]!=1 || i==0) printf("%.20g", poly[i]);
		switch(i){
		default: printf("x^%d", i); break;
		case 1: putchar('x'); break;
		case 0: break;
		}
	}
	if(!plus) putchar('0');
	putchar('\n');
}
/*
 * Use Horner's rule to evaluate rem[n] at x
 */
Flt evalrem(n, x)
Flt x;
{
	register Flt *d, *d0=&rem[n][0];
	register Flt f=0;
	for(d=d0+Drem[n];d>=d0;--d) f=f*x+*d;
	return f;
}
/*
 * Use Horner's rule to evaluate quo[n] at x
 */
Flt evalquo(n, x)
Flt x;
{
	register Flt *d, *d0=&quo[n][0];
	register Flt f=0;
	for(d=d0+Dquo[n];d>=d0;--d) f=f*x+*d;
	return f;
}
/*
 * Count Sturm sequence sign changes at x
 */
schange(x)
Flt x;
{
	register i, n=0;
	register Flt rip1, ri, rim1;
	rip1=evalrem(nrem, x);
	ri=evalrem(nrem-1, x);
	if(simple){
		for(i=nrem-1;i!=0;--i){
			if(rip1!=0) if(rip1<0?ri>0:ri<0) n++;
			rim1=ri*(quo[i][0]+x*quo[i][1])-rip1;
			rip1=ri;
			ri=rim1;
		}
	}
	else{
		for(i=nrem-1;i!=0;--i){
			if(rip1!=0) if(rip1<0?ri>0:ri<0) n++;
			rim1=ri*evalquo(i, x)-rip1;
			rip1=ri;
			ri=rim1;
		}
	}
	if(rip1!=0) if(rip1<0?ri>0:ri<0) n++;
	return n;
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].