51、通用优化方法解析

通用优化方法解析

在优化问题的解决过程中,有多种算法和策略可供选择,每种方法都有其独特的优势和适用场景。下面将详细介绍几种常见的优化算法及其应用。

1. 最短队列算法

最短队列算法是一种在线算法,但其实际效果并非总是如预期般理想。考虑一个特定实例,其中 $n = m(m - 1) + 1$,对于 $i = 1, …, n - 1$,$t_i = 1$,而 $t_n = m$。此时,最优解的完工时间 $L^* = m$,然而最短队列算法得出的解的完工时间 $L_{max} = 2m - 1$。该算法生成的解与知晓全部输入的算法所生成的解相比,最坏情况下的差距因子为 $2 - 1/m$,我们称此在线算法的竞争比 $\alpha = 2 - 1/m$。

此外,若作业按规模降序排序,最短队列算法可实现 $4/3$ 的近似比。

2. 并行贪心算法

通常情况下,贪心算法难以进行并行化处理。不过,我们可以利用应用程序的特殊属性来实现并行化。当能够预先计算出项目的优先级时,可使用并行排序对它们进行排序,甚至可以并行计算决策,例如使用前缀和。贪心背包算法就是一个例子,其决策仅取决于前面对象的总重量。

有时候,至少有一部分项目可以独立处理。例如,在有向无环图(DAG)的并行遍历算法以及并行最短路径算法中都有体现。若其他方法都行不通,还可以放宽对象的排序要求,近似实现贪心算法。

3. 动态规划

动态规划的核心思想是将一个给定的问题实例扩展为一系列辅助实例,即子问题,然后系统地解决这些子问题。在解决子问题时,假设其所有子子问题的解都已计算完成。许多优化问题遵循最优性原则,即子问题的最优解由其某些子子问题的最优解组成。

以背包问题为例,对于一个由重量向量 $w$、利润向量 $c$ 和容量 $M$ 构成的实例 $I$,我们为每个 $i \in 0..n$ 和每个介于 $0$ 到 $M$ 之间的 $C$ 定义一个子问题 $P(i, C)$,它表示仅将物品 $1$ 到 $i$ 放入背包且总重量不超过 $C$ 时的最大利润。我们的目标是计算 $P(n, M)$。

从简单情况入手,当没有物品或总重量为 $0$ 时,最大利润为 $0$,即:
- $P(0, C) = 0$,对于所有的 $C$
- $P(i, 0) = 0$,对于所有的 $i$

当 $i > 0$ 且 $C > 0$ 时,在使利润最大化的解中,有两种情况:使用物品 $i$ 或不使用。若不使用物品 $i$,最大可实现利润为 $P(i - 1, C)$;若使用物品 $i$,最大可实现利润为 $P(i - 1, C - w_i) + p_i$,前提是 $C \geq w_i$。这可以总结为以下递推公式:

$P(i,C) =
\begin{cases}
0, & \text{if } i = 0 \text{ or } C = 0 \
\max(P(i - 1, C), P(i - 1, C - w_i) + p_i) & \text{if } i \geq 1 \text{ and } w_i \leq C \
P(i - 1, C) & \text{if } i \geq 1 \text{ and } w_i > C
\end{cases}$

通过填充一个表格 $P$,其中每列对应一个可能的容量 $C$,每行对应一个物品集 $1..i$,我们可以计算出 $P(n, M)$。为了从表格中构建原始实例的最优解,我们从表格右下角的 $P(n, M)$ 开始逆向推导。

以下是一个动态规划表格的示例,用于解决背包问题,其中 $p = (10, 20, 15, 20)$,$w = (1, 3, 2, 4)$,$M = 5$:
| $C$ | 0 | 1 | 2 | 3 | 4 | 5 |
| — | — | — | — | — | — | — |
| $i = 0$ | 0 | 0 | 0 | 0 | 0 | 0 |
| 1 | 0 | 10 | 10 | 10 | 10 | 10 |
| 2 | 0 | 10 | 10 | 20 | 30 | 30 |
| 3 | 0 | 10 | 15 | 25 | 30 | 35 |
| 4 | 0 | 10 | 15 | 25 | 30 | 35 |

