/* * 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 #include 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)=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)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; }