离散模型优化是运筹学和计算机科学领域中的一个重要分支,它主要研究如何在有限的、通常是计数的决策变量空间中寻找最优解。这类问题通常出现在资源分配、生产计划、物流管理、网络设计等实际应用场景中。在这篇文章中就将介绍离散模型优化中关于线性规划等部分内容。
一、优化建模简介
优化建模是数学规划的一个重要组成部分,它涉及将现实世界的问题转化为数学形式,以便使用数学方法和算法来求解最优解决方案。优化建模的核心在于定义一个目标函数(即想要最小化或最大化的量),并根据实际情况设置一系列约束条件。
关于优化建模,其涉及以下概念:
目标函数:优化问题的核心是定义一个目标函数,表示所追求的目标,比如成本最小化、利润最大化等。
决策变量:指需要确定其值的未知数,它们直接影响目标函数的值。
约束条件:为了确保解决方案的实际可行性,需要对决策变量施加限制。这些限制可以是等式也可以是不等式。
可行解:满足所有约束条件的决策变量的取值称为可行解。
最优解:在所有可行解中,使目标函数达到最优值的解称为最优解。
然后,关于一类优化问题,我们会首先对于它给出一个基本模型,其大概为:
其中,“Opt”(Optimize)的意思是优化(最大化或最小化),下标“j”表示优化函数的一个或多个函数,这些函数是属于有限集合J,并以整数下标来区分。向量X的各个分量称为该模型的决策变量,而函数就是目标函数。“s.t.”(subject to)的意思是决策变量必须满足某些边界条件。
对于这个模型,我们可以举出如下例子:
假设我们是一家制造公司,正在尝试优化我们的产品产量 XX 以最大化利润 f(X)。然而,我们的生产和销售受到一些限制,比如原材料供应量 b1 和市场需求量 b2。我们可以将这个问题表述成以下的优化问题:
其中,
p 是产品的售价;
c 是单位产品的成本;
X 是我们要生产的数量;
b1 是可用的原材料总量;
b2 是市场的需求量。
二、优化问题的分类
关于一个优化问题是否具有约束条件,我们可以将之分为有约束的和无约束的。具体说:
无约束优化问题:这类问题没有对决策变量的任何限制,目标是在整个定义域内找到使目标函数达到最优值的点。常见的无约束优化问题包括一些简单的最小化或最大化问题,如寻找多项式的极值点。
有约束优化问题:这类问题中,决策变量必须满足一组或多组约束条件。这些约束条件可以是等式约束或不等式约束,它们限制了可行解的空间。有约束优化问题更为普遍,因为大多数实际问题都会涉及到资源、成本、物理限制等方面的约束。
其中,线性规划就是十分经典的一种有约束的优化问题。对于一个优化问题,如果它有唯一的的目标函数,且当一个决策变量出现在目标函数和任何约束函数中它都只以一次幂的形式出现,目标函数和任何约束函数不包含决策变量的乘积项,并且其中的决策变量的系数都是常数,那么这个优化问题就是线性规划问题。反之则是非线性规划问题。
另外,如果目标函数多于一个则被称为多目标或目标规划。像在刚才的问题中,p与c这样的系数大概率是不能知道精确值的,或是有时仅仅只是代表了实际值的平均值,但这个平均值又会与实际值有较大偏差。此时,如果这些系数与时间有关,那么它对应的问题就会被称呼为动态规划(DP);如果系数本质上是随机的,那么就被称呼为随机规划;如果一个或多个决策变量被限制为只能取整数,那么这样的问题称呼为整数规划(IP);如果只是部分被限制,则称呼为混合整数规划。
2.1 无约束优化问题
关于无约束优化问题下面给出一个例子:
首先我们考虑一个简单的二次函数:
我们想要求得其最小值所对应的x,则需要先求得其导数,并将倒数设为零:
并求得x=-2,为了确认 x=−2x=−2 是否为最小值点,我们可以检查二阶导数:
那么有f''(x)>0,所以x=-2是最小值点,将之带回原式可以得到:
2.2 动态规划
关于动态规划,其实我在之前的文章中有专门讲到,那么我们在这里可以比较下算法设计中的dp与数学建模的中的dp的异同点:
相同处:二者都是将原问题进行分解,并将子问题的答案进行存储以避免不必要的资源浪费。
不同处:
在应用场景上,算法中的dp用于主要应用于序列决策问题,如背包问题、最长公共子序列(LCS)、编辑距离等,并且通常处理的是离散的状态和动作空间,而数学建模中的dp主要用于解决连续或离散的优化问题,如资源分配、生产计划、库存控制等。此外它可以可以处理连续的状态和动作空间;
在状态与动作上,前者中状态往往是离散的,可以用整数或枚举类型表示,而后者中状态则可以是连续的,也可以是离散的,通常用实数或向量表示。至于动作则与状态类似;
在价值函数上,前者中通常表示为一个数组或表,存储每个子问题的最优解,而后者中可以是连续的函数,通常用数学公式表示。
2.3 整数规划
下面举一个整数规划的例子:
假设一家工厂生产两种产品 A 和 B。每种产品的生产需要消耗一定的原材料和工时,工厂希望在满足市场需求的同时最大化利润。具体数据如下:
产品 A:每单位产品需要 2 单位原材料和 1 单位工时。每单位产品的利润为 30 元。
产品 B:每单位产品需要 1 单位原材料和 2 单位工时。每单位产品的利润为 20 元。
资源限制:工厂每天最多可以提供 10 单位原材料。工厂每天最多可以提供 12 单位工时。
市场需求:每天最多可以销售 5 单位产品 A。每天最多可以销售 6 单位产品 B。
此时,我们需要建立一个整数规划模型来最大化总利润。
那么,我们可以总结出以下模型:
2.4 多目标规划
然后是一个多目标规划的例子:
假设一家公司生产两种产品 A 和 B。公司希望同时最大化利润和最小化生产成本。具体数据如下:
产品 A:每单位产品需要 2 单位原材料和 1 单位工时。每单位产品的利润为 30 元。每单位产品的生产成本为 10 元。
产品 B:每单位产品需要 1 单位原材料和 2 单位工时。每单位产品的利润为 20 元。每单位产品的生产成本为 15 元。
资源限制:公司每天最多可以提供 10 单位原材料。公司每天最多可以提供 12 单位工时。
我们需要建立一个多目标规划模型来同时最大化利润和最小化生产成本。
可以总结出模型如下:
2.5 随机规划
这里可以给出一个简单的随机规划的题目来参考下:
假设一家工厂生产一种产品,需要决定每天的生产量。生产过程中存在不确定的需求量,工厂希望通过制定生产计划来最小化总成本,同时满足需求。
三、线性规划
3.1 几何解法
所谓几何解法就是将建模所得的几个线性函数画于同一图中,然后它们所包围起来的图形就是可行域,图形的顶点是极点,然后我们可以在这些极点中找到最优解,在如下代码所画出的图中,所标红的极点就是最优解。代码与图像如下:
import numpy as np
import matplotlib.pyplot as plt# 定义约束条件的边界
def constraint1(x1):return 10 - 2 * x1def constraint2(x1):return (12 - x1) / 2# 定义目标函数的等值线
def objective_line(x1, k):return (k - 30 * x1) / 20# 绘制约束条件
x1 = np.linspace(0, 10, 400)
plt.plot(x1, constraint1(x1), label='2x1 + x2 = 10')
plt.plot(x1, constraint2(x1), label='x1 + 2x2 = 12')# 绘制非负约束
plt.axvline(x=0, color='black', linewidth=0.5)
plt.axhline(y=0, color='black', linewidth=0.5)# 绘制可行解区域
feasible_region = np.array([[0, 0],[5, 0],[4, 4],[0, 6]
])
plt.fill(feasible_region[:, 0], feasible_region[:, 1], alpha=0.3, label='Feasible Region')# 绘制目标函数的等值线
k_values = [0, 60, 120, 180, 200]
for k in k_values:plt.plot(x1, objective_line(x1, k), linestyle='--', label=f'30x1 + 20x2 = {k}')# 标记最优解
optimal_x1 = 4
optimal_x2 = 4
plt.plot(optimal_x1, optimal_x2, 'ro', label=f'Optimal Solution ({optimal_x1}, {optimal_x2})')# 设置图例和标签
plt.xlabel('x1 (Product A)')
plt.ylabel('x2 (Product B)')
plt.legend()
plt.title('Geometric Solution of Linear Programming Problem')
plt.grid(True)
plt.show()# 输出最优解和最优利润
optimal_profit = 30 * optimal_x1 + 20 * optimal_x2
print(f"Optimal Solution: x1 = {optimal_x1}, x2 = {optimal_x2}")
print(f"Optimal Profit: {optimal_profit}")
3.2 代数解法
在线性规划的代数解法中,其核心思路就是一种枚举的思路,即将所求的决策变量分别为0,然后求值比较得出最优解。
举个例子,有如下的一个模型:
那么,我们可以通过代数解法总结出如下的表来:
极点 | 目标函数值 |
A(0,0) | 0 |
B(24,0) | 600 |
C(12,15) | 750 |
D(0,23) | 690 |
观察这个表不难得出最优解为C(12,15)
4.3 单纯形法
单纯形法不同于前两种,它是通过迭代求取最优解,其融合了最优性检验和可行性检验。其具体步骤如下:
1.标准化问题:将所有不等式约束转换为等式约束,通过引入松弛变量或剩余变量。确保所有变量都是非负的。
2.构建初始单纯形表:将目标函数和约束条件写成标准形式。构建初始单纯形表,其中每一行代表一个约束条件,最后一行代表目标函数。
3.选择进入基的变量:选择目标函数中系数最大的非基变量作为进入基的变量(对于最大化问题)。如果所有非基变量的系数都非正,则当前解已经是最优解。
4.选择离开基的变量:计算每个基变量与进入基变量的比例,选择最小的非负比例对应的基变量作为离开基的变量。这一步确保新解仍然可行。
5.更新单纯形表:通过行操作将进入基的变量变为基变量,同时保持其他基变量不变。更新单纯形表中的所有元素。
6.重复迭代:重复步骤3到5,直到找不到可以改进目标函数的变量为止。
下面我们通过一个具体例子来理解:
假如我们有一个线性规划的模型如下:
那么,我们引入一个松弛变量后变为如下:
可以得到单纯形表为:
x1 | x2 | s1 | s2 | 右端项 |
2 | 1 | 1 | 0 | 10 |
1 | 2 | 0 | 1 | 12 |
-3 | -2 | 0 | 0 | 0 |
然后选择 x1作为进入基的变量,因为它的系数最大为-3。
接着计算最小比例可得:10/2=5 12/1=1,选择s1作为离开基的变量,然后原先的式子变形为:
此时又可以总结出单纯形表,重复上述步骤,最后得到一个这样的单纯形表:
x1 | x2 | s1 | s2 | 右端项 |
0 | 1 | 2/3 | -1/3 | 8/3 |
0 | 1 | -1/3 | 2/3 | 14/3 |
0 | 0 | 1.5 | 1/3 | 38/3 |
此时,所有非基变量的系数都非正,因此当前解是最优解,即为:
然后我们可以对于这个问题写出如下的代码:
from pulp import *# 创建一个问题实例
prob = LpProblem("LinearProgramming", LpMaximize)# 定义决策变量
x1 = LpVariable("x1", lowBound=0)
x2 = LpVariable("x2", lowBound=0)# 定义目标函数
prob += 3*x1 + 2*x2# 添加约束条件
prob += 2*x1 + x2 <= 10
prob += x1 + 2*x2 <= 12# 求解问题
status = prob.solve()# 输出结果
print(f"Status: {LpStatus[status]}")
print(f"x1 = {value(x1)}")
print(f"x2 = {value(x2)}")
print(f"Objective Value: {value(prob.objective)}")
其输出为:
At line 2 NAME MODEL
At line 3 ROWS
At line 7 COLUMNS
At line 14 RHS
At line 17 BOUNDS
At line 18 ENDATA
Problem MODEL has 2 rows, 2 columns and 4 elements
Coin0008I MODEL read with 0 errors
Option for timeMode changed from cpu to elapsed
Presolve 2 (0) rows, 2 (0) columns and 4 (0) elements
0 Obj -0 Dual inf 4.9999998 (2)
0 Obj -0 Dual inf 4.9999998 (2)
2 Obj 17.333333
Optimal - objective value 17.333333
Optimal objective 17.33333333 - 2 iterations time 0.002
Option for printingOptions changed from normal to all
Total time (CPU seconds): 0.00 (Wallclock seconds): 0.00Status: Optimal
x1 = 2.6666667
x2 = 4.6666667
Objective Value: 17.333333500000002
4.4 敏感性分析
敏感性分析用于研究当模型的参数(如目标函数系数、约束条件的右侧常数等)发生变化时,最优解的变化情况。敏感性分析有助于了解模型的稳健性和决策的可靠性。其主要内容有:目标函数系数的变化、约束条件右侧常数的变化、新增变量或约束的影响。
四、数值搜索方法
4.1 二分搜索法
二分法我们应该是十分熟悉的,它在算法设计等方面有广泛运用,所以在此,仅给出一份代码:
def binary_search_iterative(arr, target):low, high = 0, len(arr) - 1while low <= high:mid = (low + high) // 2if arr[mid] == target:return midelif arr[mid] < target:low = mid + 1else:high = mid - 1return -1# 示例使用
sorted_list = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
target_value = 11
index = binary_search_iterative(sorted_list, target_value)
print(f"元素 {target_value} 在列表中的位置为 {index}")
4.2 黄金分割搜索方法
黄金分割法是采用黄金数进行搜索,即是将搜索的区间按0.618的比例去进行分割。大概过程与二分搜索方法类似,所以在此不赘述了。
此上