📄 sturm.c
字号:
/* * sturm.c * * the functions to build and evaluate the Sturm sequence */#include <math.h>#include <stdio.h>#include "solve.h"/* * modp * * calculates the modulus of u(x) / v(x) leaving it in r, it * returns 0 if r(x) is a constant. * note: this function assumes the leading coefficient of v * is 1 or -1 */static intmodp(u, v, r) poly *u, *v, *r;{ int k, j; double *nr, *end, *uc; nr = r->coef; end = &u->coef[u->ord]; uc = u->coef; while (uc <= end) *nr++ = *uc++; if (v->coef[v->ord] < 0.0) { for (k = u->ord - v->ord - 1; k >= 0; k -= 2) r->coef[k] = -r->coef[k]; for (k = u->ord - v->ord; k >= 0; k--) for (j = v->ord + k - 1; j >= k; j--) r->coef[j] = -r->coef[j] - r->coef[v->ord + k] * v->coef[j - k]; } else { for (k = u->ord - v->ord; k >= 0; k--) for (j = v->ord + k - 1; j >= k; j--) r->coef[j] -= r->coef[v->ord + k] * v->coef[j - k]; } k = v->ord - 1; while (k >= 0 && fabs(r->coef[k]) < SMALL_ENOUGH) { r->coef[k] = 0.0; k--; } r->ord = (k < 0) ? 0 : k; return(r->ord);}/* * buildsturm * * build up a sturm sequence for a polynomial in smat, returning * the number of polynomials in the sequence */intbuildsturm(ord, sseq) int ord; poly *sseq;{ int i; double f, *fp, *fc; poly *sp; sseq[0].ord = ord; sseq[1].ord = ord - 1; /* * calculate the derivative and normalise the leading * coefficient. */ f = fabs(sseq[0].coef[ord] * ord); fp = sseq[1].coef; fc = sseq[0].coef + 1; for (i = 1; i <= ord; i++) *fp++ = *fc++ * i / f; /* * construct the rest of the Sturm sequence */ for (sp = sseq + 2; modp(sp - 2, sp - 1, sp); sp++) { /* * reverse the sign and normalise */ f = -fabs(sp->coef[sp->ord]); for (fp = &sp->coef[sp->ord]; fp >= sp->coef; fp--) *fp /= f; } sp->coef[0] = -sp->coef[0]; /* reverse the sign */ return(sp - sseq);}/* * numroots * * return the number of distinct real roots of the polynomial * described in sseq. */intnumroots(np, sseq, atneg, atpos) int np; poly *sseq; int *atneg, *atpos;{ int atposinf, atneginf; poly *s; double f, lf; atposinf = atneginf = 0; /* * changes at positive infinity */ lf = sseq[0].coef[sseq[0].ord]; for (s = sseq + 1; s <= sseq + np; s++) { f = s->coef[s->ord]; if (lf == 0.0 || lf * f < 0) atposinf++; lf = f; } /* * changes at negative infinity */ if (sseq[0].ord & 1) lf = -sseq[0].coef[sseq[0].ord]; else lf = sseq[0].coef[sseq[0].ord]; for (s = sseq + 1; s <= sseq + np; s++) { if (s->ord & 1) f = -s->coef[s->ord]; else f = s->coef[s->ord]; if (lf == 0.0 || lf * f < 0) atneginf++; lf = f; } *atneg = atneginf; *atpos = atposinf; return(atneginf - atposinf);}/* * numchanges * * return the number of sign changes in the Sturm sequence in * sseq at the value a. */intnumchanges(np, sseq, a) int np; poly *sseq; double a;{ int changes; double f, lf; poly *s; changes = 0; lf = evalpoly(sseq[0].ord, sseq[0].coef, a); for (s = sseq + 1; s <= sseq + np; s++) { f = evalpoly(s->ord, s->coef, a); if (lf == 0.0 || lf * f < 0) changes++; lf = f; } return(changes);}/* * sbisect * * uses a bisection based on the sturm sequence for the polynomial * described in sseq to isolate intervals in which roots occur, * the roots are returned in the roots array in order of magnitude. */sbisect(np, sseq, min, max, atmin, atmax, roots) int np; poly *sseq; double min, max; int atmin, atmax; double *roots;{ double mid; int n1 = 0, n2 = 0, its, atmid, nroot; if ((nroot = atmin - atmax) == 1) { /* * first try a less expensive technique. */ if (modrf(sseq->ord, sseq->coef, min, max, &roots[0])) return; /* * if we get here we have to evaluate the root the hard * way by using the Sturm sequence. */ for (its = 0; its < MAXIT; its++) { mid = (min + max) / 2; atmid = numchanges(np, sseq, mid); if (fabs(mid) > RELERROR) { if (fabs((max - min) / mid) < RELERROR) { roots[0] = mid; return; } } else if (fabs(max - min) < RELERROR) { roots[0] = mid; return; } if ((atmin - atmid) == 0) min = mid; else max = mid; } if (its == MAXIT) { fprintf(stderr, "sbisect: overflow min %f max %f\ diff %e nroot %d n1 %d n2 %d\n", min, max, max - min, nroot, n1, n2); roots[0] = mid; } return; } /* * more than one root in the interval, we have to bisect... */ for (its = 0; its < MAXIT; its++) { mid = (min + max) / 2; atmid = numchanges(np, sseq, mid); n1 = atmin - atmid; n2 = atmid - atmax; if (n1 != 0 && n2 != 0) { sbisect(np, sseq, min, mid, atmin, atmid, roots); sbisect(np, sseq, mid, max, atmid, atmax, &roots[n1]); break; } if (n1 == 0) min = mid; else max = mid; } if (its == MAXIT) { fprintf(stderr, "sbisect: roots too close together\n"); fprintf(stderr, "sbisect: overflow min %f max %f diff %e\ nroot %d n1 %d n2 %d\n", min, max, max - min, nroot, n1, n2); for (n1 = atmax; n1 < atmin; n1++) roots[n1 - atmax] = mid; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -