package main
import (
"fmt"
."math"
)
func main() {
/*------------------------------------------------------
Author : Song Yezhi
verison : 2021-10-7 18:25
go build -gcflags "-N -l"
------------------------------------------------------*/
r :=0.0
s :=0.7
t :=1.0
_ , _ = paraInterp(r,s,t,myfx)
}
func myfx(x float64) float64 {
/*------------------------------------------------------
Author : Song Yezhi
verison : 2021-10-7 18:26
go build -gcflags "-N -l"
------------------------------------------------------*/
fx := Pow(x,6)-11.0*Pow(x,3)+17.0*x*x - 7.0*x +1.0
return fx
}
func paraInterp(r,s,t float64,funcX func(float64) float64) (x, fx float64) {
/*------------------------------------------------------
Author : Song Yezhi
verison : 2021-10-7 18:28
go build -gcflags "-N -l"
连续抛物型法一维搜索
-----------------------------------------------------
Input Parameters :
r,s,t ------初值
funcX ----- 目标函数
Output Parameters :
x---极小点
fx--极小点函数值
------------------------------------------------------*/
var imax int = 20
var tol float64 = 1e-8
var fs,ft,fr float64
var tmp1,tmp2 float64
fs = funcX(s)
fr = funcX(r)
ft = funcX(t)
for i := 0; i < imax; i++ {
tmp1 = (fs -fr) * (t-r) * (t-s)
tmp2 = 2.0*((s-r)*(ft-fs) -(fs-fr)*(t-s))
x = (r+s)/2.0 - tmp1/tmp2
if Abs(fs-fr) < tol {
break
}
t = s
s = r
r = x
ft = fs
fs = fr
fr = funcX(r)
// *+++single function evaluation +++*
fmt.Printf("i= %4d x= %12.7f f(x)=%12.7f \n", i, x, ft)
}
return x,fx
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
class cnewton
/*--------------------------------------class comment
Version : V1.0
Coded by : syz
Date : 2011-07-11 08:46:04 *星期一*
----------------------------------------------------
Desciption : 牛顿下山法计算最优化问题
Methods :
* newton--------牛顿下山方法函数
* pivot---------选主元消去法解线性方程
* uptri---------上三角方程回代方法
* func----------目标函数
* grad----------梯度函数
* hess----------hessian矩阵
--------------------------------------------------*/
{
public StreamWriter fout = new StreamWriter("fout.txt");
//输出文件
public void newton(out VEC x, out double fx,VEC x0)
/*------------------------------------------ comment
Author : syz
Date : 2011-07-11 08:52:58
---------------------------------------------------
Desciption : 牛顿下山法计算非线性最优化
* 注意:牛顿法形式上需要计算Hessian矩阵的逆,
* 实际计算时直接解算线性方程
paramtegers :
* x------极小值点
* fx-----极值点的函数值
* x0-----初值向量
*
* ~~~~以下量在程序开头部分控制,也可以把控制量放在参数中,由外部控制
* imax------最大迭代次数
* tol-------误差容限
-------------------------------------------------*/
{
int imax = 50;
//最大允许迭代次数
double tol = 1e-7;
//误差容限
VEC dx, x1, df;
MAT H;
//这里皆不指定维数大小
for (int k = 0; k < imax; k++)
{
grad(out df, x0);
//求的梯度向量
hess(out H, x0);
//求Hessian矩阵
//实际方程为 H dx= -df
//这里转一次负号,重载算符
//df = 0 - df;
pivot(H, df, out dx);
x1 = x0 - dx;
//------------------------
//向量的模,算符重载
double err = ~dx;
//满足精度时退出循环
if (err < tol)
break;
fout.WriteLine("{0,5:D3}{1,12:F6}{2,12:F6}",
k, x1[0], x1[1]);
x0 = x1;
}
x = x0;
func(out fx, x);
}
public void pivot(MAT A, VEC b, out VEC x)
{
/*------------------------------------------ comment
Author : syz
Date : 2011-06-20 10:59:59
---------------------------------------------------
Desciption :
* 消去法解线性方程组
Post Script :
* 要用到上三角方程的回代方法
paramtegers :
* A---方程组系数矩阵
* b---右向量
* x---计算结果
-------------------------------------------------*/
int n, i, j, k;
n = b.dim;
//方程维数
double tmp;
//增广矩阵
MAT Ab = new MAT(n, n + 1);
//赋值给增广矩阵
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
Ab.ele[i, j] = A.ele[i, j];
Ab.ele[i, n] = b.ele[i];
}
double elmax;
int id_max;
double tmp1, tmp2;
//这段是高斯消去法的核心
for (k = 0; k <= n - 2; k++)
{
//主元素初值
elmax = Math.Abs(Ab.ele[k, k]);
//主元素标号
id_max = k;
//下面一段为找出主元素所在的行
//即获得主元素标号的值
for (i = k + 1; i <= n - 1; i++)
{
if (Math.Abs(Ab.ele[i, k]) > elmax)
{
elmax = Math.Abs(Ab.ele[i, k]);
//注意这里把该元素的绝对值复制给最大元素
id_max = i;
}
}
//n+1个元素,该循环为交换两行元素
for (i = 0; i < n + 1; i++)
{
tmp1 = Ab.ele[k, i];
tmp2 = Ab.ele[id_max, i];
Ab.ele[k, i] = tmp2;
Ab.ele[id_max, i] = tmp1;
}
//交换后实施消去
for (i = k + 1; i <= n - 1; i++)
{
tmp = Ab.ele[i, k] / Ab.ele[k, k];
for (j = 0; j < n + 1; j++)
Ab.ele[i, j] = Ab.ele[i, j] - tmp * Ab.ele[k, j];
}
}
//在C#中使用传值方法,不会改变原始变量的值
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
A.ele[i, j] = Ab.ele[i, j];
b.ele[i] = Ab.ele[i, n];
}
uptri(A, b, out x);
}
public void uptri(MAT A, VEC b, out VEC x)
{
/*------------------------------------------ comment
Author : syz
Date : 2011-06-17 10:04:27
---------------------------------------------------
Desciption :
* 上三角矩阵方程解法
Post Script :
* 要用到MAT 与 VEC 类
paramtegers :
* A-----系数矩阵
* b-----右向量
* x-----方程的解
-------------------------------------------------*/
int N;
N = A.dim1;
//获取方程维数
x = new VEC(N);
x.ele[N - 1] = b.ele[N - 1] / A.ele[N - 1, N - 1];
for (int i = N - 2; i >= 0; i--)
{
x.ele[i] = b.ele[i];
for (int j = i + 1; j <= N - 1; j++)
x.ele[i] = x.ele[i] - A.ele[i, j] * x.ele[j];
x.ele[i] = x.ele[i] / A.ele[i, i];
}
}
}
其c#代码为如下
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
class cDFP
/*--------------------------------------class comment
Version : V1.0
Coded by : syz
Date : 2011-07-11 15:24:20 *星期一*
----------------------------------------------------
Desciption : 变尺度之DFP方法
*
parameters :
* fout-------输出文件对象
*
Methods :
* DFP-------DFP方法函数
--------------------------------------------------*/
{
public StreamWriter fout=new StreamWriter("fout.txt");
public void DFP(out VEC x,out double fx,VEC x0)
/*------------------------------------------ comment
Author : syz
Date : 2011-07-11 15:27:17
---------------------------------------------------
Desciption : DFP方法
*
Post Script :
* 这里的矩阵与向量运算较为复杂,我们把复杂的矩阵向量运算
* 通过算符重载的方法写入矩阵和向量类
* 如此,这里便可以像MATLAB那样较为方便的使用各种矩阵运算方法
* 注意在DFP中,没有矩阵求逆或解线性方程这一步,正是变尺度法的优势
paramtegers :
* x----------极值点自变量值
* fx---------函数极小值
* x0---------初值
* ~~~~~以下变量放在方法内部控制,也可以放在类的数据成员中
* 或者直接以参数形式放在方法参数中
* imax-------最大允许迭代次数
* tol--------误差容限
-------------------------------------------------*/
{
int imax=200;
//最大允许迭代次数
double tol=1e-8;
//误差容限
int N = x0.dim;
//自变量维数
MAT H = new MAT(N, N);
for (int i = 0; i < N; i++)
H[i, i] = 1.0;
//设置H矩阵对角元素
VEC g0, g1, p,q,d;
x = new VEC(N);
grad(out g0, x0);
for (int k = 0; k < imax; k++)
{
//这里为 H*g 矩阵和向量的乘积
//采用运算符重载,重载函数见作者编写的
//矩阵向量类
d =-(H|g0);
double namda;
goldsec(out namda, 0, 1, x0, d);
x = x0 + namda * d;
//调用梯度函数
grad(out g1, x);
p = x - x0;
q = g1 - g0;
//这里运算符多次重载
//意义也有很多重
//这里之所以可以如此简洁
//是因为通过算符重载把复杂的矩阵运算
//放在了矩阵与向量类中
H = H + (p ^ p) / (p | q)
- (((H | q) ^ q) | H )/ (q |( H | q));
fout.WriteLine("{0,5:D3}{1,12:F6}{2,12:F6}",
k, x[0], x[1]);
x0 = x;
g0 = g1;
//算符重载,计算向量的2范数
double err = ~p;
if (err < tol)
break;
}
func(out fx, x);
}
public void goldsec(out double namda, double a, double b, VEC XX, VEC d)
/*------------------------------------------ comment
Author : syz
Date : 2011-07-05 12:11:39
---------------------------------------------------
Desciption : 黄金分割法一维搜索
*
Post Script :
* 控制变量----
* 1.最大迭代次数
* 2.误差容限
* 这两个量放在程序内控制
* 叙述较为简洁的方法可以参看
paramtegers :
* fx----函数极小值
* x-----极小值时候的自变量
* [a,b]--搜索区间
-------------------------------------------------*/
{
const int imax = 200;
//最大允许迭代次数
const double tol = 1e-7;
//误差容限
//这黄金分割比在外面算好,不带入循环
double g = (Math.Sqrt(5) - 1) / 2;
double x1 = a + (1 - g) * (b - a);
double x2 = a + g * (b - a);
double f1, f2;
func(out f1, XX + x1 * d);
func(out f2, XX + x2 * d);
for (int k = 0; k < imax; k++)
{
if (f1 < f2)
{
b = x2;
x2 = x1;
x1 = a + (1 - g) * (b - a);
f2 = f1;
//注意这里的形式,真正的变量是x1,XX和d是参数
func(out f1, XX + x1 * d);
}
else
{
a = x1;
x1 = x2;
x2 = a + g * (b - a);
f1 = f2;
//真正的变量是x2,XX和d是参数
func(out f2, XX + x2 * d);
}
double err = Math.Abs(b - a);
if (err < tol) break;
}
namda = (a + b) / 2;
}
}
上一节已经提到DFP与BFGS都是著名的拟牛顿算法系列,BFGS矩阵校正格式为 \[{{\bf{H}}_{k + 1}}{\bf{ = }}\left( {{\bf{I - }}\frac{{{{\bf{p}}_k}{\bf{q}}_k^T}}{{{\bf{p}}_k^T{{\bf{q}}_k}}}} \right){{\bf{H}}_k}\left( {{\bf{I - }}\frac{{{{\bf{p}}_k}{\bf{q}}_k^T}}{{{\bf{p}}_k^T{{\bf{q}}_k}}}} \right){\bf{ + }}\frac{{{{\bf{p}}_k}{\bf{p}}_k^T}}{{{\bf{p}}_k^T{{\bf{q}}_k}}}\] BFGS算法与DFP类似,这里给出其算法。
其c#代码为如下
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
class cBFGS
/*--------------------------------------class comment
Version : V1.0
Coded by : syz
Date : 2011-07-11 15:24:20 *星期一*
----------------------------------------------------
Desciption : 变尺度之BFGS方法
*
parameters :
* BFGS各种矩阵运算方法通过矩阵向量类实现
* 请参考作者编写的矩阵向量类
*
Methods :
* BFGS-------BFGS方法
* goldsec----一维搜索方法
* func-------目标函数
* grad-------梯度函数
* drive------驱动函数
--------------------------------------------------*/
{
public StreamWriter fout = new StreamWriter("fout.txt");
public void BFGS(out VEC x, out double fx, VEC x0)
/*------------------------------------------ comment
Author : syz
Date : 2011-07-11 15:27:17
---------------------------------------------------
Desciption : 拟牛顿之BFGS方法
*
Post Script :
*
paramtegers :
* x--------极值点自变量
* fx-------函数极小值
* x0-------初值
*~~~~~~~~~~~~~以下变量放在函数内控制
* imax-------最大允许迭代次数
* tol--------误差容限
-------------------------------------------------*/
{
int imax = 200;
//最大允许迭代次数
double tol = 1e-8;
//误差容限
int N = x0.dim;
//自变量维数
MAT H = new MAT(N, N);
for (int i = 0; i < N; i++)
H[i, i] = 1.0;
//设置H矩阵对角元素
VEC g0, g1, p, q, d;
x = new VEC(N);
grad(out g0, x0);
MAT E;
E = H; //E为单位矩阵
MAT TMP1 = new MAT(N, N);
for (int k = 0; k < imax; k++)
{
//这里为 H*g 矩阵和向量的乘积
//采用运算符重载,重载函数见作者编写的
//矩阵向量类
d = -(H | g0);
double namda;
goldsec(out namda, 0, 1, x0, d);
x = x0 + namda * d;
//调用梯度函数
grad(out g1, x);
p = x - x0;
q = g1 - g0;
//这里运算符多次重载
//意义也有很多重
//这里之所以可以如此简洁
//是因为通过算符重载把复杂的矩阵运算
//放在了矩阵与向量类中
TMP1 = (p ^ q) / (p | q);
TMP1 = E - TMP1;
H = TMP1 | H | (~TMP1)
+ (p ^ q) / (p | q);
//-------以上为BFGS核心部分
fout.WriteLine("{0,5:D3}{1,12:F6}{2,12:F6}",
k, x[0], x[1]);
x0 = x;
g0 = g1;
//算符重载,计算向量的2范数
double err = ~p;
if (err < tol)
break;
}
func(out fx, x);
}
public void goldsec(out double namda, double a, double b, VEC XX, VEC d)
/*------------------------------------------ comment
Author : syz
Date : 2011-07-05 12:11:39
---------------------------------------------------
Desciption : 黄金分割法一维搜索
*
Post Script :
* 控制变量----
* 1.最大迭代次数
* 2.误差容限
* 这两个量放在程序内控制
paramtegers :
* fx----函数极小值
* x-----极小值时候的自变量
* [a,b]--搜索区间
-------------------------------------------------*/
{
const int imax = 200;
//最大允许迭代次数
const double tol = 1e-7;
//误差容限
//这黄金分割比在外面算好,不带入循环
double g = (Math.Sqrt(5) - 1) / 2;
double x1 = a + (1 - g) * (b - a);
double x2 = a + g * (b - a);
double f1, f2;
func(out f1, XX + x1 * d);
func(out f2, XX + x2 * d);
for (int k = 0; k < imax; k++)
{
if (f1 < f2)
{
b = x2;
x2 = x1;
x1 = a + (1 - g) * (b - a);
f2 = f1;
//注意这里的形式,真正的变量是x1,XX和d是参数
func(out f1, XX + x1 * d);
}
else
{
a = x1;
x1 = x2;
x2 = a + g * (b - a);
f1 = f2;
//真正的变量是x2,XX和d是参数
func(out f2, XX + x2 * d);
}
double err = Math.Abs(b - a);
if (err < tol) break;
}
namda = (a + b) / 2;
}
}
拟牛顿法是非线性无约束最优化方法中最为有效的一类方法。该系列方法有许多优点, 如该系列算法不需要计算Hesse矩阵,并且具有二次终止性,对于一般情形有超线性收敛速度。