等式^−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)
答案 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
以下脚本使用此功能来解决您的问题。由于您的函数对于衍生函数来说相当简单,我将展示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
结论:
fzero
易于使用,但在所有情况下肯定不是最好的。在这种特殊情况下,Newton的方法设置起来很简单,并且具有更好的性能。 答案 1 :(得分:1)
该解决方案分为3个步骤:
步骤:将函数与自身相乘,移位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
步骤:删除最后一个条目
zx = zx(1:end-1); % Eliminate any due to ‘wrap-around’ effect
步骤:计算过零点的所有根
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
根据您的问题,您可能还需要检查边界以及零解决方案。另一种方法是迭代地执行网格搜索,即以粗网格开始,然后围绕近似零进行细化。