最优化过程中不能满足约束条件的一些解决方案

背景:最近我在做一些求最优化的问题。
有上下界约束,有等式约束和不等式约束,其中在不等式约束中需要变量x0,x1,x2,x3(出库)分别大于x4,x5,x6,x7(发电),这个约束对我的实际问题来说很重要,但是呢,在巡游的过程中,并不能保证完全不出现这种情况,尤其当我因为运算速度比较慢,将种子数量和迭代次数都减少了以后,这种情况变得更多了。

我用的算法库:scikit-opt,算法我用的GA
https://github.com/scikit-opt/scikit-opt

代码示例,不一定正确,就说一下几个关键点的使用方法:
def obj_func(p):
    x1, x2, x3 = p
    return x1 ** 2 + x2 ** 2 + x3 ** 2


constraint_eq = [
    lambda x: 1 - x[1] - x[2]
]

constraint_ueq = [
    lambda x: 1 - x[0] * x[1],
    lambda x: x[0] * x[1] - 5
]

ga = GA(func=obj_func, n_dim=3, size_pop=50, max_iter=100, 
        prob_mut=0.1, lb=[-5, -5, -5], ub=[5, 5, 5], precision=[1e-3, 1e-3, 1e-3])

经过网上搜索和自己的思考总结了以下几种解决方案:

1.约束上下界

这种办法简单直接有效,但是对我的实际情况来说没办法用。

2.用precision控制精度

这个不保证一定能起作用,且精度太小会导致迭代很难收敛。

3.在obj-fun里面加一个很大的惩罚因子

首先我们迭代的目的就是为了让obj-fun最小(如果要最大,你就在前面加个负号),你用下面的方法如果不满足你的约束,你就给他强制返回一个很大很大的数,这样就让迭代过程不选择这组解。但是还是有问题,比如他也不能完全保证不出现这样的解,或者不靠近这样的解,但是我们却不想要这样的解,所以种方案会使得计算过程变慢。

def objective(x):
    penalty = 1e6  # 惩罚因子
    if x[0] < -5 or x[0] > 5 or x[1] < -5 or x[1] > 5:
        return penalty  # 约束不满足,给一个大惩罚值
    return x[0]**2 + x[1]**2  # 目标函数

ga = GA(func=objective, n_dim=2, size_pop=50, max_iter=100, lb=[-5, -5], ub=[5, 5])

4.在不等式约束中添加

这个我当然当的添加了,但是没起作用。

5.自定义修正算子

在 GA 运行过程中,每次 变异 和 交叉 之后,手动调整解 让其符合约束。

ga.mutation 重新定义了 GA 的变异过程,确保每次更新都满足约束。

这个方法最直接有效,不影响 GA 本身的搜索能力!
具体如下:

import numpy as np
from sko.GA import GA

def objective(x):
    return sum(x)  # 目标函数示例

def constraint_correction(X):
    """ 修正解,使其满足 x0, x1, x2, x3 >= x4, x5, x6, x7 """
    for i in range(len(X)):  # 遍历种群中的每个个体
        X[i, :4] = np.maximum(X[i, :4], X[i, 4:])  # 修正前4个变量 >= 后4个变量
    return X

ga = GA(func=objective, n_dim=8, size_pop=50, max_iter=100, prob_mut=0.1, 
        lb=[-10] * 8, ub=[10] * 8)

# 重写变异函数,保证变异后的个体满足约束
ga.mutation = lambda X: constraint_correction(X + ga.mutation(X))

best_x, best_y = ga.run()
print("最优解:", best_x, "最优值:", best_y)

6.直接调整 GA 交叉策略

交叉(Crossover)时 确保新个体满足约束,可以手动调整 crossover 逻辑。

这个方法不会破坏 GA 的搜索能力,只是确保每次交叉生成的新解都符合约束。

适用于约束不复杂的情况,效果很好!

def custom_crossover(X):
    """ 交叉时修正解,使其满足 x0, x1, x2, x3 >= x4, x5, x6, x7 """
    X[:, :4] = np.maximum(X[:, :4], X[:, 4:])  # 强制前 4 个变量大于后 4 个
    return X

ga = GA(func=objective, n_dim=8, size_pop=50, max_iter=100, prob_mut=0.1, 
        lb=[-10] * 8, ub=[10] * 8)

ga.crossover = custom_crossover  # 替换默认交叉策略

best_x, best_y = ga.run()
print("最优解:", best_x, "最优值:", best_y)

总结

3.最简单直接,但是可能会收敛得慢,因为 GA 仍然会探索被惩罚的区域,而且也不保证完全有效。
5.可以在任何情况下使用,强制修正解,直接有效,缺点是可能略影响搜索。
6.适用于轻量约束问题,只影响交叉阶段,不影响变异,缺点是可能仍有超界情况。

算了毁灭吧,我都试了,没有保证有效的,可能跟初始值有关系,等我学会设定初始值了再来。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容