优化方法总结续篇:下降单纯形法(downhill simplex) 及python示例代码

优化方法总结续篇:下降单纯形法(downhill simplex) 及python示例代码 下降单纯形法(downhill simplex method)是一个广泛使用的“derivative free”的优化算法。一般来说它的效率不高,但是文献[1]提到“the downhill simplex method may frequently b...
摘要由CSDN通过智能技术生成

下降单纯形法(downhill simplex method)是一个广泛使用的“derivative free”的优化算法。一般来说它的效率不高,但是文献[1]提到“the downhill simplex method may frequently be the *best* method to use if the figure of merit is “get something working quickly” for a problem whose computational burden is small.”

单纯形法的基本思路是在\(N\)维空间中,构造一个非退化的初始单纯形,然后做一系列的几何操作,如反射、扩展、收缩等,逐步往极值点移动该单纯形。由于这些几何操作的目的基本上都是让单纯形往极小值移动,所以叫下降单纯形法。

假设待优化的函数为\(f(\mathbf{x})\),\(N\)维空间里的单纯形\(Z\)的\(N+1\)个顶点按照函数值从小到大排列分别为\(\mathbf{x}_{0},\mathbf{x}_{2},\cdots,\mathbf{x}_{N}\),定义


\(\bar{\mathbf{x}}=\sum_{i=0}^{N-1}\mathbf{x}_{i}\)


为\(Z\)中除了顶点\(\mathbf{x}_{N}\)之外其余顶点的中心点。

连接\(\bar{\mathbf{x}}\)和\(\mathbf{x}_{N}\)的直线公式可以写成:


\(\bar{\mathbf{x}}(t)=(1-t)\bar{\mathbf{x}}+t\mathbf{x}_{N}\)


下降单纯形法就是从沿着直线$\bar{\mathbf{x}}(t)$方向的几个特殊步长寻找\(\mathbf{x}_{N}\)的替代点,使该替代点处的函数值比\(\mathbf{x}_{N}\)更小,如果没有找到这种替代点,那么就把除了\(\mathbf{x}_{0}\)点之外的其余点往\(\mathbf{x}_{0}\)靠拢。

假设是要求函数的极小值,可以把对应函数值越小的点认为越好,越大的点认为越差。下降单纯形法每步迭代过程简述如下:

  1. 首先计算最差点沿着直线$\bar{\mathbf{x}}(t)$关于平均点$\bar{\mathbf{x}}$的对称点
  2. 如果对称点介于最好次差点之间,那么就接受它(reflection);
  3. 如果对称点比最好点还好,那么做沿该方向更大胆的尝试,令\(t=-2\),如果新尝试点比对称点更好则接受新尝试点(expand),否则接受当前对称点(reflection);
  4. 如果对称点介于次差点和最差点之间,那么沿该方向做更小心的尝试,即令\(t=-0.5\),如果新尝试点比对称点更好则接受新尝试点(outside contraction)
  5. 如果对称点比最差点还差,那么沿反方向做尝试,即令\(t=0.5\),如果新尝试点比对称点更好则接受新尝试点(inside contraction)
  6. 如果4和5均失败,即对称点比次差还要差而且outside contraction与inside contraction均失败,那么把最好点之外的其他点都朝最好点收缩(shrink)


上述过程如果用区间图表示会更清晰,区间的三个分界点就是最好次差最差点。对应的伪代码可以参考文献 [3] 第9.5节。

上述迭代算法需要提供一个初始单纯形,该单纯形可以参考文献[1]给出的方法得到。首先任选一点\(\mathbf{x}_{0}\),然后利用以下公式:


\(\mathbf{x}_{i}=\mathbf{x}_{0}+\lambda\mathbf{e}_{i}\)


其中\(\mathbf{e}_{i}\)代表\(N\)维空间的单位矢量,\(\lambda\)表示步长。


另外,在实际应用中还有一个很重要的诀窍,即重启(restart)。因为单纯形在迭代更新的时候很容易就卡在某个中间位置上,这时单纯形的 *最好* 和 *最差* 点几乎相同,单纯形的体积收缩的很小,会大大减慢迭代速度。为了解决这个问题,可以合理设置初始单纯形的大小。更有效的,就是可以在单纯形卡住的时候通过重新初始化单纯形来加快收敛速度。在利用初始化公式的时候,把当前单纯形的 *最好* 点作为\(\mathbf{x}_{0}\)保留下来,这样保证重启就不会影响之前已经计算的结果。


下面给出了单纯形法求解一个简单问题的python实现,其中待优化函数为\(f(\mathbf{x})=(\mathbf{x}-\mathbf{x}_{0})(\mathbf{x}-\mathbf{x}_{1})\)

首先定义一些需要用到的函数

import numpy as np
from matplotlib import pyplot as plt
import seaborn as sns

def vertice_init(vertex_0, step_length):
    '''
    initialize vertice of the simplex
    using the following formula:
    $xi=x0+step_length*ei$
    '''

    emat = np.eye(vertex_0.size) * step_length
    vertice = [vertex_0]
    for ii in range(vertex_0.size):
        vertice.append(vertex_0 + emat[:, ii])
    return vertice


