2

我有一个类似于这里的多项式类:Polynomial.java。除了我还没有弄清楚如何找到多项式的零点。我有Jenkins-Traub Algorithm的负责人,但我不知道如何在 Java 中实现它。我确实设法找到了 FORTRAN 版本,但我没有使用 FORTRAN 的经验。是否有演示该算法的开源项目?也许有人可以在这里用伪代码写出来?

4

3 回答 3

2

我已将 Jenkins–Traub 算法的 Fortran 源代码移植到 Java(实系数版本)。它是我的Java DSP 集合的一部分。

于 2013-08-25T21:38:57.667 回答
0

尝试数值方法。他们有一个实现。

参见javadocJenkinsTraubReal Class

于 2012-02-29T07:20:40.697 回答
0

在我求解多项式的过程中,我达到了 5 点:你不能合理地求解 5 次或更高阶的方程。因此,您必须转向数值分析(或其他近似方法),因为您正在离开绝对领域,并且您正在进入“让我们进入这里,也许有炸弹”的情况。

甚至 MathWorld 也将其描述为带有明显嘲笑的“复杂方法”,而其他人则称其为黑盒方法中最好的(这意味着您不知道它是如何工作的,但您认为它是有效的)。

我将在这里发布我的版本,该版本已经过测试并且与小度数的精确求解器(例如四次)相同。

