例题
例1.1 某机床厂生产甲、乙两种机床,每台销售后的利润分别为4千元与3千元。生产甲机床需用A、B机器加工,加工时间分别为每台2小时和1小时;生产乙机床需用A、B、C三种机器加工,加工时间为每台各一小时。若每天可用于加工的机器时数分别为A机器10小时、B机器8小时和C机器7小时,问该厂应生产甲、乙机床各几台,才能使总利润最大?
上述问题的数学模型:设该厂生产 $ x_{1} $ 台甲机床和 $ x_{2} $ 乙机床时总利润 $ z $ 最大,则 $ x_{1} $, $ x_{2} $ 应该满足:
$$
\begin{align}
\max\quad & z = 4x_{1}+3x_{2} \tag{1.1} \\
\text{s.t.}\quad &
\begin{cases}
2x_{1} + 2x_{2} \leq 10 \\
x_{1} + x_{2} \leq 8 \\
x_{2} \leq 7\\
x_{1}, x_{2} \geq 0
\end{cases} \tag{1.2}
\end{align}
$$
变量 $ x_{1} $, $ x_{2} $ 称之为决策变量,$ (1.1) $ 式被称为问题的目标函数,$ (1.2) $ 中几个不等式是问题的约束条件,记为 $ s.t. $ (subject to)
目标函数及约束条件均为线性函数,故称为线性规划问题。线性规划问题上在一组线性约束条件的限制下,求一线性目标函数最大或最小的问题。
概念
一般线性规划问题的数学标准型为:
$$
\begin{align}
\max \quad & z = \sum_{j=1}^{n}c_{j}x_{j} \tag{1.3}\\
\text{s.t.}\quad &
\begin{cases}
\sum_{j=1}^{n} a_{ij}x_{j}\leq b_{i} \quad i = 1,2,...,m,\\
x_{j} \geq 0 \quad j = 1,2,...,n.
\end{cases}\tag{1.4}
\end{align}
$$
其中 $ b_{i}>=0, i = 1, 2,…,m $
- 可行解 满足约束条件 $ (1.4) $ 的解 $ x=[x_{1}, …, x_{n}]^{T} $ 称为线性规划问题的可行解,而使目标函数 $ (1.3) $ 达到最大值的可行解叫最优解。
- 可行域 所有可行解构成的集合称为问题的可行域,记为 $ R $。
例如如下线性规划问题:
$$
\begin{align}
\max \quad & z = 2x_{1} + 3x_{2} - 5x{3}\\
\text{s.t.}\quad &
\begin{cases}
x_{1} + x_{2} + x_{3} = 7\\
2x_{1} - 5 x_{2} + x_{3} \geq 10\\
x_{1} + 3 x_{2} + x_{3} \leq 12\\
x_{1}, x_{2}, x_{3} \geq0
\end{cases}
\end{align}
$$
通过 python 的 pulp 库编写求解程序:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
import pulp
MyProbLP = pulp.LpProblem("LPProbDemo1", sense=pulp.LpMaximize)
x1 = pulp.LpVariable("x1", lowBound=0, upBound=7, cat="Continuous")
x2 = pulp.LpVariable("x2", lowBound=0, upBound=7, cat="Continuous")
x3 = pulp.LpVariable("x3", lowBound=0, upBound=7, cat="Continuous")
MyProbLP += 2 * x1 + 3 * x2 - 5 * x3
MyProbLP += x1 + x2 + x3 == 7
MyProbLP += 2 * x1 - 5 * x2 + x3 >= 10
MyProbLP += x1 + 3 * x2 + x3 <= 12
MyProbLP += x1 >= 0
MyProbLP += x2 >= 0
MyProbLP += x3 >= 0
MyProbLP.solve()
print("Status:", pulp.LpStatus[MyProbLP.status])
for v in MyProbLP.variables():
print(v.name, "=", v.varValue)
print("F(x) = ", pulp.value(MyProbLP.objective))
|
最后输出:
1
2
3
4
5
|
Status: Optimal
x1 = 6.4285714
x2 = 0.57142857
x3 = 0.0
F(x) = 14.57142851
|
也就是说求得的最优解为 : $ x_{1}=6.4286, x_{2}=0.5714, x_{3}=0 $ 对应的最优值为 $ z = 14.5714 $
投资的收益和风险
问题提出
市场上有 $ n $ 种资产$ (i=1,2,…,n) $可以选择,现用数额为 $ M $ 的相当大的资金作一个时期的投资。这 n 种资产在这一时期内购买$ s_{i} $的平均收益率为$ r_{i} $,风险损失率为$ q_{i} $,投资越分散,总的风险越少,总体风险可用投资的$ s_{i} $中最大的一个风险来度量。
购买$ s_{i} $时要付交易费,费率为$ p_{i} $,当购买额不超过给定值$ u_{i} $,交易费按购买$ u_{i} $计算。另外,假定同期银行存款利率是$ r_{0} $,既无交易费又无风险 $ (r_{0}=5%) $。
已知 $ n=4 $ 时相关数据如表1.1
表 1.1 投资的相关数据
| $ s_{i} $ |
$ r_{i}(%) $ |
$ q{i}(%) $ |
$ p_{i}(%) $ |
$ u_{i}(元) $ |
| $ s_{1} $ |
28 |
2.5 |
1 |
103 |
| $ s_{2} $ |
21 |
1.5 |
2 |
198 |
| $ s_{3} $ |
23 |
5.5 |
4.5 |
52 |
| $ s_{4} $ |
25 |
2.6 |
6.5 |
40 |
试给该公司设计一种投资组合方案,即用给定资金 M ,有选择地购买若干种资产或存银行生息,使净收益尽可能大,使总体风险尽可能小。
符号规定
- $ s_{i} $:第 $ i $ 种投资项目如股票,债券等,$ i=0,1,…,n, $其中 $ s_{0} $ 指存入银行
- $ r_{i}, p_i, q_i $ :分别表示 $ s_{i} $ 的平均收益率、交易费率、风险损失率 $ (i = 0,\dots,n $),且 $ p_0 = 0, q_0 = 0 $;
- $ u_i $:$ s_i $ 的交易金额$ (i = 1,\dots,n) $;
- $ x_i $:投资项目 $ s_i $ 的资金$ (i = 0,1,\dots,n) $;
- $ a $:投资风险度;
- $ Q $:总体收益。
该部分通常出现在投资组合优化或风险决策问题的模型建立之前。
模型建立
- 总体风险用所投资的 $ s_i $ 中最大的一个风险来衡量,即:
$$
\begin{aligned}
max\{q_ix_i | i = 1,2,\dots,n\}
\end{aligned}
$$
- 购买 $ s_i(i=1,\dots,n) $ 所付交易费是一个分段函数:
$$
\begin{aligned}
\text{交易费}=
\begin{cases}
p_ix_i, \quad x_i > u_i\\
p_iu_i, \quad x_i\le u_i\\
\end{cases}
\end{aligned}
$$
而题目所给的定值 $ u_i $ (单位:元)相对总投资 $ M $ 很少,$ p_iu_i $ 更小,这样购买 $ s_i $ 的净收益可简化为 $ (r_i-p_i)x_i $。(将交易费简化为线性,忽略了 $ u_i $)
- 要使净收益尽可能大,总体风险尽可能小,这是一个多目标规划模型。
目标函数为:
$$
\begin{aligned}
\begin{cases}
\max \sum_{i=0}^{n}(r_i-p_i)x_i,\\
\min \{\max_{1\le i \le n}\{q_ix_i\}\}\\
\end{cases}
\end{aligned}
$$
约束条件为:
$$
\begin{aligned}
\begin{cases}
\sum_{i=0}^{n}(1+p_i)x_i=M,\\
x_i\ge 0, i = 0,1,\dots,n.
\end{cases}
\end{aligned}
$$
模型简化
在实际投资中,投资者承受风险的程度不一样,若给定风险一个界限 $ a $,使最大的一个风险率为 $ a $,即 $ \frac{q_ix_i}{M}\le a (i=1,\dots,n) $,可找到相应的投资方案,这样把多目标规划变成一个目标的线性规划:
固定风险水平,优化收益:
$$
\begin{align*}
\max & \sum^{n}_{i=0}(r_i-p_i)x_i \\
\text{s.t.} \quad &
\begin{cases}
\frac{q_ix_i}{M} \le a, i=1, \dots,n\\
\sum^{n}_{i=0}(1+p_i)x_i=M, x_i\ge 0, i=0,1,\dots ,n.
\end{cases}
\end{align*}
$$
在此基础得到 python 代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
import pulp
import matplotlib.pyplot as plt
# ---------- 数据准备 ----------
# 投资项目数量(不包括银行存款)
n = 4
# 银行存款 s0 的参数
r0 = 0.05 # 年收益率 5%
p0 = 0.0 # 交易费率
q0 = 0.0 # 风险损失率
# 四个投资项目 (s1~s4)
data = [
{'r': 0.27, 'p': 0.01, 'q': 0.025}, # s1
{'r': 0.19, 'p': 0.02, 'q': 0.015}, # s2
{'r': 0.185, 'p': 0.045, 'q': 0.055}, # s3
{'r': 0.185, 'p': 0.065, 'q': 0.026} # s4
]
# 总资金 M
M = 1.0
# 风险界限 a (投资者设定的最大风险比例)
# 例如 a = 0.05 表示最大允许损失占总资金的 5%
a = 0.05
# ---------- 建立模型并求解 ----------
def solve_for_risk_limit(risk_limit: float) -> tuple[str, list[float], float, float]:
"""
在给定风险上限下求解最优投资方案。
Parameters
----------
risk_limit : float
风险上限 a,表示最大允许风险损失占总资金的比例。
Returns
-------
status : str
求解状态("Optimal" 表示最优解, "Infeasible" 表示无可行解等)。
values : list[float]
决策变量 [x0, x1, x2, x3, x4],依次为银行存款和 4 个投资项目的资金分配额。
net_return : float
最大净收益,即目标函数值 r0*x0 + Σ(r_i - p_i)*x_i。
risk_ratio : float
组合实际风险比例,即 Σ(q_i * x_i) / M。
"""
# 创建最大化问题
model = pulp.LpProblem("Fixed_Risk_Optimization", pulp.LpMaximize)
# 决策变量 x_i (i = 0,...,n)
# x0: 银行存款资金; x1~x4: 四个投资项目的资金
x = [pulp.LpVariable(f"x{i}", lowBound=0, cat='Continuous') for i in range(n+1)]
# 目标函数: 最大化净收益
# 银行存款净收益 = r0 * x0
# 每个投资项目净收益 = (r_i - p_i) * x_i
objective = r0 * x[0] + pulp.lpSum((data[i]['r'] - data[i]['p']) * x[i+1] for i in range(n))
model += objective
# 约束1: 风险约束 (对每个投资项目 i = 1..n)
# q_i * x_i <= a * M
for i in range(n):
model += data[i]['q'] * x[i+1] <= risk_limit * M
# 约束2: 资金总量约束
# x0 + sum_{i=1}^n (1 + p_i) * x_i = M
model += x[0] + pulp.lpSum((1 + data[i]['p']) * x[i+1] for i in range(n)) == M
# 求解
solver = pulp.PULP_CBC_CMD(msg=False)
model.solve(solver)
status = pulp.LpStatus[model.status]
values = [v.varValue if v.varValue is not None else 0.0 for v in x]
net_return = pulp.value(objective) if status == "Optimal" else 0.0
total_risk = sum(data[i]['q'] * values[i+1] for i in range(n))
risk_ratio = total_risk / M if M else 0.0
return status, values, net_return, risk_ratio
# ---------- 单点求解 ----------
status, values, net_return, risk_ratio = solve_for_risk_limit(a)
# ---------- 输出结果 ----------
print("求解状态:", status)
if status == "Optimal":
print("\n最优投资方案 (资金分配):")
print(f"银行存款 s0 : {values[0]:.2f} 元")
for i in range(n):
print(f"投资 s{i+1} : {values[i+1]:.2f} 元")
print(f"\n最大总收益 (净): {net_return:.6f}")
print(f"组合风险比例(总损失/M): {risk_ratio:.6f}")
# 验证总资金
total = values[0] + sum((1+data[i]['p']) * values[i+1] for i in range(n))
print(f"实际使用总资金: {total:.6f} (应等于 {M})")
else:
print("未找到最优解,请检查约束或参数 a 是否过小。")
# ---------- 风险-收益关系图 ----------
# 计算不同风险上限下的最优收益 (a 从 0 到 <0.05, 步长 0.001)
a_values = [i * 0.001 for i in range(50)] # 生成风险上限序列
returns = [] # 存储每个风险上限对应的最优收益
for risk_limit in a_values: # 遍历风险上限
status, _, net_return, _ = solve_for_risk_limit(risk_limit) # 求解对应的最优收益
if status == "Optimal": # 判断是否得到最优解
returns.append(net_return) # 保存最优收益
else:
returns.append(0.0) # 无解时用 0 占位
plt.figure(figsize=(7, 4.5)) # 创建画布并设置大小
plt.plot(a_values, returns, marker='*', linestyle='None') # 绘制散点图
plt.title("Risk-Return") # 图标题
plt.xlabel("a") # x 轴标签
plt.ylabel("Q") # y 轴标签
plt.grid(True, linestyle='--', alpha=0.4) # 添加网格线
plt.tight_layout() # 自动调整边距
plt.show() # 显示图像
|
输出图表:
