外点惩罚函数法·约束优化问题

外点惩罚函数法·约束优化问题

外点法惩罚函数(r增加,SUMT.java)用于求解约束优化问题,解题步骤如下:

Step1 输入目标函数与约束方程,构建外点惩罚函数法求解方程,求解初始化。

Step2 对求解方程进行一次无约束优化方法求解(鲍威尔BWE),得到新解。

Step3 新解与原解求误差,如误差满足精度要求,则输出解,否则增加因子r,执行Step 2。

鲍威尔法(BWE.java)是N维无约束求解方法,需要调用一维求解方法,一维求解方法采用黄金分割法(GSM.java)。

在实现算法的代码中,我去掉了输入处理,人为地将输入确定下来,可减少代码篇幅。

我会将文件打包放入我的下载,欢迎大家一起交流。

(1)外点法惩罚函数 SUMT.java:

package ODM.Method;

import java.util.Arrays;
/*
 * 无约束优化方法:惩罚函数法·外点法
 */
public class SUMT {

	private int n = 6;							// 维数,变量个数
	private final double eps = 1e-5;			// 精度
	private final double c = 5;				// 递增系数
	private double r = 0.1;					// 惩罚因子,趋向无穷
	public SUMT(){
		Finit();
		AlgorithmProcess();
		AnswerOutput();
	}
	// 结果
	private double[] xs;
	private double ans;
	private void Finit(){
		xs = new double[n];
		Arrays.fill(xs, 0);
		ans = -1;
		//xs[0] = xs[1] = xs[2] = xs[4] = 1;	xs[3] = 3;	xs[5] = 5;
	}
	// 算法主要流程
	private void AlgorithmProcess(){
		int icnt = 0;						// 迭代次数
		double[] x = new double[n];		// 转化为无约束优化问题的解
		while(true){
			icnt++;
			BWE temp = new BWE(n, r, xs);	// 采用鲍威尔方法求函数最优解
			x = temp.retAns();
			if(retOK(x) <= eps){			// 满足精度要求
				for(int i = 0; i < n; i++)
					xs[i] = x[i];
				ans = temp.mAns();
				break;
			}
			r = c * r;
			for(int i = 0; i < n; i++)
				xs[i] = x[i];
		}
		System.out.println("迭代次数:" + icnt);
	}
	// 收敛条件(只有一个,不完善)
	private double retOK(double[] x){
		double sum = 0;
		for(int i = 0; i < n; i++){
			sum += Math.pow(x[i] - xs[i], 2);
		}
		return Math.sqrt(sum);
	}
	// 结果输出
	private void AnswerOutput(){
		for(int i = 0; i < n; i++)
			System.out.printf("%.6f\t", xs[i]);
		System.out.printf("%.6f\n", ans);
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		new SUMT();
	}

}

(2)鲍威尔法 BWE.java:

package ODM.Method;

import java.util.Arrays;

public class BWE {

	private double r;
	// 初始化变量
	private double[] x0;						// 初始解集
	private double[][] e;						// 初始方向
	private int N;
	final private double eps = 1e-5;
	private Func F;

	// 初始化:初始点, 初始矢量(n 个,n*n 矩阵), 维数
	private void Init(int n){
		this.x0 = new double[n];
		if(r == -1)
			Arrays.fill(this.x0, 0);
		else{

		}

		this.e = new double[n][n];
		for(int i = 0; i < n; i++){
			for(int j = 0; j < n; j++){
				if(i != j)e[i][j] = 0;
				else e[i][j] = 1;
			}
		}

		this.N = n;
		if(r != -1)
			F = new Func(r);
		else
			F = new Func();
	}

	// 搜索点, 方向矢量
	private double[][] x;
	private double[][] d;

	// 方向重排, 队列操作
	private void queueDir(double[] X){
		// 删去首方向
		for(int i = 0; i < N-1; i++){
			for(int j = 0; j < N; j++){
				d[i][j] = d[i+1][j];
			}
		}
		// 新方向插入队尾
		for(int i = 0; i < N; i++)
			d[N-1][i] = X[i];
	}

