如何找出函数的根

时间:2017-01-02 12:25:11

标签: matlab

等式^−0.2sin(+2) = 0.1在区间0<<10中有三个解。找到这三个解决方案。 (答案: = 1.0187, 4.5334, 7.0066)。

我怎样才能得到所有根源? 我使用了fzero函数:

fun = @(x) (exp(-0.2.*x).*sin(x+2));
x   = 0:.01:10;
z0  = fzero(fun, 0)

4 个答案:

答案 0 :(得分:2)

我将为这个问题提供一个规范的答案,因为它经常以某种方式被问到 - 而不仅仅是在Stack Overflow上。

如果您事先知道您的功能是平滑且连续的,那么您可以使用Stephen this File Exchange submission中使用的方法,我为了效率和清晰度而进行了一些改进。

他的方法通过其Chebyshev polynomial近似于感兴趣区间的函数。这给出了接近最佳的近似值,并且具有最小的函数评估。使用类似于MATLAB自己的roots函数的方法可以很容易地找到这个多项式的根。

这是重新设计的功能:

% FINDREALROOTS     Find approximations to all real roots of any function 
%                   on an interval [a, b].
%
% USAGE:
%  Roots = FindRealRoots(funfcn, a, b, n, vectorized, make_plot)
%
% FINDREALROOTS() approximates all the real roots of the function 'funfcn' 
% in the interval [a,b]. It does so by finding the roots of an [n]-th degree 
% Chebyshev polynomial approximation, via the eigenvalues of the associated 
% companion matrix. 
%
% When the argument [vectorized] is [true], FINDREALROOTS() will evaluate 
% the function 'funfcn' at all [n] required points in the interval 
% simultaneously. Otherwise, it will use ARRAYFUN() to calculate the [n] 
% function values one-by-one. [vectorized] defaults to [false]. 
%
% When the argument [make_plot] is true, FINDREALROOTS() plots the 
% original function and the Chebyshev approximation, and shows any roots on
% the given interval. Also [make_plot] defaults to [false]. 
%
% All [Roots] (if any) will be sorted.
% 
% See also roots, eig.
function Roots = FindRealRoots(funfcn, a, b, n, vectorized, make_plot)

