简体   繁体   中英

Laguerre's method to obtain poly roots (Matlab)

I must write using Laguerre's method a piece of code to find the real and complex roots of poly:

P=X^5-5*X^4-6*X^3+6*X^2-3*X+1

I have little doubt. I did the algorithm in the matlab, but 3 out of 5 roots are the same and I don't think that is correct.

syms X                     %Declearing x as a variabl
P=X^5-5*X^4-6*X^3+6*X^2-3*X+1;    %Equation we interest to solve
n=5;                        % The eq. order
Pd1 = diff(P,X,1);          % first differitial of f
Pd2 = diff(P,X,2);          %second differitial of f
err=0.00001;                  %Answear tollerance

N=100;                      %Max. # of Iterations
x(1)=1e-3;                  % Initial Value
for k=1:N
    G=double(vpa(subs(Pd1,X,x(k))/subs(P,X,x(k))));
    H=G^2 - double(subs(Pd2,X,x(k))) /subs(P,X,x(k));
    D1= (G+sqrt((n-1)*(n*H-G^2)));
    D2= (G-sqrt((n-1)*(n*H-G^2)));
    D = max(D1,D2);
    a=n/D;
    x(k+1)=x(k)-a   
    Err(k) = abs(x(k+1)-x(k));
    if Err(k) <=err
        break
    end
end

output (roots of polynomial):

x =

0.0010 + 0.0000i 0.1434 + 0.4661i 0.1474 + 0.4345i 0.1474 + 0.4345i 0.1474 + 0.4345i

What you actually see are all the values x(k) which arose in the loop. The last one, 0.1474 + 0.4345i is the end result of this loop - the approximation of the root which is in your given tolerance threshold. The code

syms X                                                  %Declaring x as a variable
P = X^5 - 5 * X^4 - 6 * X^3 + 6 * X^2 - 3 * X + 1;      %Polynomial
n=5;                                                    %Degree of the polynomial
Pd1 = diff(P,X,1);                                      %First derivative of P
Pd2 = diff(P,X,2);                                      %Second derivative of P
err = 0.00001;                                          %Answer tolerance

N = 100;                                                  %Maximal number of iterations
x(1) = 0;                                               %Initial value
for k = 1:N
    G = double(vpa(subs(Pd1,X,x(k)) / subs(P,X,x(k))));
    H = G^2 - double(subs(Pd2,X,x(k))) / subs(P,X,x(k));
    D1 = (G + sqrt((n-1) * (n * H-G^2)));
    D2 = (G - sqrt((n-1) * (n * H-G^2)));
    D = max(D1,D2);
    a = n/D;
    x(k+1) = x(k) - a;   
    Err(k) = abs(x(k+1)-x(k));
    if Err(k) <=err
        fprintf('Initial value %f, result %f%+fi', x(1), real(x(k)), imag(x(k)))
        break
    end
end

results in

Initial value -2.000000, result -1.649100+0.000000i

If you want to get other roots, you have to use other initial values. For example one can obtain

Initial value 10.000000, result 5.862900+0.000000i
Initial value -2.000000, result -1.649100+0.000000i
Initial value 3.000000, result 0.491300+0.000000i
Initial value 0.000000, result 0.147400+0.434500i
Initial value 1.000000, result 0.147400-0.434500i

These are all zeros of the polynomial.

A method for calculating the next root when you have found another one would be that you divide through the corresponding linear factor and use your loop for the resulting new polynomial. Note that this is in general not very easy to handle since rounding errors can have a big influence on the result.

Problems with the existing code

You do not implement the Laguerre method properly as a method in complex numbers. The denominator candidates D1,D2 are in general complex numbers, it is inadvisable to use the simple max which only has sensible results for real inputs. The aim is to have a=n/D be the smaller of both variants, so that one has to look for the D in [D1,D2] with the larger absolute value. If there were a conditional assignment as in C, this would look like

D = (abs(D_1)>abs(D2)) ? D1 : D2;

As that does not exist, one has to use commands with a similar result

D = D1; if (abs(D_1)<abs(D2)) D=D2; end

The resulting sequence of approximation points is

x(0) = 0.0010000
x(1) = 0.143349512707684+0.466072958423667i
x(2) = 0.164462212064089+0.461399841949893i
x(3) = 0.164466373475316+0.461405404094130i

There is a point where one can not expect the (residual) polynomial value at the root approximation to substantially decrease. The value close to zero is obtained by adding and subtracting rather large terms in the sum expression of the polynomial. The accuracy lost in these catastrophic cancellation events can not be recovered.

