首页 > 代码库 > 外点惩罚函数法·约束优化问题

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

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

       外点法惩罚函数(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();
	}*/

}