% First version 26th May 2007 by Stephen Morris, 
% Nightingale-EOS Ltd., St. Asaph, Wales.
%
% Modified 2017/January/02 by Rody Oldenhuis, LuxSpace sarl

    % parse input and initialize.
    inarg = nargin; 
    if n <= 2, n = 3; end                    % Minimum [n] is 3:    
    if (inarg < 5), vectorized = false; end  % default: function isn't vectorized
    if (inarg < 6), make_plot = false; end   % default: don't make plot

    % some convenient variables
    bma   = (b-a)/2;  
    bpa   = (b+a)/2; 
    Roots = [];

    % Obtain the Chebyshev coefficients for the function
    %
    % Based on the routine given in Numerical Recipes (3rd) section 5.8;
    % calculates the Chebyshev coefficients necessary to approximate some
    % function over the interval [a,b]

    % initialize 
    c = zeros(1,n);  
    k = (1:n)';  
    y = cos(pi*((1:n)-1/2)/n); 

    % evaluate function on Chebychev nodes
    if vectorized
        f = feval(funfcn, y*bma + bpa);
    else
        f = arrayfun(@(x) feval(funfcn,x), y*bma + bpa);
    end

    % compute the coefficients
    for jj = 1:n
        c(jj) = ( f(:).' * (cos((pi*(jj-1)) * ((k-0.5)/n))) ) * (2-(jj==1))/n; 
    end       

    % Coefficients may be [NaN] if [inf]
    if any(~isfinite(c(:))) || (c(n) == 0)
        return; end

    % Define [A] as the Frobenius-Chebyshev companion matrix. This is based
    % on the form given by J.P. Boyd, Appl. Num. Math. 56 pp.1077-1091 (2006).
    one          = ones(n-3,1);
    A            = diag([one/2; 0],-1) + diag([1; one/2],+1);
    A(end, :)    = -c(1:n-1)/2/c(n);
    A(end,end-1) = A(end,end-1) + 0.5;

    % Now we have the companion matrix, we can find its eigenvalues using the
    % MATLAB built-in function. We're only interested in the real elements of
    % the matrix:
    eigvals  = eig(A);  
    realvals = eigvals(imag(eigvals)==0);

    % if there aren't any real roots, return
    if isempty(realvals)
        return; end

    % Of course these are the roots scaled to the canonical interval [-1,1]. We
    % need to map them back onto the interval [a, b]; we widen the interval just
    % a tiny bit to make sure that we don't miss any that are right on the 
    % boundaries.
    rangevals = nonzeros(realvals(abs(realvals) <= 1+1e-5));

    % also sort the roots
    Roots = sort(rangevals*bma + bpa);

    % As a sanity check we'll plot out the original function and its Chebyshev
    % approximation: if they don't match then we know to call the routine again
    % with a larger 'n'.
    if make_plot

        % simple grid
        grid = linspace(a,b, max(25,n));

        % evaluate function like before
        if vectorized
            fungrid = feval(funfcn, grid);
        else
            fungrid = arrayfun(@(x) feval(funfcn,x), grid);
        end        
        % corresponding Chebychev-grid (more complicated but essentially
        % identical)
        y  = (2.*grid-a-b) ./ (b-a); 
        d  = zeros(1,length(grid)); 
        dd = d;
        for j = length(c):-1:2
            sv=d;
            d=(2*y.*d)-dd+c(j);
            dd=sv;
        end
        chebgrid = (y.*d) - dd + c(1);

        % Now make plot
        figure(1), clf,  hold on
        plot(grid, fungrid ,'color' , 'r');
        line(grid, chebgrid,'color' , 'b'); 
        line(grid, zeros(1,length(grid)), 'linestyle','--')
        legend('function', 'interpolation')

    end % make plot

end % FindRealRoots

以下脚本使用此功能来解决您的问题。由于您的函数对于衍生函数来说相当简单,我将展示fzeroNewton's methodHalley's method4th order Householder method的比较:

% Parameters
N    = 9;  % Degree of the Chebyshev polynomial to use
xmin = 0;  % LB
xmax = 10; % UB

% Function of interest, and its first 3 derivatives
f    = @(x) +exp(-0.2*x) .*       sin(x+2)                  - 0.1;
fp   = @(x) +exp(-0.2*x) .* (     cos(x+2)  - 0.200*sin(x+2));
fpp  = @(x) -exp(-0.2*x) .* ( 0.40*cos(x+2) + 0.960*sin(x+2));
fppp = @(x) +exp(-0.2*x) .* (-0.88*cos(x+2) + 0.592*sin(x+2));

% Approximate the roots 
R = FindRealRoots(f, xmin, xmax, N, true, false);

% Refine the roots
% Compare fzero against the first 3 Householder methods
A = R;  fevals_fzero  = N;
B = R;  fevals_Newton = N;
C = R;  fevals_Halley = N;
D = R;  fevals_4th    = N;

options = optimset('TolX'   , 1e-10,...
                   'display', 'off');

for ii = 1:numel(A)

    % FZERO
    [A(ii), ~,~, out] = fzero(f, R(ii), options);       
    fevals_fzero = fevals_fzero + out.funcCount;

    % Newton's method
    xprev = inf;
    x     = R(ii);    
    while ( abs(xprev - x) > 1e-10 )
        xprev = x;
        x     = x - f(x)/fp(x);
        fevals_Newton = fevals_Newton + 2;
    end    
    B(ii) = x;

    % Halley's method
    xprev = inf;
    x     = R(ii);    
    while ( abs(xprev - x) > 1e-10 )

        fx   = f(x);
        fpx  = fp(x);
        fppx = fpp(x);

        xprev = x;
        x     = x - 2*fx*fpx/(2*fpx*fpx - fx*fppx);

        fevals_Halley = fevals_Halley + 3;
    end    
    C(ii) = x;

    % 4th order method 
    xprev = inf;
    x     = R(ii);    
    while ( abs(xprev - x) > 1e-10 )

        fx    = f(x);    fx2  = fx*fx;
        fpx   = fp(x);   fpx2 = fpx*fpx;
        fppx  = fpp(x);
        fpppx = fppp(x);

        xprev = x;
        x     = x -  3*(2*fx*fpx2 - fx2*fppx) / ...
                    (6*(fpx2*fpx - fx*fpx*fppx) + fx2*fpppx);

        fevals_4th = fevals_4th + 4;
    end    
    D(ii) = x;

end

% Check for convergence
all_equal = all(abs(A-B) < 1e-8) && ...
            all(abs(A-C) < 1e-8) && ...
            all(abs(A-D) < 1e-8)

% function evaluations per method
fevals_fzero 
fevals_Newton
fevals_Halley
fevals_4th 

FindRealRoots的最后一个参数设置为true时生成的图:

Chebyshev approximant

命令行输出:

all_equal =
 1
fevals_fzero =
    29
fevals_Newton =
    27
fevals_Halley =
    36
fevals_4th =
    41

结论:

  • 对于平滑连续的函数,切比雪夫多项式非常有效,足以满足大多数意图和目的。此处的示例仅使用9个函数评估来查找(1)根的数量,以及(2)每个到约2个数字的准确度的初始估计。这些数字仅针对增加的多项式阶数而改善。对于这个特定的功能,与更多&#34;标准&#34;的任何相比,精度提高了,计算成本更低。这里演示的细化方法 - 将多项式顺序设置为大约19(意味着19个函数评估)可以提供相当的精度和性能,因此实际上不需要进行细化。
  • fzero易于使用,但在所有情况下肯定不是最好的。在这种特殊情况下,Newton的方法设置起来很简单,并且具有更好的性能。
  • 不要过分依赖数值方法收敛速度的陈述;这是一个理论上的限制,它本身并不能证明在实践中使用的方法的优越性。如上所示,细化方法的顺序越高,整体性能越差。

答案 1 :(得分:1)

https://es.mathworks.com/matlabcentral/answers/143643-fzero-function-calculating-all-zeros-within-interval

中也给出了良好的结果

该解决方案分为3个步骤:

  1. 步骤:将函数与自身相乘,移位1个元素以得到零交叉并获得负值

    fun =@(x) exp(-0.2.*x).*sin(x+2) - 0.1;
    x = linspace(0,10);
    y = fun(x);
    zx = x(fun(x).*circshift(fun(x),[0 -1]) <= 0);  % Estimate zero crossings
    
  2. 步骤:删除最后一个条目

    zx = zx(1:end-1);  % Eliminate any due to ‘wrap-around’ effect
    
  3. 步骤:计算过零点的所有根

    for k1 = 1:length(zx)
        fz(k1) = fzero(fun, zx(k1));
    end
    

答案 2 :(得分:0)

函数fzero在找到第一个零时停止。在你编码的方式中,这个零将是最接近'0'(第二个参数)的。

您会发现这些链接很有用,因为问题非常相似。解决方案是循环函数fzero。

https://www.mathworks.com/matlabcentral/answers/103169-how-do-i-find-all-the-zeros-of-a-function

https://www.mathworks.com/matlabcentral/answers/18398-getting-3-zeros-from-a-function-using-fzero

答案 3 :(得分:0)

作为一般数值问题,这确实很难。

你需要做一些假设来帮助fsolve()(隐含在M_Tornack的答案中)。例如,假设某个容差的粗略网格搜索找到所有过零点。公差定义了零的最小间隔。网格搜索速度慢且不精确,因此我们之后使用fsolve优化答案。如下:

fun = @(x) (exp(-0.2.*x).*sin(x+2))-0.1;
x = 0:0.1:10; % interval start : minimum separation of zeroes : end
zeros_approx = x(find(diff(fun(x)>0))) % grid search
for i = 1:length(zeros_approx)
    zeros_refined(i) = fsolve(fun,zeros_approx(i))
end

根据您的问题,您可能还需要检查边界以及零解决方案。另一种方法是迭代地执行网格搜索,即以粗网格开始,然后围绕近似零进行细化。