def f(v):
    '''
    Evaluation of Function $f$
    '''
    dim = v.size
    v0 = np.ones(dim) * 5
    v1 = np.ones(dim) * 3
    return 0.5 * np.dot(v - v0, v - v1)


def line(t, v1, v2):
    return (1 - t) * v1 + t * v2

接下来定义算法主函数,注意里面的restart部分:

def simplex(f, vertice, maxit=1000, step_length=100, tol=1e-3):
    vertice_max_list = []  # store the max vertex during each iteration
    vertice_min_list = []  # store the min vertex during each iteration
    for jj in range(maxit):
        y = []
        for ii in vertice:
            y.append(f(ii))
        y = np.array(y)
        #  only the highest (worst), next-highest, and lowest (best) vertice
        # are neeed
        idx = np.argsort(y)
        vertice_max_list.append(vertice[idx[-1]])
        vertice_min_list.append(vertice[idx[0]])
        
        # centroid of the best n vertice
        # NOTE: the worst vertex should be excluded, but for simplicity we don't do this
        v_mean = np.mean(vertice)

        # compute the candidate vertex and corresponding function vaule
        v_ref = line(-1, v_mean, vertice[idx[-1]])
        y_ref = f(v_ref)
        if y_ref >= y[idx[0]] 
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 下山单纯形法是一种数学优化方法,用来求解线性规划问题。它可以用多种编程语言实现,比如C/C++、Java、Python等。下面是一个用Python实现下山单纯形法示例代码:import numpy as npdef simplex(A, b, c): n, m = A.shape assert n == len(b) assert m == len(c) B = np.array(range(m, m + n)) cB = c[B] AB = A[:, B] xB = np.linalg.solve(AB, b) while True: c_red = c - np.dot(cB.T, np.linalg.inv(AB).dot(A)) j = np.argmax(c_red) if c_red[j] <= 0: break u = np.linalg.solve(AB, A[:, j]) if np.all(u <= 0): return None theta = np.min(xB / u) xB = xB - theta * u i = np.argmin(xB / u) B[i] = j cB[i] = c[j] return xB, cB.sum() ### 回答2: 下山单纯形法(Nelder-Mead算法)是一种无约束优化算法,用于求解无约束非线性优化问题。其代码实现可以按照以下步骤进行: 1. 首先,定义目标函数,确定需要优化的变量个数和初始取值,设置收敛条件(例如最大迭代次数或误差限制)。 2. 创建一个初始简单形状,例如一个等边三角形,每个顶点代表一个变量取值组合。 3. 计算每个顶点对应的目标函数值,并按照目标函数值的大小对顶点进行排序。 4. 计算重心(剔除最差的顶点,即目标函数值最大的顶点,并计算其余顶点的质心)。 5. 尝试寻找更好的顶点(通过尝试不同的运算符,例如反射、扩展和收缩来获得新的顶点)。 6. 根据新的顶点计算目标函数值,并按照目标函数值的大小对顶点进行排序。 7. 检查是否满足终止条件,如达到最大迭代次数或目标函数值足够小。 8. 如果未满足终止条件,则返回第3步,继续优化。 这是一个简单的概述,实际代码的细节还要根据具体的编程语言和问题进行相应的实现。这个算法的关键是在每一步基于当前顶点选择合适的运算符来生成新的顶点,并通过比较目标函数值来进行排序。 ### 回答3: 下山单纯形法Downhill simplex method)是一种用于非线性优化的算法。其基本思想是通过构建一个单纯形(simplex)的几何结构,在每一步迭代中不断缩小目标函数的范围,最终找到目标函数的最小值点。 下山单纯形法代码的实现主要包括以下几个步骤: 1. 初始化算法参数:包括单纯形的维度、单纯形各个顶点的初始化值、目标函数、迭代停止条件等。 2. 计算目标函数的值:根据当前单纯形的顶点坐标,计算目标函数在各个顶点上的值。 3. 对单纯形的顶点进行排序:根据目标函数值的大小,对单纯形的顶点进行排序,将目标函数值最小的顶点放在第一个位置。 4. 计算单纯形的重心:将目标函数值最小的顶点除外,通过计算剩余顶点的平均值,得到当前单纯形的重心坐标。 5. 根据重心坐标生成新的顶点:通过一定的步长和方向,生成离重心最远的点,也即单纯形的扩张点。 6. 比较新点和目标函数值最小的顶点,确定是否替换:如果新点的目标函数值小于目标函数值最小的顶点,则进行替换操作;否则,进行下一步。 7. 判断迭代停止条件:判断是否满足迭代停止条件,如达到最大迭代次数或目标函数值的变化小于一定阈值,如果满足则结束迭代,否则返回第3步。 8. 返回结果:返回当前单纯形各个顶点的坐标,以及目标函数在最小值点的值。 需要注意的是,下山单纯形法代码实现比较复杂,需要仔细理解并正确实现各个步骤。同时,还需要根据具体问题进行适当的调整和优化,以提高算法的效率和准确性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值