实现最速下降算法,可变步长

Implementing steepest descent algorithm, variable step size

我正在尝试用编程语言 (C/C++/fortran) 实现最速下降算法。

例如 f(x1,x2) = x1^3 + x2^3 - 2*x1*x2

的最小化
  1. 估计起始设计点 x0,迭代计数器 k0,收敛参数公差 = 0.1。 假设这个起点是 (1,0)

  2. 计算f(x1,x2)在当前点x(k)的梯度为grad(f)。我会在这里使用数值微分。

    d/dx1 (f) = lim (h->0) (f(x1+h,x2) - f(x1,x2) )/h

    这是梯度(f)=(3*x1^2 - 2*x2, 3*x2^2 - 2*x1)

    grad(f) 在 (0,1) 是 c0 = (3,-2)

  3. 因为c0的L2范数>公差,我们进行下一步

  4. 方向d0 = -c0 = (-3,2)

  5. 计算步长a。最小化 f(a) = f(x0 + ad0) = (1-3a,2a) = (1-3a)^3 + (2a)^3 - 2(1- 3a)*(2a)。我没有保持恒定的步长。

  6. 更新:新[x1,x2] = 旧[x1,x2]x + a*d0。

我不明白第 5 步该怎么做。 我有一个使用二分法的一维最小化程序,它看起来像:

program main()
    ...
    ...
    define upper, lower interval
    call function value
    ...calculations
    ...
    ...


function value (input x1in) (output xout)
    ...function is x^4 - 2x^2 + x + 10 
    xout = (xin)^4 - 2*(xin)^2 + (xin) + 10

在这种情况下,查看第 5 步,我无法传递符号 a。 关于如何用编程语言实现算法的任何想法,尤其是第 5 步?请建议是否有完全不同的编程方式。我见过很多步长不变的程序,但我想在每一步都计算它。这个算法可以很容易地在 MATLAB ot python sympy 中使用符号来实现,但我不想使用符号。 任何建议表示赞赏。谢谢。

如果 C++ 是一个选项,您可以利用 functors and lambdas

让我们考虑一个我们想要最小化的函数,例如y = x2 - x + 2。它可以表示为一个函数对象,它是一个class 重载 operator():

struct MyFunc {
    double operator()( double x ) const {
        return  x * x - x + 2.0;
    }
};

现在我们可以声明一个这种类型的对象,像函数一样使用它,并将它作为模板参数传递给其他模板函数。

// given this templated function:
template < typename F >
void tabulate_function( F func, double a, double b, int steps ) {
    //  the functor     ^^^^^^  is passed to the templated function
    double step = (b - a) / (steps - 1);

    std::cout << "    x          f(x)\n------------------------\n";
    for ( int i = 0; i < steps; ++i ) {
        double x = a + i * step,
               fx = func(x);
        //          ^^^^^^^ call the operator() of the functor
        std::cout << std::fixed << std::setw(8) << std::setprecision(3) << x
                  << std::scientific << std::setw(16) << std::setprecision(5)
                  << fx << '\n';
    }   
}

// we can use the previous functor like this:
MyFunc example;
tabulate_function(example, 0.0, 2.0, 21);

OP 的功能可以用类似的方式实现(给定一个助手 class 来表示 2D 点):

struct MyFuncVec {
    double operator()( const Point &p ) const {
        return p.x * p.x * p.x  +  p.y * p.y * p.y  -  2.0 * p.x * p.y;
    }
};

该函数的梯度可以表示为(给定一个 class,它实现了一个 2D 向量):

struct MyFuncGradient {
    Vector operator()( const Point &p ) {
        return Vector(3.0 * p.x * p.x  -  2.0 * p.y, 3.0 * p.y * p.y  -  2.0 * p.x);
    }
};     

现在,OP题的第五步要求使用一维优化算法沿梯度方向最小化第一个函数,需要传递一个一维函数。我们可以使用 lambda 来解决这个问题:

    MyFuncVec funcOP;
    MyFuncGradient grad_funcOP; 
    Point p0(0.2, 0.8);
    Vector g = grad_funcOP(p0);

    // use a lambda to transform the OP function to 1D
    auto sliced_func = [&funcOP, &p0, &g] ( double t ) -> double {
        // those variables ^^^ ^^^ ^^ are captured and used
        return funcOP(p0 - t * g);
    };

    tabulate_function(sliced_func, 0, 0.5, 21);

实例HERE.