	private void Process(){

		x = new double[N+1][N];
		d = new double[N][N];

		for(int j = 0; j < N; j++)
			x[0][j] = x0[j];
		for(int i = 0; i < N; i++){
			for(int j = 0; j < N; j++){
				d[i][j] = e[i][j];
			}
		}

		int k = 0;							// 迭代次数
		while(k < N){
			for(int i = 1; i <= N; i++){
				GSM t = new GSM(F, x[i-1], d[i-1]);
				x[i] = t.getOs();
			}
			double[] X = new double[N];
			for(int i = 0; i < N; i++)
				X[i] = x[N][i] - x[0][i];
			queueDir(X);
			GSM t = new GSM(F, x[N], X);
			x[0] = t.getOs();
			k++;
		}
	}
	// 答案打印
	private void AnswerOutput(){
		for(int i = 0; i < N; i++){
			System.out.printf("x[%d] = %.6f\n", i+1, x[0][i]);
//			System.out.print(x[0][i] + " ");
		}
		System.out.printf("最小值:%.6f\n", F.fGetVal(x[0]));
//		System.out.println(": " + F.fGetVal(x[0]));
	}

	public BWE(int n){
		this.r = -1;
		Init(n);
		Process();
		AnswerOutput();
	}

	public BWE(int n, double r, double[] x){
		this.r = r;
		Init(n);
		for(int i = 0; i < n; i++)
			x0[i] = x[i];
		Process();
	}
	// 返回结果,解向量和最优值
	public double[] retAns(){
		return x[0];
	}
	public double mAns(){
		return F.fGetVal(x[0], 0);
	}
	/*
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		new BWE(2);
	}*/

}

(3)黄金分割 GSM.java:

package ODM.Method;
/*
 * 黄金分割法
 */
public class GSM {

	private int N;																// 维度
	private final double landa = (Math.sqrt(5)-1)/2;							// 0.618
	private double[] x1;
	private double[] x2;
	private double[] os;
	private final double eps = 1e-5;											// 解精度
	private ExtM EM;															// 用于获取外推法结果

	// 最优值输出
	public double[] getOs() {
		return os;
	}
	// 函数, 初始点, 方向矢量
	public GSM(Func Sample, double[] x, double[] e) {
		//for(int i = 0; i < e.length; i++)System.out.print(e[i] + " ");System.out.println();
		initial(Sample, x, e);
		process(Sample);
		AnswerPrint(Sample);
	}
	// 结果打印
	private void AnswerPrint(Func Sample) {
		os = new double[N];
		for(int i = 0; i < N; i++)
			os[i] = 0.5*(x1[i] + x2[i]);

//		System.out.println("os = " + os[0] + " " + os[1]);
//		System.out.println("ans = " + Sample.fGetVal(os));

	}
	// 向量范值
	private double FanZhi(double[] b, double[] a){
		double sum = 0;
		for(int i = 0; i < N; i++){
			if(b[i] - a[i] != 0 && b[i] == 0)return eps*(1e10);
			if(b[i] == 0)continue;
			sum += Math.pow((b[i] - a[i]) / b[i], 2);
		}
		return Math.pow(sum, 0.5);
	}
	// 算法主流程
	private void process(Func Sample) {
		double[] xx1 = new double[N];
		SubArraysCopy(xx1);
		double yy1 = Sample.fGetVal(xx1);

		double[] xx2 = new double[N];
		AddArraysCopy(xx2);
		double yy2 = Sample.fGetVal(xx2);
		// 迭代过程
		while(true){
			if(yy1 >= yy2){
				ArraysCopy(xx1, x1);
				ArraysCopy(xx2, xx1);	yy1 = yy2;
				AddArraysCopy(xx2);
				yy2 = Sample.fGetVal(xx2);
			}else{
				ArraysCopy(xx2, x2);
				ArraysCopy(xx1, xx2);	yy2 = yy1;
				SubArraysCopy(xx1);
				yy1 = Sample.fGetVal(xx1);
			}
			//System.out.println(FanZhi(x2, x1) + " / " + Math.abs((yy2 - yy1)/yy2));
			if(FanZhi(x2, x1) < eps && Math.abs(yy2 - yy1) < eps)break;
		}
	}
	// 获得外推法结果:左右边界
	private void initial(Func Sample, double[] x, double[] e) {
		N = x.length;
		EM = new ExtM(Sample, x, e);
		x1 = EM.getX1();
		x2 = EM.getX3();
	}
	// 向量赋值
	private void ArraysCopy(double[] s, double[] e){
		for(int i = 0; i < N; i++)
			e[i] = s[i];
	}
		// + landa
	private void AddArraysCopy(double[] arr){
		for(int i = 0; i < N; i++)
			arr[i] = x1[i] + landa*(x2[i] - x1[i]);
	}
		// - landa
	private void SubArraysCopy(double[] arr){
		for(int i = 0; i < N; i++)
			arr[i] = x2[i] - landa*(x2[i] - x1[i]);
	}
	/*
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		double[] C = {0, 0};
		double[] d = {1, 0};
		new GSM(new Func(), C, d);
	}
	*/
}