The threshold for polynomial values that are effectively zero can be estimated as the machine constant of the double type times the polynomial value where all coefficients and the evaluation point are replaced by their absolute values. This test serves in the code primarily to avoid divisions by zero or near-zero.

Finding all roots

One approach is to apply the method to a sufficiently large number of initial points along some circle containing all the roots, with some strict rules for early termination at too slow convergence. One would have to make the list of the roots found unique, but keep the multiplicity,...

The other standard method is to apply deflation, that is, divide out the linear factor of the root found. This works well in low degrees.

There is no need for the slower symbolic operations as there are functions that work directly on the coefficient array, such as polyval and polyder . Deflation by division with remainder can be achieved using the deconv function.

For real polynomials, we know that the complex conjugate of a root is also a root. Thus initialize the next iteration with the deflated polynomial with it.

Other points:

  • There is no point in the double conversions as at no point there is a conversion into the single type.
  • If you don't do anything with it, it makes no sense to create an array, especially not for Err .

Roots of the example

Implementing all this I get a log of

x(0) = 0.001000000000000+0.000000000000000i,  |Pn(x(0))| =  0.99701
x(1) = 0.143349512707684+0.466072958423667i, |dx|= 0.48733
x(2) = 0.164462212064089+0.461399841949893i, |dx|=0.021624
x(3) = 0.164466373475316+0.461405404094130i, |dx|=6.9466e-06
root found x=0.164466373475316+0.461405404094130i with value P0(x)=-2.22045e-16+9.4369e-16i
Deflation
x(0) = 0.164466373475316-0.461405404094130i,  |Pn(x(0))| = 2.1211e-15
root found x=0.164466373475316-0.461405404094130i with value P0(x)=-2.22045e-16-9.4369e-16i
Deflation
x(0) = 0.164466373475316+0.461405404094130i,  |Pn(x(0))| =   4.7452
x(1) = 0.586360702193454+0.016571894375927i, |dx|= 0.61308
x(2) = 0.562204173408499+0.000003168181059i, |dx|=0.029293
x(3) = 0.562204925474889+0.000000000000000i, |dx|=3.2562e-06
root found x=0.562204925474889+0.000000000000000i with value P0(x)=2.22045e-16-1.33554e-17i
Deflation
x(0) = 0.562204925474889-0.000000000000000i,  |Pn(x(0))| =   7.7204
x(1) = 3.332994579372812-0.000000000000000i, |dx|=  2.7708
root found x=3.332994579372812-0.000000000000000i with value P0(x)=6.39488e-14-3.52284e-15i
Deflation
x(0) = 3.332994579372812+0.000000000000000i,  |Pn(x(0))| =   5.5571
x(1) = -2.224132251798332+0.000000000000000i, |dx|=  5.5571
root found x=-2.224132251798332+0.000000000000000i with value P0(x)=-3.33067e-14+1.6178e-15i

for the modified code

P = [1, -2, -6, 6, -3, 1];
P0 = P;

deg=length(P)-1;           % The eq. degree
err=1e-05;                 %Answer tolerance

N=10;                      %Max. # of Iterations
x=1e-3;                    % Initial Value
for n=deg:-1:1
  dP = polyder(P);          % first derivative of P
  d2P = polyder(dP);        %second derivative of P
  fprintf("x(0) = %.15f%+.15fi,  |Pn(x(0))| = %8.5g\n",  real(x),imag(x), abs(polyval(P,x)));
  for k=1:N
    Px = polyval(P,x);
    dPx = polyval(dP,x);
    d2Px = polyval(d2P,x);
    if abs(Px) < 1e-14*polyval(abs(P),abs(x)) 
      break    % if value is zero in relative accuracy
    end
    G = dPx/Px;
    H=G^2 - d2Px / Px;
    D1= (G+sqrt((n-1)*(n*H-G^2)));
    D2= (G-sqrt((n-1)*(n*H-G^2)));
    D = D1;
    if abs(D2)>abs(D1) D=D2; end    % select the larger denominator
    a=n/D;
    x=x-a;
    fprintf("x(%d) = %.15f%+.15fi, |dx|=%8.5g\n",k,real(x),imag(x), abs(a));
    if abs(a) < err*(err+abs(x))
        break
    end
  end
  y = polyval(P0,x);   % check polynomial value of the original polynomial
  fprintf("root found x=%.15f%+.15fi with value P0(x)=%.6g%+.6gi\n", real(x),imag(x),real(y),imag(y));
  disp("Deflation");
  [ P,R ] = deconv(P,[1,-x]);  % division with remainder
  x = conj(x);  % shortcut for conjugate pairs and clustered roots
end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM