当前位置: 首页 > news >正文

关于动态规划的一份介绍

动态规划算法的应用范围是极广的,在机器学习中它就有所运用,比较经典的如强化学习中策略评估与策略估计,还比如在NLP中有关标签的寻找时也会用到,所以我将在这篇文章中详细介绍动态规划算法有关的东西,如:基本概念、算法设计的思路与步骤、具体代码举例、具体ml中的应用等。

一、概念

动态规划(Dynamic Programming)是通过构建最优子结构,并将之存贮,在需要时不重复计算而直接拿来用以得到目标结果的一种算法。

接着,是有关dp算法的一些基本概念:

1.1 递归结构

动态规划通常用于解决可以分解为一系列子问题的问题。每个子问题的解可以用来构建更大问题的解。例如,在计算斐波那契数列时,第 n 项的值由前两项的和构成。

1.2 重叠子问题

当我们在解决某个问题时,我们会发现有一些问题我们会不断重复去计算,如此就会浪费大量的资源,所以,我们可以考虑将之前计算出的结果保存起来,然后在需要时调用这个存储的结果,如此来节约资源。

1.3 最优子结构

最优子结构是指问题的最优解包含了其子问题的最优解。也就是说,如果一个问题是通过解决若干子问题来解决的,那么这些子问题的解也应该是最优的。

二、算法设计思路与步骤

一般来说,dp算法通常可以分为四个步骤,分别为:定义状态、写状态转移方程、初始化与填充数组。接下来,我们详细来看看。

2.1 定义状态

确定问题的状态表示,也就是哪些变量可以唯一地描述问题的当前情况。例如,在背包问题中,状态可以由物品的索引和当前背包的剩余容量来表示。

2.2 状态转移方程

定义如何从一个状态转移到另一个状态,即状态转移方程。状态转移方程描述了如何根据当前状态和动作来计算下一个状态的值。继续以背包问题举例:

其中,wi是第 i 个物品的重量,vi​ 是第 i个物品的价值。

2.3 初始化

初始化是设置边界条件,即最简单或最基础的情况下的状态值。这些边界条件通常是显而易见的,例如在背包问题中,如果背包容量为 0 或没有物品可选时,价值都是 0。

同时,初始化也会包括预处理一些初始状态,以方便之后的求解。

2.4 填充数组

填充数组是按照状态转移方程逐步计算并填充状态值的过程。这一过程通常涉及到一个或多个嵌套循环,按照某种顺序遍历所有状态,并使用状态转移方程更新状态值。例如在背包问题中,填充数组的过程就是按照物品的顺序和背包容量的大小逐个计算最大价值。

三、具体代码举例

3.1 背包问题

对于上述的步骤具体地看,问题还是那个0/1背包问题,我们可以用Java代码来举个例子:

public class KnapsackProblem {public static void main(String[] args) {int[] weights = {2, 3, 4, 5};int[] values = {3, 4, 5, 6};int capacity = 5;System.out.println("The maximum value that can be obtained is: " + knapsack(weights, values, capacity));}public static int knapsack(int[] weights, int[] values, int capacity) {int n = values.length;int[][] dp = new int[n+1][capacity+1];//用于存储解// 当容量为0时,价值为0for (int i = 0; i <= n; i++) {dp[i][0] = 0;}// 当没有物品时,价值也为0for (int w = 0; w <= capacity; w++) {dp[0][w] = 0;}// 填充dp数组for (int i = 1; i <= n; i++) {for (int w = 1; w <= capacity; w++) {if (weights[i - 1] <= w) {dp[i][w] = Math.max(dp[i - 1][w], dp[i - 1][w - weights[i - 1]] + values[i - 1]);} else {dp[i][w] = dp[i - 1][w];}}}return dp[n][capacity];}
}

其输出为:The maximum value that can be obtained is: 7 

3.2 最长子序列问题

我们还可以用leetCode中这一题来作为例子来展示dp算法,题目如下:

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

该问题的代码为:

class Solution {public static int lengthOfLIS(int[] nums) {if (nums == null || nums.length == 0) {return 0;}int n = nums.length;int[] dp = new int[n];// 存储最优子问题的解for (int i = 0; i < n; i++) {dp[i] = 1;// 初始化}int maxLength = 1;for (int i = 1; i < n; i++) {for (int j = 0; j < i; j++) {if (nums[i]>nums[j]){dp[i] = Math.max(dp[i],dp[j]+1);// 状态转移方程}}maxLength = Math.max(maxLength,dp[i]);}return maxLength;}public static void main(String[] args) {int[] nums = {10, 9, 2, 5, 3, 7, 101, 18};int result = Solution.lengthOfLIS(nums);System.out.println("the Longest Increasing Subsequence is:"+result);}
}

该代码的输出为:the Longest Increasing Subsequence is:4

我们以第二个问题,即最长子序列问题的代码来画一个流程图以方便理解,流程图如下:

我们分析代码可以其时间复杂度为O(n^2),如果这个问题我们采用递归的方式来解决,那么其时间复杂度为O(2^n),而如果直接暴力求解的话,时间复杂度更是会到O(2^n*n)。

