Я собираюсь предоставить канонический ответ на этот вопрос, потому что его часто задают так или иначе - и не только здесь, на переполнении стека.
Если вы заранее знаете, что ваша функция плавная и непрерывная, вы можете использовать метод, используемый Стивеном Моррисом в 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
Следующий скрипт использует эту функцию, чтобы решить вашу проблему. Поскольку ваша функция довольно тривиальна брать производные, я собираюсь показать, как fzero
сравнивает Newton's method, Halley's method и 4th 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
:
линейный выход
Команда:
all_equal =
1
fevals_fzero =
29
fevals_Newton =
27
fevals_Halley =
36
fevals_4th =
41
Выводы:
- Для гладких непрерывных функций, полином Чебышева является достаточно эффективным и достаточно для большинства намерений и целей. В этом примере здесь использовались только 9 оценок функций, чтобы найти (1) количество корней и (2) начальные оценки для каждой из них с точностью до 2 цифр. Эти цифры улучшаются только для увеличения полиномиального порядка. Для этой конкретной функции точность улучшается за счет меньших вычислительных затрат, чем любого из из более «стандартных» методов уточнения, продемонстрированных здесь. Установка порядка полинома примерно до 19 (подразумевает 19 оценок функций) дает сопоставимую точность и производительность, поэтому не уточняется на самом деле необходимо.
fzero
прост в использовании, но, конечно, не самый лучший при любых обстоятельствах. В этом конкретном случае метод Ньютона тривиален для настройки и имеет лучшую производительность.
- Не полагайтесь слишком сильно на утверждения о скорости конвергенции численного метода; это теоретический предел, который сам по себе не доказывает превосходство метода, используемого на практике. В вышесказанном показано, что чем выше порядок метода уточнения, тем выше производительность.
И что делает ваш код? –