非线性规划问题
一般形式
min F ( x ) subject to: g i ( x ) ≥ 0 ( i = 1 , 2 , ⋯ , p ) h j ( x ) = 0 ( j = q + 1 , q + 2 , ⋯ , q ) \begin{aligned} & \min F(x) \\ & \text{subject to:} & & g_i(x) \geq 0 \quad (i = 1, 2, \cdots, p) \\ & & & h_j(x) = 0 \quad (j = q+1, q+2, \cdots, q) \end{aligned} minF(x)subject to:gi(x)≥0(i=1,2,⋯,p)hj(x)=0(j=q+1,q+2,⋯,q)
选址问题
问题背景
设某城市有某种物品的10个需求点,第 i i i个需求点 p i p_{i} pi的坐标为(a,b),道路网与坐标轴平行,彼此正交。现打算建一个物品的供应中心,且由于受到城市某些条件的限制,该供应中心只能设在 x x x界于[3,8], y y y界于[4,10]的范围之内(可以取小数)。问该中心应建在何处为好?
a i a_i ai | 1 | 4 | 3 | 5 | 9 | 12 | 6 | 20 | 17 | 8 |
---|---|---|---|---|---|---|---|---|---|---|
b i b_i bi | 2 | 10 | 8 | 18 | 1 | 4 | 5 | 10 | 8 | 9 |
问题分析
这里来使用曼哈顿距离来定义两点之间的距离,曼哈顿距离(Manhattan Distance)是一种基于网格的距离度量方法。在二维平面中,两个点 (
x
1
x_1
x1,
y
1
y_1
y1)和 (
x
2
x_2
x2,
y
2
y_2
y2))之间的曼哈顿距离定义为:
d
Manhattan
=
∣
x
1
−
x
2
∣
+
∣
y
1
−
y
2
∣
d_\text{Manhattan}=|x_1-x_2|+|y_1-y_2|
dManhattan=∣x1−x2∣+∣y1−y2∣
由于城市的道路网是平行于坐标轴的,因此两个点之间的距离计算使用曼哈顿距离是合理的。我们需要在给定的坐标范围内选择一个最佳位置 (x,y),使得供应中心到所有需求点的最大曼哈顿距离最小。这一问题实际上是一个优化问题,目标是最小化最大曼哈顿距离。
模型建立
目标函数
我们需要最小化从供应中心到所有需求点的总距离。这种情况下,距离是曼哈顿距离(因为道路网与坐标轴平行且彼此正交),即目标函数为:
min
∑
i
=
1
10
(
∣
x
−
a
i
∣
+
∣
y
−
b
i
∣
)
\min\sum_{i=1}^{10}\left(|x-a_i|+|y-b_i|\right)
mini=1∑10(∣x−ai∣+∣y−bi∣)
约束条件
该供应中心只能设在
x
x
x界于[3,8],
y
y
y界于[4,10]的范围之内,所以供应中心的坐标需要满足以下约束:
3
≤
x
≤
8
4
≤
y
≤
10
\begin{aligned}3&\leq x\leq8\\4&\leq y\leq10\end{aligned}
34≤x≤8≤y≤10
建立完整的优化模型
min
∑
i
=
1
10
(
∣
x
−
a
i
∣
+
∣
y
−
b
i
∣
)
s. t.
{
3
⩽
x
⩽
8
4
⩽
y
⩽
10
\min\sum_{i=1}^{10}\left(|x-a_i|+|y-b_i|\right) \\\text{s. t.}\begin{cases}3\leqslant x\leqslant8\\4\leqslant y\leqslant10\end{cases}
mini=1∑10(∣x−ai∣+∣y−bi∣)s. t.{3⩽x⩽84⩽y⩽10
模型求解
使用SLSQL算法进行优化
-
导入必要的库
import numpy as np from scipy.optimize import minimize
numpy
:用于处理数组和数学运算的库。minimize
:scipy.optimize
模块中的一个函数,用于最小化标量函数。 -
定义需求点的坐标
a = np.array([1, 4, 3, 5, 9, 12, 6, 20, 17, 8]) b = np.array([2, 10, 8, 18, 1, 4, 5, 10, 8, 9])
a
和b
是两个数组,分别表示10个需求点在二维平面上的横坐标和纵坐标。例如,第一个需求点的坐标为 (1, 2),第二个需求点的坐标为 (4, 10)。
-
定义目标函数
def func(x): # 计算供应中心到各需求点的曼哈顿距离和 total_distance = np.sum(np.abs(x[0] - a) + np.abs(x[1] - b)) return total_distance
func(x)
是需要最小化的目标函数。输入:
x
是一个包含两个元素的数组,分别表示供应中心的横坐标和纵坐标。计算:
np.abs(x[0] - a)
计算供应中心与各需求点在横坐标上的绝对距离,np.abs(x[1] - b)
计算在纵坐标上的绝对距离。两个绝对距离相加即为曼哈顿距离。返回值:将所有需求点的曼哈顿距离求和,作为目标函数的返回值。
-
定义约束条件
bounds = [(3, 8), (4, 10)]
bounds
是优化变量的约束条件。供应中心的横坐标 x x x的范围是 [3, 8]。供应中心的纵坐标 y y y 的范围是 [4, 10]。
-
定义初始猜测位置
x0 = [6, 6]
x0
是供应中心位置的初始猜测值。这里假设供应中心初始位于 (6, 6)。 -
使用
SLSQP
算法进行优化res = minimize(func, x0, bounds=bounds, method="SLSQP")
minimize
函数尝试最小化目标函数func
。输入参数:
func
:需要最小化的目标函数。x0
:初始猜测的供应中心位置。bounds
:供应中心位置的约束条件。method="SLSQP"
:指定优化算法为 SLSQP(Sequential Least Squares Quadratic Programming)。输出:
res
是优化结果的对象,包含最优解、最小目标函数值等信息。 -
输出最优解和最小曼哈顿距离和
print(f"供应中心应设置在坐标 ({res.x[0]:.2f}, {res.x[1]:.2f}) 处。") print(f"最小曼哈顿距离和为: {res.fun:.2f}")
res.x
:最优供应中心的坐标。res.x[0]
是横坐标,res.x[1]
是纵坐标。通过:.2f
格式化为保留两位小数的浮点数。res.fun
:优化后的最小目标函数值,即最小曼哈顿距离和。
完整代码:
import numpy as np
from scipy.optimize import minimize
# 需求点的坐标
a = np.array([1, 4, 3, 5, 9, 12, 6, 20, 17, 8])
b = np.array([2, 10, 8, 18, 1, 4, 5, 10, 8, 9])
# 定义目标函数
def func(x):
# 计算供应中心到各需求点的曼哈顿距离和
total_distance = np.sum(np.abs(x[0] - a) + np.abs(x[1] - b))
return total_distance
# 定义坐标的约束条件
bounds = [(3, 8), (4, 10)]
# 初始猜测位置
x0 = [6, 6]
# 使用SLSQP算法进行优化
res = minimize(func, x0, bounds=bounds, method="SLSQP")
# 输出最优解
print(f"供应中心应设置在坐标 ({res.x[0]:.2f}, {res.x[1]:.2f}) 处。")
print(f"最小曼哈顿距离和为: {res.fun:.2f}")
使用模拟退火算法求解
模拟退火算法是一种用于全局优化问题的概率算法,尤其适用于寻找函数的全局最优解。它模拟物理退火过程,通过在初始阶段允许较大的随机性逐渐降低温度,从而逐步收敛到全局最优解。下面是使用模拟退火算法来求解供应中心位置优化问题的代码示例。
-
优化过程
result = dual_annealing(func, bounds=bounds)
使用
scipy.optimize.dual_annealing
函数,这是专门为模拟退火设计的优化函数。该函数不需要初始猜测值,只需要传入目标函数和边界。
-
输出结果
同样返回一个
OptimizeResult
对象,通过result.x
获取最优解,通过result.fun
获取最小目标值。由于算法的随机性,它在全局范围内搜索,结果更可能是全局最优解。
完整代码:
from scipy.optimize import dual_annealing
# 调用模拟退火进行优化
result = dual_annealing(func, bounds=bounds)
# 输出结果
print(f"最优解: {result.x}")
print(f"最小曼哈顿距离和: {result.fun}")
两种算法的区别
对比维度 | SLSQP 代码 | 模拟退火代码 |
---|---|---|
优化函数 | scipy.optimize.minimize ,指定 method="SLSQP" | scipy.optimize.dual_annealing |
初始猜测值 | 需要提供初始猜测值 x0 | 不需要初始猜测值,自动在边界内随机选择初始点 |
函数调用格式 | python<br>res = minimize(func, x0, bounds=bounds, method="SLSQP")<br> | python<br>result = dual_annealing(func, bounds=bounds)<br> |
返回结果 | res.x 获取最优解,res.fun 获取最小目标值 | result.x 获取最优解,result.fun 获取最小目标值 |
收敛速度 | 较快,依赖梯度信息并通过局部优化实现 | 相对较慢,因为模拟退火需要在初期进行广泛搜索 |
对初始条件敏感性 | 高,结果强烈依赖初始猜测值,如果初始点不好,可能收敛到局部最优解 | 低,由于在全局范围内搜索,即使初始点不好,也可能找到全局最优解 |
代码复杂度 | 代码简洁,但需要合理选择初始猜测值 | 代码简单,自动处理初始条件,但可能需要更多迭代时间 |
适用场景 | 适用于规则连续且对初始猜测值比较明确的场景 | 适用于复杂、不规则、具有多个局部最优的场景 |