 四、机器学习中的dp算法

4.1 强化学习中策略评估与策略估计

如下这个代码是用dp算法解决马尔可夫决策问题,它属于一个简化的决策评估算法。策略评估的目标是找到一个特定策略下的状态值函数 V^π,即从每个状态出发,在遵循给定策略 π 的情况下所能获得的期望回报总和。

import numpy as npdef policy_evaluation(P, R, pi, gamma=0.9, theta=1e-6):n_states = len(P)n_actions = len(P[0])# 初始化状态值函数V = np.random.rand(n_states) * 0.1  # 使用随机小数初始化while True:delta = 0  # 用于检测收敛性的变量V_new = np.copy(V)  # 复制旧的状态值函数# 更新状态值函数for s in range(n_states):old_value = V[s]a = pi[s]  # 根据策略选择动作# 确保 P[s][a] 是一个概率分布assert len(P[s][a]) == n_states# 计算状态转移后的期望回报V_new[s] = sum(P[s][a][sp] * (R[sp] + gamma * V[sp]) for sp in range(n_states))# 计算最大变化delta = max(delta, abs(old_value - V_new[s]))# 检查收敛性if delta < theta:break# 更新状态值函数V = V_newreturn V# 示例 MDP 数据
# 假设状态集合为 {0, 1, 2}, 动作集合为 {0, 1}
# P 是一个三维数组,形状为 (状态数, 动作数, 状态数)
P = [[[0.5, 0.5, 0], [0, 0, 1]],  # 状态0的动作转移概率[[0.5, 0.5, 0], [0, 0, 1]],  # 状态1的动作转移概率[[0, 1, 0], [0, 0, 1]]  # 状态2的动作转移概率
]# R 是一个一维数组,表示各个状态的即时奖励
R = [1, -1, 0]# pi 是一个一维数组,表示在每个状态下应采取的动作
pi = [0, 1, 1]  # 假设策略为在状态0时采取动作0,在状态1和2时采取动作1# 调用策略评估函数
V = policy_evaluation(P, R, pi)
print("状态值函数:", V)

输出为:状态值函数: [9.9913792e-06 9.9913792e-06 9.9913792e-06] 

4.2 NLP标签寻找

在自然语言处理(NLP)中,动态规划(DP)可以应用于多种任务,如序列标注问题,其中包括命名实体识别(NER)、词性标注(POS tagging)等。这类任务通常可以通过隐马尔可夫模型(HMM)、条件随机场(CRF)或其他序列模型来解决。接下来,我将给出一个简单的用于寻找标签序列的python代码,其具体内容是使用动态规划的思想在一个简单的序列标注问题上寻找最优标签序列。

假设我们有一个句子,我们想要为其分配一系列标签(例如,词性标签或命名实体标签)。我们将使用一个简化版本的动态规划算法,该算法类似于Viterbi算法,用于找到最有可能的标签序列。

代码如下:

import numpy as npdef viterbi(obs, obs_to_idx, tag_to_idx, emission_matrix, transition_matrix, start_tag='<s>', end_tag='<e>'):"""使用Viterbi算法解码最有可能的标签序列。:param obs: 输入的单词列表:param obs_to_idx: 单词到索引的映射字典:param tag_to_idx: 标签到索引的映射字典:param emission_matrix: 发射概率矩阵:param transition_matrix: 转移概率矩阵:param start_tag: 开始标签:param end_tag: 结束标签:return: 最有可能的标签序列"""T = len(obs)  # 单词的数量L = len(tag_to_idx)  # 标签的数量# 初始化动态规划表dp = np.zeros((T, L))  # 存储每个时间步的最优路径得分backpointer = np.zeros((T, L), dtype=int)  # 存储每个时间步的前一个标签的索引# 设置开始状态dp[0, :] = emission_matrix[tag_to_idx[start_tag], obs_to_idx[obs[0]]] * \transition_matrix[tag_to_idx[start_tag], :]# 动态规划for t in range(1, T):for j in range(L):  # 当前标签jmax_score = float('-inf')prev_tag_best = Nonefor i in range(L):  # 上一个标签iscore = dp[t - 1, i] * \transition_matrix[i, j] * \emission_matrix[j, obs_to_idx[obs[t]]]if score > max_score:max_score = scoreprev_tag_best = idp[t, j] = max_scorebackpointer[t, j] = prev_tag_best# 找到最后一个词的最佳标签索引best_path_score = float('-inf')best_path_last_tag = Nonefor k in range(L):score = dp[T - 1, k] * transition_matrix[k, tag_to_idx[end_tag]]if score > best_path_score:best_path_score = scorebest_path_last_tag = k# 回溯找出最优路径best_path = [best_path_last_tag]for t in reversed(range(T - 1)):best_path.insert(0, backpointer[t, best_path[0]])# 将索引转换回标签tags = [list(tag_to_idx.keys())[int(i)] for i in best_path]return tags# 示例数据
words = ['The', 'cat', 'sat', 'on', 'the', 'mat']
obs_to_idx = {word: idx for idx, word in enumerate(words)}
tag_to_idx = {'<s>': 0, '<e>': 1, 'O': 2, 'B-PER': 3, 'I-PER': 4, 'B-LOC': 5, 'I-LOC': 6}# 随机初始化发射矩阵和转移矩阵
emission_matrix = np.random.rand(len(tag_to_idx), len(words))
transition_matrix = np.random.rand(len(tag_to_idx), len(tag_to_idx))
transition_matrix /= transition_matrix.sum(axis=1)[:, np.newaxis]  # 归一化转移概率# 调用Viterbi算法
tags = viterbi(words, obs_to_idx, tag_to_idx, emission_matrix, transition_matrix)
print("最佳标签序列:", tags)

其输出为:最佳标签序列: ['<s>', 'I-PER', '<s>', 'I-PER', 'B-LOC', 'O']

关于ml中的dp算法的应用还有很多,我将在之后的强化学习一文中给出更多运用了dp算法的代码举例。

此上


http://www.mrgr.cn/news/55385.html

相关文章:

  • 软件I2C的代码
  • ANSYS Workbench纤维混凝土3D
  • 外贸企业如何应对网络卡顿与网页打不开的问题
  • 学习eNSP对提升就业竞争力有多大帮助?
  • 在Rocky Linux上安装Docker
  • 【微信小程序_19_自定义组件(1)】
  • 【AI大模型】本地部署 Code Llama 大模型
  • ROS 2 Jazzy Jalisco 模型工具 Xacro 总结和习题
  • FineReport 页面设置
  • 【小白学机器学习17】 概率论的认识论和方法论
  • 鲸信私有化即时通信如何平衡安全性与易用性之间的关系?
  • 观察者模式
  • Vue--数据代理
  • 相同的树算法
  • 在做题中学习(63):替换问号
  • vue3学习记录-TransitionGroup
  • 携手并进,智驭教育!和鲸科技与智谱 AI 签署“101 数智领航计划”战略合作协议
  • GB28181协议视频监控平台-鉴权的含义
  • Java 当中使用 “google.zxing ”开源项目 和 “github 的 qrcode-plugin” 开源项目 生成二维码
  • 【04】双样本等方差(t-检验)
  • P3137 [USACO16FEB] Circular Barn S
  • 全面了解 NGINX 的负载均衡算法
  • c语言基础程序——经典100道实例(二)
  • 中电金信重磅发布《金融数据安全治理白皮书》
  • 百度地图引入个性化样式,加载时出现大片白块的解决办法
  • 数据中心母线槽测温监控装置的优势和如何选型