以上算法文件包含函数方程,黄金分割时有一维搜索的外推法确定“高低高”区间。

函数方程 Func.java,外推法 ExtM.java。

Func.java:

package ODM.Method;

public class Func {

	private int N;								// N 维
	private double[] left;						// 函数左边界
	private double[] right;					// 函数右边界
	private double r;

	public Func() {
		r = -1;
	}
	public Func(double r) {
		this.r = r;
	}

	// 定义函数与函数值返回
	public double fGetVal(double[] x){
		if(r != -1)return fGetVal(x, r);
		// 10*(x1+x2-5)^2 + (x1-x2)^2
		return 10*Math.pow(x[0]+x[1]-5, 2) + Math.pow(x[0]-x[1], 2);
		//
	}
	private double max(double a, double b){return a > b ? a : b;}
	public double fGetVal(double[] x, double r){
		double ret = 0;
//		function f1
//		ret =  Math.pow(x[0]-5, 2) + 4*Math.pow(x[1]-6, 2)
//				+ r * (
//				+ Math.pow(max(64-x[0]*x[0]-x[1]*x[1], 0), 2)
//				+ Math.pow(max(x[1]-x[0]-10, 0),  0)
//				+ Math.pow(max(x[0]-10, 0), 2)
//				);

//		function f2
//		ret = x[0]*x[0] + x[1]*x[1] + r*(1-x[0]>0 ? 1-x[0] : 0)*(1-x[0]>0 ? 1-x[0] : 0);

//		function f3
		ret = Math.pow(x[0]-x[3], 2) + Math.pow(x[1]-x[4], 2) + Math.pow(x[2]-x[5], 2) +
				r * (
				+ Math.pow(max(x[0]*x[0]+x[1]*x[1]+x[2]*x[2]-5, 0), 2)
				+ Math.pow(max(Math.pow(x[3]-3, 2)+x[4]*x[4]-1, 0), 2)
				+ Math.pow(max(x[5]-8, 0), 2)
				+ Math.pow(max(4-x[5], 0), 2)
				);
		return ret;
	}
}

ExtM.java:

package ODM.Method;

/*
 * 外推法确定“高-低-高”区间
 */
public class ExtM {

	private int N;							// 函数维数
	private double[] x1;
	private double[] x2;
	private double[] x3;
	private double y1;
	private double y2;
	private double y3;
	private double h;						// 步长
	private double[] d;					// 方向矢量
	public double[] getX1() {
		return x1;
	}

	public double[] getX2() {
		return x2;
	}

	public double[] getX3() {
		return x3;
	}

	public double getH() {
		return h;
	}
	// 函数, 初始点,方向
	public ExtM(Func Sample, double[] x, double[] e) {
		initial(Sample, x, e);
		process(Sample);
		AnswerPrint();
	}
	// 初始化阶段
	private void initial(Func Sample, double[] x, double[] e)
	{
		N = x.length;
		x1 = new double[N];
		x2 = new double[N];
		x3 = new double[N];
		h = 0.01;
		d = new double[N];

		ArraysCopy(e, 0, d);
		//for(int i = 0; i < d.length; i++)System.out.print(d[i]);System.out.println();
		ArraysCopy(x, 0, x1);
		y1 = Sample.fGetVal(x1);

		ArraysCopy(x, h, x2);
		y2 = Sample.fGetVal(x2);

	}
	// 循环部分
	private void process(Func Sample)
	{
		if(y2 > y1){
			h = -h;
			ArraysCopy(x1, 0, x3);
			y3 = y1;
		}else{
			ArraysCopy(x2, h, x3);	y3 = Sample.fGetVal(x3);
		}
		while(y3 < y2){
			h = 2*h;
//			System.out.println("h = " + h);
			ArraysCopy(x2, 0, x1);		y1 = y2;
			ArraysCopy(x3, 0, x2);		y2 = y3;
			ArraysCopy(x2, h, x3);		y3 = Sample.fGetVal(x3);
//			System.out.println("x1 = " + x1[0] + " " + x1[1] + " y1 = " + y1);
//			System.out.println("x2 = " + x2[0] + " " + x2[1] + " y2 = " + y2);
//			System.out.println("x3 = " + x3[0] + " " + x3[1] + " y3 = " + y3);
		}

	}
	// 打印算法结果
	private void AnswerPrint()
	{
//		System.out.println("x1 = " + x1[0] + " " + x1[1] + " y1 = " + y1);
//		System.out.println("x2 = " + x2[0] + " " + x2[1] + " y2 = " + y2);
//		System.out.println("x3 = " + x3[0] + " " + x3[1] + " y3 = " + y3);
	}
	// 向量转移
	private void ArraysCopy(double[] s, double c, double[] e){
		for(int i = 0; i < s.length; i++)
			e[i] = d[i]*c + s[i];
	}

	/*
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		// new ExtM();
	}*/

}

外点惩罚函数法·约束优化问题

时间: 2024-10-12 18:29:29

外点惩罚函数法·约束优化问题的相关文章

Atitit 迭代法&#160;&#160;“二分法”和“牛顿迭代法&#160;attilax总结

