我们先回顾下最优问题的概念。所谓最优化,就是指在满足某些约束条件的前提下,使得指定的目标函数极大化或极小化的过程。在工业界很多场景都可以归结为一个最优问题,利用合适的最优算法可以极大地提升效率和减小人工成本。通常情况下,现实的优化问题可以抽象为一个非线性规划,即:
m i n i m i z e f ( x ) s u b j e c t t o g i ( x ) ≤ 0 i = 1 , . . . , m h j ( x ) = 0 j = 1 , . . . , l x L ≤ x ≤ x U \begin{aligned} minimize \ &f(\mathsf{x})\\ subject\ to\ &g_i(\mathsf{x})\leq0\ \ i=1,...,m\\ &h_j(\mathsf{x})=0\ \ j=1,...,l\\ &\mathsf{x}^L\leq \mathsf{x} \leq \mathsf{x}^U \end{aligned} minimize subject to f(x)gi(x)≤0 i=1,...,mhj(x)=0 j=1,...,lxL≤x≤xU
其中, x = [ x 1 , x 2 , . . . , x n ] T \mathsf{x}=[x_1,x_2,...,x_n]^T x=[x1,x2,...,xn]T为 n n n维列向量,各元素称为决策变量,而 x L \mathsf{x}^L xL, x U \mathsf{x}^U xU表示决策变量的上界和下界; f f f称为目标函数或成本函数, g g g为不等式约束, h h h为等式约束。不同的优化问题通过适当的调整,总可以写成上面的形式。其实优化问题是个非常宽泛的概念,现在很火热的机器学习,其各种核心算法大多数也是在构建一个优化模型,例如神经网络经典的BP算法就属于一个无约束最优算法。
根据决策变量和目标函数的不同,一个非线性规划可以细分成下面这些特殊规划问题,通常我们对于特定的规划问题会有一些专门的算法来解算:
对各种优化问题,现在已经有很多特定的或一般性的算法,一些第三方公司会以这些算法为核心推出自己的优化软件工具,方便研究者和工程师的应用。那么对于优化工具的使用者来说,他们要做的最核心的工作将是优化问题建模。建模就是采用数学模型来描述物理问题,要根据实际情况抽象出决策变量、目标函数以及约束条件,表述成上述的非线性规划形式。通过建模,我们可以了解问题属于哪一类优化问题,从而采用合适的优化工具来处理。
Google OR-Tools是谷歌前才几年推出的优化套件,是一个相当新的优化库,其社区也是相当活跃,毕竟谷歌的影响力摆在那呢,可是也因为是谷歌的产品,其官网地址国内得才能访问。我个人在最近的一个项目里使用的就是Google OR-Tools,根据官方的资料和我自己的使用体验,总结下来有下面这些特点:
我们来基于OR-Tools写个小的Demo来体验一下。官网的教程是以Python为主的,只在前几章提供了其他语言的源码,我因为C#用的比较多,下面的Demo及后续文章的代码都在.net core平台实现。
我们来求解一个最简单的线性规划问题:
m a x i m i z e 3 x + y 0 ≤ x ≤ 1 0 ≤ y ≤ 2 x + y ≤ 2 \begin{aligned} maximize \ &3x+y \\ &0\leq x \leq 1\\ &0\leq y \leq 2\\ &x+y \leq 2 \end{aligned} maximize 3x+y0≤x≤10≤y≤2x+y≤2
用OR-Tools解决优化问题,一般我们都会进行以下的步骤:
打开VS(最新的OR-Tools需要VS 2019的支持,低于2019的VS可能会报运行时异常),新建一个.Net Core的控制台项目
在Nuget包管理页面中搜索并下载OR-Tools依赖
因为这是个标准的线性规划,我们只需要调用线性优化库来计算。在主程序中添加LinearSolver空间的引用
using Google.OrTools.LinearSolver;
首先我们定义solver对象,线性优化库的程序中是需要依靠solver对象来创建变、约束和目标的,因此这里没有完全依照上面描述的步骤:
//Create the linear solver with the GLOP backend
Solver solver = Solver.CreateSolver("SimpleLpProgram", "GLOP_LINEAR_PROGRAMMING");
接着定义变量,即 x x x和 y y y。MakeNumVar方法的三个参数分别表示变量的下限、上限和名字,这里通过变量定义,就已经加上了原问题的前两个约束。
//Create variables
Variable x = solver.MakeNumVar(0.0, 1.0, "x");
Variable y = solver.MakeNumVar(0.0, 2.0, "y");
Console.WriteLine("Number of variables = " + solver.NumVariables());
我们需要定义第三个约束。一个Constraint对象就代表一个线性约束,我们通过SetCoefficient方法设置这两个变量的权值都是1,也就表示 0 ≤ x + y ≤ 2 0 \leq x+y \leq 2 0≤x+y≤2
//Create a Linear constraint
Constraint ct = solver.MakeConstraint(0.0, 2.0, "ct");
ct.SetCoefficient(x, 1);
ct.SetCoefficient(y, 1);
Console.WriteLine("Number of constraints = " + solver.NumConstraints());
现在就剩目标还没有定义了,通过solver对象创建Objective对象,并设定一个最大化的目标
//Create the objective function
Objective objective = solver.Objective();
objective.SetCoefficient(x, 3);
objective.SetCoefficient(y, 1);
objective.SetMaximization();
最后我们就可以调用solver的Solve方法计算得到结果了
//Get the result
solver.Solve();
Console.WriteLine("Solution:");
Console.WriteLine("Objective value = " + solver.Objective().Value());
Console.WriteLine("x = " + x.SolutionValue());
Console.WriteLine("y = " + y.SolutionValue());
using System;
using Google.OrTools.LinearSolver;
namespace Demo1
{
class Program
{
static void Main(string[] args)
{
//Create the linear solver with the GLOP backend
Solver solver = Solver.CreateSolver("SimpleLpProgram", "GLOP_LINEAR_PROGRAMMING");
//Create variables
Variable x = solver.MakeNumVar(0.0, 1.0, "x");
Variable y = solver.MakeNumVar(0.0, 2.0, "y");
Console.WriteLine("Number of variables = " + solver.NumVariables());
//Create a Linear constraint
Constraint ct = solver.MakeConstraint(0.0, 2.0, "ct");
ct.SetCoefficient(x, 1);
ct.SetCoefficient(y, 1);
Console.WriteLine("Number of constraints = " + solver.NumConstraints());
//Create the objective function
Objective objective = solver.Objective();
objective.SetCoefficient(x, 3);
objective.SetCoefficient(y, 1);
objective.SetMaximization();
//Get the result
solver.Solve();
Console.WriteLine("Solution:");
Console.WriteLine("Objective value = " + solver.Objective().Value());
Console.WriteLine("x = " + x.SolutionValue());
Console.WriteLine("y = " + y.SolutionValue());
}
}
}