【问题标题】:Univariate root-finding with minimum number of function evaluations具有最少函数评估次数的单变量求根
【发布时间】:2016-11-17 21:26:52
【问题描述】:

我正在寻找使用很少函数评估的寻根算法(目标是最低限度)。寻根问题具有以下特点:

f(x) = 0, R -> R

  • 函数 (f(.)) 评估成本极高*;
  • 边界区间 ([a,b]) 可用于开始(相对较好的近似值,不是胡乱猜测);
  • f(.) 是连续的;
  • f(.) 可微分(解析导数不可用);
  • 已知只有一个根位于起始区间 ([a,b]);
  • 平滑变化的f(.)(函数不会出现极端情况);
  • 允许的停止标准,例如|f(x)| < 1e-2 就足够了。

*我们可以放心地假设,与f(.) 的单个评估相比,该算法完成的任何计算都可以忽略不计。因此,即使节省单个函数评估也是一项重大收获。

鉴于这些,找到函数评估次数最少的根的最有效算法是什么?

基于 Matlab 的 fzero 和 scipy 的 root-finding functions,Brent 的方法似乎是流行的选择,尽管对于上述特定问题可能有更有效的算法。

也欢迎参考书籍和评论文章。

【问题讨论】:

    标签: python matlab numerical-methods


    【解决方案1】:

    好吧,如果你想试试布伦特的方法,你可以试试我对下面原始算法的翻译。这只是我将原始 C 代码翻译成 MATLAB。我已经验证了原始 C 代码的所有测试用例在我的翻译中产生了相同的结果。

    在下面的代码中This 是一个函数句柄,ab 是搜索边界。

    function x = brent(This,a,b) 
    
    tol = 1e-2 ;
    maxit = 1e+3 ;
    displayIter = true ;
    
    Fa = This(a) ;
    Fb = This(b) ;
    c = a ;
    Fc = Fa ;
    
    it = 0 ;
    if displayIter
        fprintf(1,'Searching for a root in the interval [%g,%g] using Brent''s method...\n',a,b) ;
    end
    while it<maxit
        it = it + 1 ;
    
        prevStep = b - a ;
    
        if abs(Fc) < abs(Fb)
            % swap for b to be best approximation
            a = b ;
            b = c ;
            c = a ;
            Fa = Fb ;
            Fb = Fc ;
            Fc = Fa ;
        end
    
        tolAct =  2*eps*abs(b) + tol/2 ;
    
        newStep = (c-b)/2 ;
    
        if abs(newStep) <= tolAct || abs(Fb)<eps
            % acceptable solution found
            x = b ;
            return 
        end
    
        if abs(prevStep) >= tolAct && Fa == Fb
            % previous step was large enough and in the right direction, try
            % interpolation
            cb = c-b ;
            if abs(a-c)<eps
                % if only two distinct points, interpolate linearly
                t1 = Fb/Fa ;
                p = cb*t1 ;
                q = 1 - t1 ;
            else
                % three points, do quadratic inverse  interpolation
                a = Fa/Fc ;
                t1 = Fb/Fc ;
                t2 = Fb/Fa ;
                p = t2*( cb*q*(q-t1) - (b-a)*(t1-1) ) ;
                q = (q-1)*(t1-1)*(t2-1) ;
            end
            if p>0
                q = -q ;
            else
                p = -p ;
            end
            if p < ( 0.75*cb*q-abs(tolAct*q)/2 ) && p < abs(prevStep*q/2)
                newStep = p/q ;
            end
        end
        % step must be at least as large as tolerance
        if abs(newStep)<tolAct
            if newStep>0
                newStep = tolAct ;
            else
                newStep = -tolAct ;
            end
        end
    
        a = b ;
        Fa = Fb ;
        b = b + newStep ;
        Fb = This(b) ;
        if ( Fb > 0 && Fc > 0 ) || ( Fb < 0 && Fc < 0 )
            c = a ;
            Fc = Fa ;
        end
        if displayIter
            fprintf(1,'[%g] Gap = %g, dx = %g\n',it,abs(Fb),newStep) ;
        end
    end
    fprintf(1,'[%g] Gap = %g, dx = %g\n',it,abs(Fb),newStep) ;
    
    end
    

    【讨论】:

    • 感谢您的代码 (3)。我已经测试过 Brent 的方法,除了 fzero (1) 我也在使用 this(2) Matlab 实现。使用fun = @(x) exp(-exp(-x)) - xx0 = [0, 1]tolx = 1e-2 的快速测试显示以下函数评估数: (1) 5; (2) 4个; (3) 7. 我正在寻找更有效的东西。 :)
    猜你喜欢
    • 1970-01-01
    • 2019-10-04
    • 2015-12-01
    • 2019-08-28
    • 2023-03-11
    • 1970-01-01
    • 2013-11-29
    • 1970-01-01
    • 2021-04-29
    相关资源
    最近更新 更多