(由// http://hvks.com/Numerical/Downloads/cpoly_ver_1.cpp提供,从 Fortran 翻译成 C)

public class Poly {

  double DBL_EPSILON=2.2204460492503131e-16, DBL_RADIX=2, DBL_MAX=1E+37, DBL_MIN=1E-37;
  double sr, si, are, mre, eta, infin;
  double[] pvri=new double[2], tri=new double[2];
  int nn;
  double[] pr, pi, hr, hi, qpr, qpi, qhr, qhi, shr, shi; 

  public int poly(double[] opr, double[] opi, int degree, double[] zeror, double[] zeroi) {
   int cnt1, cnt2, idnn2, i, conv=0;
   double xx, yy, cosr, sinr, smalno, base, xxx, bnd;
   double[] zri=new double[2];

   base = DBL_RADIX;
   eta = DBL_EPSILON;
   infin = DBL_MAX;
   smalno = DBL_MIN;
   are = eta;
   mre = 2.0 * Math.sqrt( 2.0 ) * eta;
   xx = 0.70710678;
   yy = -xx;
   cosr = -0.060756474;
   sinr = -0.99756405;
   nn = degree;  

   // Algorithm fails if the leading coefficient is zero
   if( opr[ 0 ] == 0 && opi[ 0 ] == 0 )
      return -1;

   // Allocate arrays
   pr = new double [ degree+1 ];
   pi = new double [ degree+1 ];
   hr = new double [ degree+1 ];
   hi = new double [ degree+1 ];
   qpr= new double [ degree+1 ];
   qpi= new double [ degree+1 ];
   qhr= new double [ degree+1 ];
   qhi= new double [ degree+1 ];
   shr= new double [ degree+1 ];
   shi= new double [ degree+1 ];

   // Remove the zeros at the origin if any
   while( opr[ nn ] == 0 && opi[ nn ] == 0 ) {
      idnn2 = degree - nn;
      zeror[ idnn2 ] = 0;
      zeroi[ idnn2 ] = 0;
      nn--;
   }

   // Make a copy of the coefficients
   for( i = 0; i <= nn; i++ ) {
      pr[ i ] = opr[ i ];
      pi[ i ] = opi[ i ];
      shr[ i ] = cmod( pr[ i ], pi[ i ] );
   }

   // Scale the polynomial
   bnd = scale( nn, shr, eta, infin, smalno, base );
   if( bnd != 1 ) {
      for( i = 0; i <= nn; i++ ) {
         pr[ i ] *= bnd;
         pi[ i ] *= bnd;
      }
   }

   do {
   if( nn <= 1 ) {
      double[] cri=cdivid( -pr[ 1 ], -pi[ 1 ], new double[]{pr[ 0 ], pi[ 0 ]});
      zeror[ degree-1 ]=cri[0]; zeroi[ degree-1 ]=cri[1];
      break;
   }

   // Calculate bnd, alower bound on the modulus of the zeros
   for( i = 0; i<= nn; i++ )
      shr[ i ] = cmod( pr[ i ], pi[ i ] );

   bnd=cauchy( nn, shr, shi);

   // Outer loop to control 2 Major passes with different sequences of shifts
   for( cnt1 = 1; cnt1 <= 2; cnt1++ ) {
      // First stage  calculation , no shift
      noshft( 5 );

      // Inner loop to select a shift
      for( cnt2 = 1; cnt2 <= 9; cnt2++ ) {
         // Shift is chosen with modulus bnd and amplitude rotated by 94 degree from the previous shif
         xxx = cosr * xx - sinr * yy;
         yy = sinr * xx + cosr * yy;
         xx = xxx;
         sr = bnd * xx;
         si = bnd * yy;

         // Second stage calculation, fixed shift
         conv=fxshft( 10 * cnt2, zri);
         if( conv==1 ) {
            // The second stage jumps directly to the third stage ieration
            // If successful the zero is stored and the polynomial deflated
            idnn2 = degree - nn;
            zeror[ idnn2 ] = zri[0];
            zeroi[ idnn2 ] = zri[1];
            nn--;
            for( i = 0; i <= nn; i++ ) {
               pr[ i ] = qpr[ i ];
               pi[ i ] = qpi[ i ];
            }
            break;
         }
         // If the iteration is unsuccessful another shift is chosen
         }
      // if 9 shifts fail, the outer loop is repeated with another sequence of shifts
      }
   }
   while(true);

   // The zerofinder has failed on two major passes
   // return empty handed with the number of roots found (less than the original degree)
   degree -= nn;

   return degree;       
  }

// COMPUTES  THE DERIVATIVE  POLYNOMIAL AS THE INITIAL H
// POLYNOMIAL AND COMPUTES L1 NO-SHIFT H POLYNOMIALS.
//
  public void noshft(int l1 ) {
   int i, j, jj, n, nm1;
   double xni, t1, t2;

   n = nn;
   nm1 = n - 1;
   for( i = 0; i < n; i++ ) {
      xni = nn - i;
      hr[ i ] = xni * pr[ i ] / n;
      hi[ i ] = xni * pi[ i ] / n;
   }
   for( jj = 1; jj <= l1; jj++ ) {
      if( cmod( hr[ n - 1 ], hi[ n - 1 ] ) > eta * 10 * cmod( pr[ n - 1 ], pi[ n - 1 ] ) ) {
         double[] tri2=cdivid( -pr[ nn ], -pi[ nn ], new double[]{hr[ n - 1 ], hi[ n - 1 ]});
         tri[0]=tri2[0]; tri[1]=tri2[1];
         for( i = 0; i < nm1; i++ ) {
            j = nn - i - 1;
            t1 = hr[ j - 1 ];
            t2 = hi[ j - 1 ];
            hr[ j ] = tri[0] * t1 - tri[1] * t2 + pr[ j ];
            hi[ j ] = tri[0] * t2 + tri[1] * t1 + pi[ j ];
         }
         hr[ 0 ] = pr[ 0 ];
         hi[ 0 ] = pi[ 0 ];
      }
      else {
         // If the constant term is essentially zero, shift H coefficients
         for( i = 0; i < nm1; i++ ) {
            j = nn - i - 1;
            hr[ j ] = hr[ j - 1 ];
            hi[ j ] = hi[ j - 1 ];
         }
         hr[ 0 ] = 0;
         hi[ 0 ] = 0;
      }
   }
  }

// COMPUTES L2 FIXED-SHIFT H POLYNOMIALS AND TESTS FOR CONVERGENCE.
// INITIATES A VARIABLE-SHIFT ITERATION AND RETURNS WITH THE
// APPROXIMATE ZERO IF SUCCESSFUL.
// L2 - LIMIT OF FIXED SHIFT STEPS
// ZR,ZI - APPROXIMATE ZERO IF CONV IS .TRUE.
// CONV  - LOGICAL INDICATING CONVERGENCE OF STAGE 3 ITERATION
//
  public int fxshft(int l2, double[] zri) {
   int i, j, n, conv=0;
   int test, pasd;
   double otr=0, oti=0, svsr, svsi;

   n = nn;
   polyev( nn, sr, si, pr, pi, qpr, qpi, pvri);
   test = 1;
   pasd = 0;

   // Calculate first T = -P(S)/H(S)
   int bool=calct();

   // Main loop for second stage
   for( j = 1; j <= l2; j++ ) {
      otr = tri[0];
      oti = tri[1];

      // Compute the next H Polynomial and new t
      nexth( bool );
      bool=calct();
      zri[0] = sr + tri[0];
      zri[1] = si + tri[1];

      // Test for convergence unless stage 3 has failed once or this
      // is the last H Polynomial
      if( !( bool==1 || test==0 || j == 12 ) ) {
         if( cmod( tri[0] - otr, tri[1] - oti ) < 0.5 * cmod( zri[0], zri[1] ) ) {
            if( pasd==1 ) {
               // The weak convergence test has been passwed twice, start the third stage
               // Iteration, after saving the current H polynomial and shift
               for( i = 0; i < n; i++ ) {
                  shr[ i ] = hr[ i ];
                  shi[ i ] = hi[ i ];
               }
               svsr = sr;
               svsi = si;
               conv=vrshft( 10, zri);
               if(conv==1) return conv;

               //The iteration failed to converge. Turn off testing and restore h,s,pv and T
               test = 0;
               for( i = 0; i < n; i++ ) {
                  hr[ i ] = shr[ i ];
                  hi[ i ] = shi[ i ];
               }
               sr = svsr;
               si = svsi;
               polyev( nn, sr, si, pr, pi, qpr, qpi, pvri);
               bool=calct();
               continue;
            }
            pasd = 1;
         }
         else
            pasd = 0;
        }
      }

   // Attempt an iteration with final H polynomial from second stage
   conv=vrshft( 10, zri);
   return conv;
  }

// CARRIES OUT THE THIRD STAGE ITERATION.
// L3 - LIMIT OF STEPS IN STAGE 3.
// ZR,ZI   - ON ENTRY CONTAINS THE INITIAL ITERATE, IF THE
//           ITERATION CONVERGES IT CONTAINS THE FINAL ITERATE ON EXIT.
// CONV    -  .TRUE. IF ITERATION CONVERGES
//
  public int vrshft(int l3, double[] zri) {
   int b, bool=0, conv=0;
   int i, j;
   double mp=0, ms, omp=0, relstp=0, r1, r2, tp;

   conv = 0;
   b = 0;
   sr = zri[0];
   si = zri[1];

   // Main loop for stage three
   for( i = 1; i <= l3; i++ ) {
      // Evaluate P at S and test for convergence
      polyev( nn, sr, si, pr, pi, qpr, qpi, pvri);
      mp = cmod( pvri[0], pvri[1] );
      ms = cmod( sr, si );
      if( mp <= 20 * errev( nn, qpr, qpi, ms, mp, are, mre ) ) {
         // Polynomial value is smaller in value than a bound onthe error
         // in evaluationg P, terminate the ietartion
         conv = 1;
         zri[0] = sr;
         zri[1] = si;
         return conv;
      }
      if( i != 1 ) {
         if( !( b==1 || mp < omp || relstp >= 0.05 ) ) {
            // Iteration has stalled. Probably a cluster of zeros. Do 5 fixed 
            // shift steps into the cluster to force one zero to dominate
            tp = relstp;
            b = 1;
            if( relstp < eta ) tp = eta;
            r1 = Math.sqrt( tp );
            r2 = sr * ( 1 + r1 ) - si * r1;
            si = sr * r1 + si * ( 1 + r1 );
            sr = r2;
            polyev( nn, sr, si, pr, pi, qpr, qpi, pvri);
            for( j = 1; j <= 5; j++ ) {
               bool=calct();
               nexth( bool );
            }
            omp = infin;

            bool=calct();
            nexth( bool );
            bool=calct();
            if( bool==0 ) {
              relstp = cmod( tri[0], tri[1] ) / cmod( sr, si );
              sr += tri[0];
              si += tri[1];
            }
            continue;
         }

         // Exit if polynomial value increase significantly
         if( mp *0.1 > omp ) return conv;
      }

      omp = mp;

   }
   return conv;
  }

// COMPUTES  T = -P(S)/H(S).
// BOOL   - LOGICAL, SET TRUE IF H(S) IS ESSENTIALLY ZERO.
  public int calct() {
   int n;
   double[] hvri=new double[2];

   n = nn;

   // evaluate h(s)
   polyev( n - 1, sr, si, hr, hi, qhr, qhi, hvri);
   int bool = cmod( hvri[0], hvri[1] ) <= are * 10 * cmod( hr[ n - 1 ], hi[ n - 1 ] ) ? 1 : 0;
   if( bool==0 ) {
      double[] tri2=cdivid( -pvri[0], -pvri[1], new double[]{hvri[0], hvri[1]});
      tri[0]=tri2[0]; tri[1]=tri2[1];
      return bool;
   }

   tri[0] = 0;
   tri[1] = 0;

   return bool;
  }

// CALCULATES THE NEXT SHIFTED H POLYNOMIAL.
// BOOL   -  LOGICAL, IF .TRUE. H(S) IS ESSENTIALLY ZERO
//
  public void nexth(int bool ) {
   int j, n;
   double t1, t2;

   n = nn;
   if( bool==0 ) {
      for( j = 1; j < n; j++ ) {
         t1 = qhr[ j - 1 ];
         t2 = qhi[ j - 1 ];
         hr[ j ] = tri[0] * t1 - tri[1] * t2 + qpr[ j ];
         hi[ j ] = tri[0] * t2 + tri[1] * t1 + qpi[ j ];
      }
      hr[ 0 ] = qpr[ 0 ];
      hi[ 0 ] = qpi[ 0 ];
      return;
   }

   // If h[s] is zero replace H with qh
   for( j = 1; j < n; j++ ) {
      hr[ j ] = qhr[ j - 1 ];
      hi[ j ] = qhi[ j - 1 ];
   }
   hr[ 0 ] = 0;
   hi[ 0 ] = 0;
  }

// EVALUATES A POLYNOMIAL  P  AT  S  BY THE HORNER RECURRENCE
// PLACING THE PARTIAL SUMS IN Q AND THE COMPUTED VALUE IN PV.
//  
  public void polyev(int nn, double sr, double si, double pr[], double pi[], double qr[], double qi[], double[] pvri) {
   int i;
   double t;

   qr[ 0 ] = pr[ 0 ];
   qi[ 0 ] = pi[ 0 ];
   pvri[0] = qr[ 0 ];
   pvri[1] = qi[ 0 ];

   for( i = 1; i <= nn; i++ ) {
      t = ( pvri[0] ) * sr - ( pvri[1] ) * si + pr[ i ];
      pvri[1] = ( pvri[0] ) * si + ( pvri[1] ) * sr + pi[ i ];
      pvri[0] = t;
      qr[ i ] = pvri[0];
      qi[ i ] = pvri[1];
   }
  }

// BOUNDS THE ERROR IN EVALUATING THE POLYNOMIAL BY THE HORNER RECURRENCE.
// QR,QI - THE PARTIAL SUMS
// MS    -MODULUS OF THE POINT
// MP    -MODULUS OF POLYNOMIAL VALUE
// ARE, MRE -ERROR BOUNDS ON COMPLEX ADDITION AND MULTIPLICATION
//
  public double errev(int nn, double qr[], double qi[], double ms, double mp, double are, double mre ) {
   int i;
   double e;

   e = cmod( qr[ 0 ], qi[ 0 ] ) * mre / ( are + mre );
   for( i = 0; i <= nn; i++ )
      e = e * ms + cmod( qr[ i ], qi[ i ] );

   return e * ( are + mre ) - mp * mre;
  }

// CAUCHY COMPUTES A LOWER BOUND ON THE MODULI OF THE ZEROS OF A
// POLYNOMIAL - PT IS THE MODULUS OF THE COEFFICIENTS.
//
  public double cauchy(int nn, double[] pt, double[] q) {
   int i, n;
   double x, xm, f, dx, df;

   pt[ nn ] = -pt[ nn ];

   // Compute upper estimate bound
   n = nn;
   x = Math.exp( Math.log( -pt[ nn ] ) - Math.log( pt[ 0 ] ) ) / n;
   if( pt[ n - 1 ] != 0 ) {
      // Newton step at the origin is better, use it
      xm = -pt[ nn ] / pt[ n - 1 ];
      if( xm < x ) x = xm;
    }

   // Chop the interval (0,x) until f < 0
   while(true) {
      xm = x * 0.1;
      f = pt[ 0 ];
      for( i = 1; i <= nn; i++ )
         f = f * xm + pt[ i ];
      if( f <= 0 )
         break;
      x = xm;
   }
   dx = x;

   // Do Newton iteration until x converges to two decimal places
   while( Math.abs( dx / x ) > 0.005 ) {
      q[ 0 ] = pt[ 0 ];
      for( i = 1; i <= nn; i++ )
         q[ i ] = q[ i - 1 ] * x + pt[ i ];
      f = q[ nn ];
      df = q[ 0 ];
      for( i = 1; i < n; i++ )
         df = df * x + q[ i ];
      dx = f / df;
      x -= dx;
   }

   return x;
  }

// RETURNS A SCALE FACTOR TO MULTIPLY THE COEFFICIENTS OF THE POLYNOMIAL.
// THE SCALING IS DONE TO AVOID OVERFLOW AND TO AVOID UNDETECTED UNDERFLOW
// INTERFERING WITH THE CONVERGENCE CRITERION.  THE FACTOR IS A POWER OF THE
// BASE.
// PT - MODULUS OF COEFFICIENTS OF P
// ETA, INFIN, SMALNO, BASE - CONSTANTS DESCRIBING THE FLOATING POINT ARITHMETIC.
//
  public double scale(int nn, double pt[], double eta, double infin, double smalno, double base ) {
   int i, l;
   double hi, lo, max, min, x, sc;
   double fn_val;

   // Find largest and smallest moduli of coefficients
   hi = Math.sqrt( infin );
   lo = smalno / eta;
   max = 0;
   min = infin;

   for( i = 0; i <= nn; i++ ) {
      x = pt[ i ];
      if( x > max ) max = x;
      if( x != 0 && x < min ) min = x;
   }

   // Scale only if there are very large or very small components
   fn_val = 1;
   if( min >= lo && max <= hi ) return fn_val;
   x = lo / min;
   if( x <= 1 )
      sc = 1 / ( Math.sqrt( max )* Math.sqrt( min ) );
   else {
      sc = x;
      if( infin / sc > max ) sc = 1;
   }
   l = (int)( Math.log( sc ) / Math.log(base ) + 0.5 );
   fn_val = Math.pow( base , l );
   return fn_val;
  }

// COMPLEX DIVISION C = A/B, AVOIDING OVERFLOW.
//
  public double[] cdivid(double ar, double ai, double[] bri) {
   double r, d, t, infin;
   double[] cri=new double[2];

   if( bri[0] == 0 && bri[1] == 0 ) {
      // Division by zero, c = infinity
      double[] mc=mcon();
      t=mc[1]; infin=mc[2]; t=mc[3]; t=mc[0];
      cri[0] = infin;
      cri[1] = infin;
      return cri;
   }

   if( Math.abs( bri[0] ) < Math.abs( bri[1] ) ) {
      r = bri[0]/ bri[1];
      d = bri[1] + r * bri[0];
      cri[0] = ( ar * r + ai ) / d;
      cri[1] = ( ai * r - ar ) / d;
      return cri;
   }

   r = bri[1] / bri[0];
   d = bri[0] + r * bri[1];
   cri[0] = ( ar + ai * r ) / d;
   cri[1] = ( ai - ar * r ) / d;

   return cri;
  }

// MODULUS OF A COMPLEX NUMBER AVOIDING OVERFLOW.
//
  public double cmod( double r, double i ) {
   double ar, ai;

   ar = Math.abs( r );
   ai = Math.abs( i );
   if( ar < ai )
      return ai * Math.sqrt( 1.0 + Math.pow( ( ar / ai ), 2.0 ) );

   if( ar > ai )
      return ar * Math.sqrt( 1.0 + Math.pow( ( ai / ar ), 2.0 ) );

   return ar * Math.sqrt( 2.0 );
  }

// MCON PROVIDES MACHINE CONSTANTS USED IN VARIOUS PARTS OF THE PROGRAM.
// THE USER MAY EITHER SET THEM DIRECTLY OR USE THE STATEMENTS BELOW TO
// COMPUTE THEM. THE MEANING OF THE FOUR CONSTANTS ARE -
// ETA       THE MAXIMUM RELATIVE REPRESENTATION ERROR WHICH CAN BE DESCRIBED
//           AS THE SMALLEST POSITIVE FLOATING-POINT NUMBER SUCH THAT
//           1.0_dp + ETA &gt; 1.0.
// INFINY    THE LARGEST FLOATING-POINT NUMBER
// SMALNO    THE SMALLEST POSITIVE FLOATING-POINT NUMBER
// BASE      THE BASE OF THE FLOATING-POINT NUMBER SYSTEM USED
//
  public double[] mcon() {
   return new double[]{DBL_RADIX, Double.MIN_VALUE, Double.MAX_VALUE, Double.MIN_VALUE};
  }

}
于 2019-11-15T13:49:52.190 回答