为了提高空间利用率和速度,Nemhauser - Ullmann 算法只计算帕累托最优解。帕累托最优解是指不存在能支配它的解,即没有利润更高且成本不增加,或者利润相同但成本更低的解。该算法将这些解存储在按 $C$ 值排序的列表 $L_i$ 中。

mermaid 流程图如下:

graph TD
    A[开始] --> B[初始化L0 = <(0,0)>]
    B --> C[计算Li-1]
    C --> D[通过Li-1得到L'i-1]
    D --> E[合并Li-1和L'i-1]
    E --> F[删除被支配元素]
    F --> G[得到Li]
    G --> H{是否完成所有i?}
    H -- 否 --> C
    H -- 是 --> I[结束]
4. 系统搜索 - 分支限界法

在许多优化问题中,对于给定的实例 $I$,可能的解的集合 $U$ 是有限的,理论上我们可以通过尝试所有可能性来解决优化问题。但简单地应用这种方法往往效果不佳,因为搜索空间 $U$ 通常会随着输入规模 $|I|$ 的增加而迅速增大。不过,我们可以将搜索范围限制在有希望的候选解上,从而使这种方法更具实用性。

以背包问题为例,我们使用分支限界法进行系统搜索。可行解可以表示为向量(或更一般的序列),其分量只能取有限个值。系统搜索将搜索空间视为一棵树,通过递归过程生成和遍历这棵树。

以下是一个用于背包问题的分支限界算法的伪代码:

Function bbKnapsack((p1, ..., pn), (w1, ..., wn), M) : {0, 1}n
    assert p1/w1 ≥ p2/w2 ≥ ··· ≥ pn/wn
    // assume input sorted by profit density
    ˆx = heuristicKnapsack((p1, ..., pn), (w1, ..., wn), M) : {0, 1}n
    // best solution so far
    x : {0, 1}n
    // current partial solution (x1, ..., xn)
    recurse(1, M, 0)
    return ˆx

// recurse finds solutions assuming x1, ..., xi−1 are fixed, M′ = M − ∑j<i xjwj, P = ∑j<i xj pj.
Procedure recurse(i, M′, P : N)
    u := P + upperBound((pi, ..., pn), (wi, ..., wn), M′)
    if u > p · ˆx then
        // possibly better solution than ˆx
        if i > n then ˆx := x
        else
            // branch on variable xi
            if wi ≤ M′ then xi := 1; recurse(i + 1, M′ − wi, P + pi)
            if u > p · ˆx then xi := 0; recurse(i + 1, M′, P)

分支限界法中的分支操作是系统搜索例程的基本步骤,通过为部分解的某个自由分量插入所有合理的值来生成节点的子节点,并对每个新节点递归调用该过程。限界操作则用于修剪那些不可能包含比已知更好解的子树。算法会将目前找到的最佳可行解存储在全局变量中,即所谓的当前最优解。在处理部分解 $x$ 时,会为其计算一个上界 $u$,当 $u \leq p · ˆx$ 时,停止扩展当前搜索树的分支。

此外,还有一些相关的练习可以帮助我们更好地理解和应用这些算法,例如:
- 证明通过定义 $P(i, C) = -\infty$(当 $C < 0$ 时)可以避免在 $P(i, C)$ 的定义中对 $w_i$ 进行情况区分。
- 为背包问题的动态规划算法设计一个更节省空间的解决方案。
- 开发一个用于背包问题的按利润进行动态规划的算法。
- 为自动售货机设计一个找零算法,使其使用最少的硬币。
- 利用动态规划优化矩阵链乘法的计算顺序。
- 计算两个字符串之间的编辑距离。
- 检查最小生成树是否遵循最优性原则。
- 解决有约束的最短路径问题。
- 并行化动态规划算法。
- 解决八皇后问题和 15 拼图问题。

通用优化方法解析(续)

5. 相关练习解析
5.1 动态规划相关练习
  • 避免 $w_i$ 情况区分 :在定义 $P(i,C)$ 时,若定义 $P(i,C) = -\infty$ (当 $C < 0$ 时),可以避免对 $w_i$ 进行情况区分。当 $C < w_i$ 时,$P(i - 1, C - w_i) = -\infty$,那么 $\max(P(i - 1, C), P(i - 1, C - w_i) + p_i)$ 就会自动取 $P(i - 1, C)$,与原定义中 $w_i > C$ 的情况一致,从而统一了表达式。
  • 更节省空间的背包动态规划算法 :原算法需要存储一个包含 $\Theta(nM)$ 个整数的表格。我们可以设计一个更节省空间的方案,在任何给定时间,每个表格条目只存储一个位,同时存储表格 $P$ 的两行。存储的位可以表示当前物品是否被选中。通过按特定顺序填充表格,我们可以只保留两行信息,因为计算当前行只依赖于上一行。例如,我们可以逐行填充表格,在计算新的一行时,只保留上一行和当前正在计算的行。
步骤 操作
1 初始化两行表格,分别存储上一行和当前行的信息。
2 从第一行开始,逐行计算每个容量对应的最大利润。
3 在计算当前行时,根据上一行的信息更新当前行。
4 计算完成后,根据存储的位信息回溯得到最优解。
  • 按利润进行动态规划的背包算法 :定义 $W(i,P)$ 为使用物品 $1$ 到 $i$ 获得至少利润 $P$ 所需的最小重量。显然,$W(i,P) = 0$ (对于 $1 \leq i \leq n$ 和所有 $P \leq 0$),$W(0,P) = \infty$ (对于所有 $P > 0$)。递推公式为 $W(i,P) = \min{W(i - 1, P), W(i - 1, P - p_i) + w_i}$ (对于 $1 \leq i \leq n$ 和 $P \geq 0$)。
    • 若已知最优解的利润 $p^*$ 或其一个较好的上界,我们可以构建一个表格,按列填充,每列对应一个利润值,每行对应一个物品集。通过递推公式计算每个单元格的值,最终找到满足 $W(n,P) \leq M$ 的最大 $P$。
    • 若不知道 $p^*$,可以先通过贪心算法得到一个上界,然后在这个范围内进行计算。

mermaid 流程图如下:

graph TD
    A[开始] --> B[初始化W表格]
    B --> C{是否知道p*?}
    C -- 是 --> D[确定表格范围]
    C -- 否 --> E[使用贪心算法得到上界]
    E --> D
    D --> F[按列填充表格]
    F --> G[找到满足W(n,P) <= M的最大P]
    G --> H[结束]
5.2 实际应用问题练习
  • 自动售货机找零算法
    • 贪心算法(适用于特定货币体系) :在欧元区(硬币面值为 1、2、5、10、20、50、100、200 美分)和加拿大(硬币面值为 1、5、10、25、50、100、200 美分),可以使用贪心算法。每次选择不超过剩余金额的最大面值硬币,直到找零完成。
    • 动态规划算法(适用于任意货币体系) :定义 $f(x)$ 为找零金额 $x$ 所需的最少硬币数。初始化 $f(0) = 0$,对于 $x > 0$,$f(x) = \min{f(x - c) + 1}$ (其中 $c$ 为所有可用硬币面值)。通过填充一个长度为 $x$ 的表格,从 $1$ 到 $x$ 依次计算每个金额所需的最少硬币数。
步骤 操作
1 初始化表格 $f$,$f(0) = 0$。
2 对于每个金额 $x$ 从 $1$ 到目标找零金额,遍历所有硬币面值 $c$。
3 若 $x \geq c$,则 $f(x) = \min(f(x), f(x - c) + 1)$。
4 最终 $f(x)$ 即为找零 $x$ 所需的最少硬币数。
  • 矩阵链乘法优化 :要计算矩阵乘积 $M_1M_2 \cdots M_n$,其中 $M_i$ 是 $k_{i - 1} \times k_i$ 矩阵。使用动态规划,定义 $m[i,j]$ 为计算矩阵链 $M_iM_{i + 1} \cdots M_j$ 所需的最少元素乘法次数。
    • 初始化 $m[i,i] = 0$ (对于 $i = 1, \cdots, n$)。
    • 对于链长 $l$ 从 $2$ 到 $n$,计算 $m[i,j]$ (其中 $j = i + l - 1$)。
    • 递推公式为 $m[i,j] = \min_{i \leq k < j}{m[i,k] + m[k + 1,j] + k_{i - 1}k_k k_j}$。
    • 通过填充一个 $n \times n$ 的表格,最终 $m[1,n]$ 即为计算整个矩阵链乘积所需的最少元素乘法次数。同时,记录每个子问题的最优分割点 $s[i,j]$,用于回溯得到最优计算顺序。

mermaid 流程图如下:

graph TD
    A[开始] --> B[初始化m表格和s表格]
    B --> C[设置链长l从2到n]
    C --> D[设置i从1到n - l + 1]
    D --> E[计算j = i + l - 1]
    E --> F[初始化m[i,j] = 无穷大]
    F --> G[设置k从i到j - 1]
    G --> H[计算q = m[i,k] + m[k + 1,j] + k_{i - 1}k_k k_j]
    H --> I{q < m[i,j]?}
    I -- 是 --> J[m[i,j] = q; s[i,j] = k]
    I -- 否 --> G
    G --> K{是否完成所有k?}
    K -- 否 --> G
    K -- 是 --> L{是否完成所有i?}
    L -- 否 --> D
    L -- 是 --> M{是否完成所有l?}
    M -- 否 --> C
    M -- 是 --> N[结束]
  • 编辑距离计算 :两个字符串 $s$ 和 $t$ 的编辑距离 $L(s,t)$ 是将 $s$ 转换为 $t$ 所需的最少字符删除、插入和替换操作次数。定义 $d(i,j) = L(\langle s_1, \cdots, s_i \rangle, \langle t_1, \cdots, t_j \rangle)$ (对于 $0 \leq i \leq n$,$0 \leq j \leq m$)。递推公式为 $d(i,j) = \min{d(i - 1, j) + 1, d(i, j - 1) + 1, d(i - 1, j - 1) + [s_i \neq t_j]}$,其中 $[s_i \neq t_j]$ 为 1 (当 $s_i$ 和 $t_j$ 不同时),为 0 (当 $s_i$ 和 $t_j$ 相同时)。
    • 初始化 $d(0,j) = j$ (对于 $j = 0, \cdots, m$)和 $d(i,0) = i$ (对于 $i = 0, \cdots, n$)。
    • 按行或列填充一个 $(n + 1) \times (m + 1)$ 的表格,最终 $d(n,m)$ 即为编辑距离。
    • 回溯表格,根据 $d(i,j)$ 的值确定每个操作(删除、插入或替换),得到最优编辑步骤序列。
步骤 操作
1 初始化 $d$ 表格,$d(0,j) = j$,$d(i,0) = i$。
2 按行或列填充表格,计算每个 $d(i,j)$。
3 根据递推公式更新 $d(i,j)$。
4 最终 $d(n,m)$ 为编辑距离。
5 从 $d(n,m)$ 开始回溯,确定编辑步骤。
5.3 图论相关练习
  • 最小生成树最优性原则检查 :分别考虑三种定义子问题的方式:节点子集、任意边子集和排序边序列的前缀。
    • 节点子集 :对于一个节点子集 $S$,其最小生成树不一定是整个图最小生成树的一部分,因为可能存在连接 $S$ 与其他节点的边影响整体最优性,所以不满足最优性原则。
    • 任意边子集 :任意边子集的最小生成树也不一定能构成整个图的最小生成树,因为边的组合可能导致不连通或不是最优的,不满足最优性原则。
    • 排序边序列的前缀 :在 Kruskal 算法中,按边权排序后依次选择边,每次选择的边都不会破坏已选边构成的森林的最优性,满足最优性原则。
  • 有约束的最短路径问题 :在有向图 $G = (V,E)$ 中,边 $e \in E$ 有长度 $\ell(e)$ 和成本 $c(e)$,要找到从节点 $s$ 到节点 $t$ 的路径,使总长度最小且总成本不超过 $C$。子路径不一定是最短路径,因为在满足成本约束的情况下,可能需要选择一些较长但成本较低的边来保证整体最优。
6. 并行化相关练习
  • 并行化动态规划 :动态规划算法通常花费大量时间填充表格。如果表格中的多个条目可以独立计算,就可以并行处理。
    • 背包问题 :对于背包问题的动态规划递推公式 $P(i,C)$,第 $i$ 行的所有条目只依赖于第 $i - 1$ 行,因此可以并行计算第 $i$ 行的所有条目。
    • Nemhauser - Ullmann 算法并行化 :可以使用并行合并和前缀和来并行化该算法的一步。具体步骤如下:
      • 利用并行合并将 $L_{i - 1}$ 和 $L’_{i - 1}$ 合并为一个列表。
      • 使用前缀和计算来确定哪些元素是被支配的,从而删除这些元素。
      • 最终得到 $L_i$。
    • 编辑距离计算并行化 :在计算编辑距离时,按对角线填充表格可以获得一定的并行性。对于固定的 $k$,条目 $d(i,k - i)$ 是独立的,且只依赖于前两条对角线。
7. 其他问题练习
  • 八皇后问题 :在棋盘上放置八个皇后,使它们互不攻击。设 $x_i$ 为第 $i$ 行皇后的位置,$x_i \in 1..8$,需要满足 $x_i \neq x_j$,$i + x_i \neq j + x_j$,$x_i - i \neq x_j - j$ (对于 $1 \leq i < j \leq 8$)。这些条件分别表示皇后不在同一列、同一正对角线和同一反对角线。可以使用系统搜索,当固定一个变量 $x_i$ 时,排除一些自由变量的可能值,当某个自由变量没有可用值时停止探索。
  • 15 拼图问题 :目标是将 15 个编号的方块从给定的初始配置移动到有序配置。使用迭代加深深度优先搜索,先尝试所有一步移动序列,然后是两步移动序列,以此类推。对于 15 拼图问题,还可以进行以下优化:
    • 不撤销上一步的移动。
    • 使用所有方块自由移动到目标位置所需的移动次数作为下界,当该下界证明当前搜索深度过小时停止探索。
    • 预先确定移动次数的奇偶性。

通过对这些优化算法和相关练习的学习和实践,我们可以更好地理解和应用不同的优化策略,解决各种实际问题。

源码地址: https://pan.quark.cn/s/d1f41682e390 miyoubiAuto 米游社每日米游币自动化Python脚本(务必使用Python3) 8更新:更换cookie的获取地址 注意:禁止在B站、贴吧、或各大论坛大肆传播! 作者已退游,项目不维护了。 如果有能力的可以pr修复。 小引一波 推荐关注几个非常可爱有趣的女孩! 欢迎B站搜索: @嘉然今天吃什么 @向晚大魔王 @乃琳Queen @贝拉kira 第三方库 食用方法 下载源码 在Global.py中设置米游社Cookie 运行myb.py 本地第一次运行时会自动生产一个文件储存cookie,请勿删除 当前仅支持单个账号! 获取Cookie方法 浏览器无痕模式打开 http://user.mihoyo.com/ ,登录账号 按,打开,找到并点击 按刷新页面,按下图复制 Cookie: How to get mys cookie 当触发时,可尝试按关闭,然后再次刷新页面,后复制 Cookie。 也可以使用另一种方法: 复制代码 浏览器无痕模式打开 http://user.mihoyo.com/ ,登录账号 按,打开,找到并点击 控制台粘贴代码并运行,获得类似的输出信息 部分即为所需复制的 Cookie,点击确定复制 部署方法--腾讯云函数版(推荐! ) 下载项目源码和压缩包 进入项目文件夹打开命令行执行以下命令 xxxxxxx为通过上面方式或取得米游社cookie 一定要用双引号包裹!! 例如: png 复制返回内容(包括括号) 例如: QQ截图20210505031552.png 登录腾讯云函数官网 选择函数服务-新建-自定义创建 函数名称随意-地区随意-运行环境Python3....
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值