找到黑盒函数的第一个根,或相同函数的任何负值

时间:2013-05-11 19:55:43

标签: algorithm search mathematical-optimization orbital-mechanics

我有一个黑盒函数,f(x)和x的一系列值。 我需要找到x的最小值,其中f(x)= 0。

我知道,对于x的范围的开始,f(x)> 0,如果我有一个值f(x)<0 0我可以使用调节falsi或类似的根寻找方法来尝试确定f(x)= 0。

我知道f(x)是连续的,并且对于所讨论的范围应该只有0,1或2个根,但它可能具有局部最小值。

f(x)在某种程度上计算成本很高,而且我必须经常找到第一个根。

我正在考虑某种随机性的爬山以避免任何局部最小值,但是你怎么知道没有最小值小于零或者你是否还没找到它?我认为这个函数不应该有两个以上的最小点,但我不能完全肯定这个就足够了。

如果有帮助,x在这种情况下代表一个时间,而f(x)代表船舶与当时轨道(月球/行星)中的物体之间的距离。我需要第一点,它们彼此相距一定距离。

4 个答案:

答案 0 :(得分:2)

我的方法听起来很复杂,但最终方法的计算时间远小于距离计算(评估你的f(x))。此外,它已经在现有的库中编写了很多实现。

那我该做什么:

  • 使用Chebychev多项式近似f(x)
  • 找到该多项式的真正根源
  • 如果找到任何一个,请在更精确的rootfinder(如果需要)中使用这些根作为初始估计值

鉴于你的函数的性质(平滑,连续,表现良好)以及有0,1或2个根的信息,已经可以找到一个好的Chebychev多项式,其中有3个f(x)的评估。 / p>

然后找到Chebychev系数的伴随矩阵的特征值;这些对应于Chebychev多项式的根。

如果所有都是虚构的,则有0根。 如果有一些真正的根,检查两个是否相等(你所说的“罕见”情况)。 否则,所有真实的特征值都是根;最低的一个是你寻找的根。

然后使用Newton-Raphson进行细化(如果需要,或使用更好的Chebychev多项式)。 f的导数可以使用中心差异近似

f'(x) = ( f(x+h)-f(h-x) ) /2/h     (for small h)

我在Matlab / Octave中实现了Chebychev例程(如下所示)。使用方式如下:

R = FindRealRoots(@f, x_min, x_max, 5, true,true);

[x_min,x_max]您的范围在x5用于查找多项式的点数(越高,越准确。等于所需的函数评估量),最后true将绘制实际函数和Chebychev近似值的图(主要用于测试目的)。

现在,实施:

% 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 eignevalues 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 ARRAFUN() 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.
% 
% First version 26th May 2007 by Stephen Morris, 
% Nightingale-EOS Ltd., St. Asaph, Wales.
%
% Modified 14/Nov (Rody Oldenhuis)
%
% See also roots, eig.

function Roots = FindRealRoots(funfcn, a, b, n, vectorized, make_plot)

    % 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 j=1:n, c(j)=(f(:).'*(cos((pi*(j-1))*((k-0.5)/n))))*(2-(j==1))/n; end       

    % coefficients may be [NaN] if [inf]
    % ??? TODO - it is of course possible for c(n) to be zero...
    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
        if vectorized
            fungrid = feval(funfcn, grid);
        else
            fungrid = arrayfun(@(x) feval(funfcn,x), grid);
        end        
        % corresponding Chebychev-grid (more complicated but essentially the same)
        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

答案 1 :(得分:0)

您可以使用secant method这是牛顿方法的离散版本。

enter image description here

通过计算两点(=割线)与X轴交叉点之间的线来估算根。

答案 2 :(得分:0)

您的函数只有0,1或2个根,因此可以使用不保证第一个根的算法来完成。

  • 使用牛顿方法或其他方法查找一个根。如果找不到任何root,这个算法也会放弃。
  • 让找到的根为r,x的范围的开头为x0。让d = (r-x0)/2
  • d > 0期间,计算f(r-d)。 if f(r-d) > 0,half dd := d / 2)和循环。如果 f(r-d) <= 0,逃避循环。
  • 如果循环由d = 0完成,则报告r作为第一个根。如果d > 0,请使用任何其他方法在x0r-d之间找到根并报告。

我假设了两个先决条件。

  1. f(x)取x个浮点数
  2. 在f(x)的根的每个点处,f(x)的图与交叉。在f(x)= x ^ 2中,它们不是触摸根,如x = 0。
  3. 使用条件2,您可以证明如果没有f(r-d) < 0∀ x: x0 < x < r, f(x) > 0这一点。

答案 3 :(得分:0)

您可以对R库rootSolve中的uniroot.all函数进行少量更改。

uniroot.all <- function (f, interval, lower= min(interval),
                         upper= max(interval), tol= .Machine$double.eps^0.2,
                         maxiter= 1000, n = 100, nroots = -1, ... ) {

  ## error checking as in uniroot...
  if (!missing(interval) && length(interval) != 2)
    stop("'interval' must be a vector of length 2")
  if (!is.numeric(lower) || !is.numeric(upper) || lower >=
      upper)
    stop("lower < upper  is not fulfilled")

  ## subdivide interval in n subintervals and estimate the function values
  xseq <- seq(lower,upper,len=n+1)
  mod  <- f(xseq,...)

  ## some function values may already be 0
  Equi <- xseq[which(mod==0)]

  ss   <- mod[1:n]*mod[2:(n+1)]  # interval where functionvalues change sign
  ii   <- which(ss<0)

  for (i in ii) {
    Equi <- c(Equi, uniroot(f, lower = xseq[i], upper = xseq[i+1] ,...)$root)
    if (length(Equi) == nroots) {
      return(Equi)
    }
  }
  return(Equi)
}

然后像这样运行:

uniroot.all(f = your_function, interval = c(start, stop), nroots = 1)