Atitit 迭代法  "二分法"和"牛顿迭代法 attilax总结 1.1. ."二分法"和"牛顿迭代法"属于近似迭代法1 1.2. 直接法(或者称为一次解法),即一次性的快速解决问题,1 1.3. 最常见的迭代法是"二分法 牛顿法.还包括以下算法1 1.4.  二分法(dichotomie)1 1.5. 牛顿迭代法(Newton's method)又称为牛顿-拉夫逊(拉弗森)方法(Newton-Raphson method

转载 迭代算法实现开平方

迭代是数值分析中通过从一个初始估计出发寻找一系列近似解来解决问题(一般是解方程或者方程组)的过程,为实现这一过程所使用的方法统称为迭代法(Iterative Method). 一般可以做如下定义:对于给定的线性方程组x=Bx+f(这里的x.B.f同为矩阵,任意线性方程组都可以变换成此形式),用公式x(k+1)=Bx(k)+f(括号中为上标,代表迭代k次得到的x,初始时k=0)逐步带入求近似解的方法称为迭代法(或称一阶定常迭代法).如果k趋向无穷大时limt(k)存在,记为x*,称此迭代法收敛.显

007-算法-迭代法

一.概念:(Iteratice Method),迭代是数值分析中通过一个初始估计出发寻找一系列近似解解决问题(一般是解方程或者方程组)的过程,为实现这一过程所使用的方法统称为迭代法. 常见的迭代法是牛顿法.其它还包括最速下降法.共轭迭代法.变尺度迭代法.最小二乘法.线性规划.非线性规划.单纯型法.惩罚函数法.斜率投影法.遗传算法.模拟退火等等. 二.基本思想 迭代法是通过一种常用的设计方法.迭代式一个不断用新值取代变量的旧值,或者由旧值递推出变量的新值的过程.迭代机制需要以下一些要素: ① 迭代

[转]JQuery控制div外点击隐藏,div内点击不会隐藏

一直弄清楚这个效果如何实现,看了这篇博客的几行代码原来如此简单. 比如有个div其id为body,实现在div外点击隐藏,div内点击不隐藏,采用jQuery实现如下: $("#body").click(function(e) { $(this).show(); e.stopPropagation(); }); $(document).click(function(event) { $("#body").hide(); }); 如果div内点击隐藏,可采用jQuer

带约束优化问题 拉格朗日 对偶问题 KKT条件

转自:七月算法社区http://ask.julyedu.com/question/276 咨询:带约束优化问题 拉格朗日 对偶问题 KKT条件 关注 | 22 ... 咨询下各位,在机器学习相关内容中,每次看到带约束优化问题,总是看到先用拉格朗日函数变成无约束问题,然后转成求拉格朗日对偶问题,然后有凸函数假设,满足KKT条件时原问题最优解和对偶问题最优解等价. 每次看到这个,总不是很理解为什么要这么做?为什么首先转为无约束问题(这个相对好理解一点,因为容易处理)为什么拉格朗日函数无约束问题要转变

数值算法:无约束优化之一维搜索方法之黄金分割法、斐波那契数列法

目标函数为一元单值函数f:R->R的最小化优化问题,一般不会单独遇到,它通常作为多维优化问题中的一个部分出现,例如梯度下降法中每次最优迭代步长的估计. 一维搜索方法是通过迭代方式求解的,这不同于我们人脑的直接通过解表达式求解方法.迭代算法是从初始搜索点x(0)出发,产生一个迭代序列x(1),x(2),....在第k=0,1,2,...次迭代中,通过当前迭代点x(k)和目标函数 f 构建下一个迭代点x(k+1).某些算法可能只需要用到迭代点处的目标函数值,而另一些算法还可能用到目标函数的导数 f'

无约束优化方法

本文讲解的是无约束优化中几个常见的基于梯度的方法,主要有梯度下降与牛顿方法.BFGS 与 L-BFGS 算法,无约束优化的问题形式如下,对于 $x \in \mathbb{R}^n$ ,目标函数为: \[\min_xf(x)\] 泰勒级数 基于梯度的方法都会涉及泰勒级数问题,这里简单介绍一下,泰勒级数就是说函数 $f(x)$ 在点 $x_0$ 的邻域内具有 $n+1$ 阶导数,则该邻域内 $f(x)$ 可展开为 $n$ 阶泰勒级数为: \[f(x) = f(x_0) + \nabla f(x_0

约束优化方法之拉格朗日

这里的主题是约束优化方法,在求取有约束条件的优化问题时,拉格朗日乘子法(Lagrange Multiplier) 和KKT条件是非常重要的两个求取方法,对于等式约束的优化问题,可以应用拉格朗日乘子法去求取最优值:如果含有不等式约束,可以应用KKT条件去求取.当然,这两个方法求得的结果只是必要条件,只有当是凸函数的情况下,才能保证是充分必要条件.需要注意KKT条件是拉格朗日乘子法的泛化. 待解决问题: \begin{aligned} &\min_{x \in \mathbb{R}^n} \  f(

高数之拉格朗日乘法---解决约束优化问题

作为一种优化算法,拉格朗日乘子法主要用于解决约束优化问题,它的基本思想就是通过引入拉格朗日乘子来将含有n个变量和k个约束条件的约束优化问题转化为含有(n+k)个变量的无约束优化问题.拉格朗日乘子背后的数学意义是其为约束方程梯度线性组合中每个向量的系数. 如何将一个含有n个变量和k个约束条件的约束优化问题转化为含有(n+k)个变量的无约束优化问题?拉格朗日乘数法从数学意义入手,通过引入拉格朗日乘子建立极值条件,对n个变量分别求偏导对应了n个方程,然后加上k个约束条件(对应k个拉格朗日乘子)一起构成