强化学习实战2:动手写迷宫环境

迷宫环境介绍与创建

迷宫环境图示如下:

在这里插入图片描述

如图所示,其为一个 三乘三 的网格世界,我们要让 agent 从 S0 采取策略出发,然后走到 S8,图中红线部分表示障碍不能逾越,其中 S1 和 S4 之间有一个障碍,S3 和 S4 之间有一个障碍,S3 和 S6 之间有一个障碍,S5 和 S8 之间有一个障碍。

这就是我们的迷宫环境。

代码如下:

import matplotlib.pyplot as plt# 创建一个新的图形对象,并设置其大小为 5x5 英寸
fig = plt.figure(figsize=(5, 5))# 获取当前图形对象的轴对象
ax = plt.gca()# 设置坐标轴的范围
ax.set_xlim(0, 3)
ax.set_ylim(0, 3)# 绘制红色的方格边界,表示迷宫的结构
plt.plot([2, 3], [1, 1], color='red', linewidth=2)
plt.plot([0, 1], [1, 1], color='red', linewidth=2)
plt.plot([1, 1], [1, 2], color='red', linewidth=2)
plt.plot([1, 2], [2, 2], color='red', linewidth=2)# 在指定位置添加文字标签,表示每个状态(S0-S8)、起点和终点
plt.text(0.5, 2.5, 'S0', size=14, ha='center')
plt.text(1.5, 2.5, 'S1', size=14, ha='center')
plt.text(2.5, 2.5, 'S2', size=14, ha='center')
plt.text(0.5, 1.5, 'S3', size=14, ha='center')
plt.text(1.5, 1.5, 'S4', size=14, ha='center')
plt.text(2.5, 1.5, 'S5', size=14, ha='center')
plt.text(0.5, 0.5, 'S6', size=14, ha='center')
plt.text(1.5, 0.5, 'S7', size=14, ha='center')
plt.text(2.5, 0.5, 'S8', size=14, ha='center')
plt.text(0.5, 2.3, 'Start', ha='center')
plt.text(2.5, 0.3, 'Goal', ha='center')# 设置坐标轴的显示参数,使得坐标轴不显示
plt.tick_params(axis='both', which='both',bottom=False, top=False,right=False, left=False,labelbottom=False, labelleft=False)# 在起点位置绘制一个绿色的圆形表示当前位置
line, = ax.plot([0.5], [2.5], marker='o', color='g', markersize=60)# 显示图形
plt.show()

运行结果如下:

在这里插入图片描述

agent 的动作策略设计

如下图所示:

在这里插入图片描述

不难看出,state 总共有九个,也就是离散(discrete)且有限(finite)的三乘三网格世界。

而动作空间共有上下左右四个选择,用一个数组来表示。

而 Π 就是我们的策略了,可以看见其有一个下标 θ,这是参数的意思,在神经网络参与的算法中这个策略参数由神经网络确定,而在本节中尚未提及神经网络,因此使用一个 table ,也就是一个 状态-行为 矩阵来确定策略。

下面开始来刻画环境需要的边界和障碍信息:

# 刻画环境:边界border 和 障碍barrier
theta_0 = np.array([[np.nan, 1, 1, np.nan],  # 表示S0时的策略,即agent不能往上、不能往左走,但可以往右和下走[np.nan, 1, np.nan, 1],  # S1[np.nan, np.nan, 1, 1],  # S2[1, np.nan, np.nan, np.nan],  # S3[np.nan, 1, 1, np.nan],  # S4[1, np.nan, np.nan, 1],  # S5[np.nan, 1, np.nan, np.nan],  # S6[1, 1, np.nan, 1],  # S7# S8 已经是终点了,因此不再需要上下左右到处走了
])

然后需要将上述的参数矩阵给转换成策略 Π 的值,也就是采取各个 action 的概率值:

# 将 theta_0 转换为 策略 Π,而 Π 其实就是概率值嘛
def cvt_theta_0_to_pi(theta):m, n = theta.shapepi = np.zeros((m, n))for r in range(m):# pi[r, :]这是一个赋值操作的左侧表达式,它使用了NumPy的索引机制。# 在这里,pi 应该是一个二维数组(矩阵),r 表示行索引,: 表示选择该行的所有列。# 因此,这一部分指定了 pi 矩阵中的第 r 行的所有列。# np.nansum() 这是一个NumPy函数调用。np.nansum() 用于计算数组中元素的总和,忽略 NaN 值。# theta[r, :] 提供了一个一维数组作为函数的参数,表示对 theta 矩阵中第 r 行的所有元素进行求和。# 因此下面这一行代码是一个按元素的除法操作。# 它将 theta 矩阵中第 r 行的每个元素分别除以该行所有元素的和(忽略 NaN 值)。# 这样做可以将该行的元素归一化为一个概率分布,确保它们的总和为 1。pi[r, :] = theta[r, :] / np.nansum(theta[r, :])return np.nan_to_num(pi)pi = cvt_theta_0_to_pi(theta_0)
# 先打印一下看是什么样子的结果
print(pi)

运行结果如下:

在这里插入图片描述

可以看见此时的 state-action 矩阵就已经构建好了。

下面构建动作空间列表以及状态转移函数:

# 动作空间:0表示向上,1表示向右,2表示向下,3表示向左
actions = list(range(4))# 状态转移函数
def step(state, action):if action == 0:state -= 3elif action == 1:state += 1elif action == 2:state += 3elif action == 3:state -= 1return state

策略测试

那么接下来我们就可以进行测试了,使用上述构建的环境和配置,可以进行路径的搜索了:

state = 0
action_history = []
state_history = [state]
while True:"""对下面这一行代码进行的解释:np.random.choice:这是 NumPy 库中的一个函数,用于从给定的一维数组或类似序列中随机选择元素。在这里,我们将从 actions 数组中随机选择一个元素。actions:这是一个包含可选动作的一维数组或列表。np.random.choice 将从这个数组中进行选择。p=pi[state, :]:这是 np.random.choice 函数的一个参数,用于指定每个元素被选择的概率。在这里,pi 是一个二维数组(矩阵),state 是当前状态的索引,: 表示选择该行的所有列。因此,pi[state, :] 提供了一个概率分布,用于指定在选择时每个动作的相对概率。赋值操作:action = ... 将 np.random.choice 的结果赋值给变量 action。这意味着 action 将是从 actions 数组中随机选择的一个元素,选择的概率由 pi[state, :] 给出。"""action = np.random.choice(actions, p=pi[state, :])state = step(state, action)if state == 8:state_history.append(8)breakaction_history.append(action)state_history.append(state)print(len(state_history))
print(state_history)

运行结果如下:

在这里插入图片描述

可以看到在这一次运行中,agent 走了 38 步才到达 S8,所走过的路径如上图所示。

动画可视化搜索过程

直接上代码:

from matplotlib import animation
from IPython.display import HTMLdef init():line.set_data([], [])return (line, )def animate(i):state = state_history[i]x = (state % 3) + 0.5y = 2.5 - int(state / 3)line.set_data(x, y)anim = animation.FuncAnimation(fig, animate, init_func=init, frames=len(state_history), interval=200, repeat=False)
anim.save('maze_0.mp4')
# 视频观测有时候不太友好,我们还可以使用 IPython 提供的 HTML 的交互式工具
# 由于 PyCharm 不支持显示 IPython 的交互式输出,因此我们这里将 IPython 的输出转换为 HTML 文件再打开
with open('animation.html', 'w') as f:f.write(anim.to_jshtml())

运行结果如下:

在这里插入图片描述

比起直接观看视频,这种交互式的过程更能帮助我们从细节上一点一点看清楚 agent 的运行轨迹。

总结

上面的代码是为了讲清楚各部分的功能和实现细节,全部代码合在一起就是下面这样:

import matplotlib.pyplot as plt
import numpy as np# 创建一个新的图形对象,并设置其大小为 5x5 英寸
fig = plt.figure(figsize=(5, 5))# 获取当前图形对象的轴对象
ax = plt.gca()# 设置坐标轴的范围
ax.set_xlim(0, 3)
ax.set_ylim(0, 3)# 绘制红色的方格边界,表示迷宫的结构
plt.plot([2, 3], [1, 1], color='red', linewidth=2)
plt.plot([0, 1], [1, 1], color='red', linewidth=2)
plt.plot([1, 1], [1, 2], color='red', linewidth=2)
plt.plot([1, 2], [2, 2], color='red', linewidth=2)# 在指定位置添加文字标签,表示每个状态(S0-S8)、起点和终点
plt.text(0.5, 2.5, 'S0', size=14, ha='center')
plt.text(1.5, 2.5, 'S1', size=14, ha='center')
plt.text(2.5, 2.5, 'S2', size=14, ha='center')
plt.text(0.5, 1.5, 'S3', size=14, ha='center')
plt.text(1.5, 1.5, 'S4', size=14, ha='center')
plt.text(2.5, 1.5, 'S5', size=14, ha='center')
plt.text(0.5, 0.5, 'S6', size=14, ha='center')
plt.text(1.5, 0.5, 'S7', size=14, ha='center')
plt.text(2.5, 0.5, 'S8', size=14, ha='center')
plt.text(0.5, 2.3, 'Start', ha='center')
plt.text(2.5, 0.3, 'Goal', ha='center')# 设置坐标轴的显示参数,使得坐标轴不显示
plt.tick_params(axis='both', which='both',bottom=False, top=False,right=False, left=False,labelbottom=False, labelleft=False)# 在起点位置绘制一个绿色的圆形表示当前位置
line, = ax.plot([0.5], [2.5], marker='o', color='g', markersize=60)# 显示图形
# plt.show()# 刻画环境:边界border 和 障碍barrier
theta_0 = np.array([[np.nan, 1, 1, np.nan],  # 表示S0时的策略,即agent不能往上、不能往左走,但可以往右和下走[np.nan, 1, np.nan, 1],[np.nan, np.nan, 1, 1],[1, np.nan, np.nan, np.nan],[np.nan, 1, 1, np.nan],[1, np.nan, np.nan, 1],[np.nan, 1, np.nan, np.nan],[1, 1, np.nan, 1],# S8 已经是终点了,因此不再需要上下左右到处走了
])# 将 theta_0 转换为 策略 Π,而 Π 其实就是概率值嘛
def cvt_theta_0_to_pi(theta):m, n = theta.shapepi = np.zeros((m, n))for r in range(m):# pi[r, :]这是一个赋值操作的左侧表达式,它使用了NumPy的索引机制。# 在这里,pi 应该是一个二维数组(矩阵),r 表示行索引,: 表示选择该行的所有列。# 因此,这一部分指定了 pi 矩阵中的第 r 行的所有列。# np.nansum() 这是一个NumPy函数调用。np.nansum() 用于计算数组中元素的总和,忽略 NaN 值。# theta[r, :] 提供了一个一维数组作为函数的参数,表示对 theta 矩阵中第 r 行的所有元素进行求和。# 因此下面这一行代码是一个按元素的除法操作。# 它将 theta 矩阵中第 r 行的每个元素分别除以该行所有元素的和(忽略 NaN 值)。# 这样做可以将该行的元素归一化为一个概率分布,确保它们的总和为 1。pi[r, :] = theta[r, :] / np.nansum(theta[r, :])return np.nan_to_num(pi)pi = cvt_theta_0_to_pi(theta_0)# 动作空间
actions = list(range(4))
print(actions)# 状态转移函数
def step(state, action):if action == 0:state -= 3elif action == 1:state += 1elif action == 2:state += 3elif action == 3:state -= 1return statestate = 0
action_history = []
state_history = [state]
while True:"""对下面这一行代码进行的解释:np.random.choice:这是 NumPy 库中的一个函数,用于从给定的一维数组或类似序列中随机选择元素。在这里,我们将从 actions 数组中随机选择一个元素。actions:这是一个包含可选动作的一维数组或列表。np.random.choice 将从这个数组中进行选择。p=pi[state, :]:这是 np.random.choice 函数的一个参数,用于指定每个元素被选择的概率。在这里,pi 是一个二维数组(矩阵),state 是当前状态的索引,: 表示选择该行的所有列。因此,pi[state, :] 提供了一个概率分布,用于指定在选择时每个动作的相对概率。赋值操作:action = ... 将 np.random.choice 的结果赋值给变量 action。这意味着 action 将是从 actions 数组中随机选择的一个元素,选择的概率由 pi[state, :] 给出。"""action = np.random.choice(actions, p=pi[state, :])state = step(state, action)if state == 8:state_history.append(8)breakaction_history.append(action)state_history.append(state)print(len(state_history))
print(state_history)from matplotlib import animation
from IPython.display import HTMLdef init():line.set_data([], [])return (line, )def animate(i):state = state_history[i]x = (state % 3) + 0.5y = 2.5 - int(state / 3)line.set_data(x, y)anim = animation.FuncAnimation(fig, animate, init_func=init, frames=len(state_history), interval=200, repeat=False)
anim.save('maze_0.mp4')
# 视频观测有时候不太友好,我们还可以使用 IPython 提供的 HTML 的交互式工具
# 由于 PyCharm 不支持显示 IPython 的交互式输出,因此我们这里将 IPython 的输出转换为 HTML 文件再打开
with open('animation.html', 'w') as f:f.write(anim.to_jshtml())

封装迷宫环境 API

上面的代码虽然已经完成了环境的编写了,但是过于混乱,因此这一节我们仿照 Gym 的格式将其封装一下。

主要就是两个部分,一个是 环境,还有一个是 Agent。

对于环境其最重要的就是 step 函数,step 函数接收一个 action 然后返回一个新的状态以及 reward 等各种信息。

对于 Agent 而言其最重要的是一个动作策略的选取。

基本结构如下:

import gym
import numpy as np
import matplotlib.pyplot as pltclass MazeEnv(gym.Env):def __init__(self):passdef reset(self):passdef step(self, action):passclass Agent:def __init__(self):passdef choose_action(self, state):pass

封装如下:

import gym
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation
from IPython.display import HTML# MazeEnv 类维护着状态,以及 step 函数的返回
class MazeEnv(gym.Env):def __init__(self):self.state = 0def reset(self):self.state = 0return self.statedef step(self, action):if action == 0:self.state -= 3elif action == 1:self.state += 1elif action == 2:self.state += 3elif action == 3:self.state -= 1done = Falseif self.state == 8:done = True# 1 表示 reward, done 表示是否结束# {} 是一个空的字典字面量,表示返回的第四个值是一个空字典。return self.state, 1, done, {}# Agent 类基于当前环境中的状态选择动作形成策略
class Agent:def __init__(self):# action spaceself.actions = list(range(4))# 刻画环境:边界 border 和 障碍 barrierself.theta_0 = np.array([[np.nan, 1, 1, np.nan],  # 表示S0时的策略,即agent不能往上、不能往左走,但可以往右和下走[np.nan, 1, np.nan, 1],[np.nan, np.nan, 1, 1],[1, np.nan, np.nan, np.nan],[np.nan, 1, 1, np.nan],[1, np.nan, np.nan, 1],[np.nan, 1, np.nan, np.nan],[1, 1, np.nan, 1],# S8 已经是终点了,因此不再需要上下左右到处走了])# 策略 Πself.pi = self._cvt_theta_0_to_pi(self.theta_0)# 将 theta_0 转换为 策略 Π,而 Π 其实就是概率值嘛def _cvt_theta_0_to_pi(self, theta):m, n = theta.shapepi = np.zeros((m, n))for r in range(m):pi[r, :] = theta[r, :] / np.nansum(theta[r, :])return np.nan_to_num(pi)def choose_action(self, state):action = np.random.choice(self.actions, p=self.pi[state, :])return action# MazeAnimation 类封装了可视化展示代码的 API
class MazeAnimation:def __init__(self):# 创建一个新的图形对象,并设置其大小为 5x5 英寸self.fig = plt.figure(figsize=(5, 5))# 获取当前图形对象的轴对象self.ax = plt.gca()# 设置坐标轴的范围self.ax.set_xlim(0, 3)self.ax.set_ylim(0, 3)# 绘制红色的方格边界,表示迷宫的结构plt.plot([2, 3], [1, 1], color='red', linewidth=2)plt.plot([0, 1], [1, 1], color='red', linewidth=2)plt.plot([1, 1], [1, 2], color='red', linewidth=2)plt.plot([1, 2], [2, 2], color='red', linewidth=2)# 在指定位置添加文字标签,表示每个状态(S0-S8)、起点和终点plt.text(0.5, 2.5, 'S0', size=14, ha='center')plt.text(1.5, 2.5, 'S1', size=14, ha='center')plt.text(2.5, 2.5, 'S2', size=14, ha='center')plt.text(0.5, 1.5, 'S3', size=14, ha='center')plt.text(1.5, 1.5, 'S4', size=14, ha='center')plt.text(2.5, 1.5, 'S5', size=14, ha='center')plt.text(0.5, 0.5, 'S6', size=14, ha='center')plt.text(1.5, 0.5, 'S7', size=14, ha='center')plt.text(2.5, 0.5, 'S8', size=14, ha='center')plt.text(0.5, 2.3, 'Start', ha='center')plt.text(2.5, 0.3, 'Goal', ha='center')# 设置坐标轴的显示参数,使得坐标轴不显示plt.tick_params(axis='both', which='both',bottom=False, top=False,right=False, left=False,labelbottom=False, labelleft=False)# 在起点位置绘制一个绿色的圆形表示当前位置self.line, = self.ax.plot([0.5], [2.5], marker='o', color='g', markersize=60)# state_history 是状态历史数组def save_as_mp4_html(self, state_history):def init():self.line.set_data([], [])return (self.line,)def animate(i):state = state_history[i]x = (state % 3) + 0.5y = 2.5 - int(state / 3)self.line.set_data(x, y)anim = animation.FuncAnimation(self.fig, animate, init_func=init, frames=len(state_history), interval=200,repeat=False)anim.save('maze.mp4')with open('maze_animation.html', 'w') as f:f.write(anim.to_jshtml())# -------------------------------测试代码---------------------------------# 创建迷宫环境
env = MazeEnv()
# 重置迷宫环境到初始状态
state = env.reset()
# 创建 agent
agent = Agent()
# 结束标志 done
done = False
# 动作历史
action_history = []
# 状态历史,含一开始位于 S0 的状态
state_history = [state]
# 一个循环就是一个 episode
while not done:action = agent.choose_action(state)state, reward, done, _ = env.step(action)state_history.append(state)action_history.append(action)# 打印状态历史长度
print(len(state_history))
# 可视化展示
# 创建封装好的可视化对象
show = MazeAnimation()
# 调用该对象中的可视化展示函数
show.save_as_mp4_html(state_history)

运行结果和之前一样,不再赘述。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3226971.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

换新启航环游浪漫新篇章

✨🎉【焕新启航,环游浪漫新篇章 —— 《焕新环游传》盛大开播】🎉✨在时光的温柔转角,一场前所未有的梦幻之旅悄然拉开序幕!🌟《焕新环游传》—— 这不仅仅是一部剧集的开播,更是对过往角色遗憾…

Springboot 设置个性化banner

在 Spring Boot 中自定义 banner 的方法有几种,可以通过以下步骤来实现: 1、使用文本文件作为 banner 在 src/main/resources 目录下创建一个名为 banner.txt 的文件。 编辑这个文件,输入想要显示的文本。确保文本中包含换行符和空格…

Vue 项目中 history 路由模式的使用

在最近帮客户开发的一个项目中,由于项目的特殊性,需要用到 Vue 中的 history路由模式。该模式使用时会涉及到“上传白屏”和“刷新 404 问题”。在帮助客户解决这两个问题的过程中,总结问题的解决方案并记录下来,希望能够保留这篇…

将格内多行文字展开成多格

表格的A列是分类,B列由多行文字组成,即分隔符是换行符。 AB1Account NumberInteraction21Jan 1,2023 - Hello.32Jan 2, 2023 - Good morning. Jan 3, 2023 - Good night. Jan 4, 20 Jan 5, 2023 - Good night. Jan 6, 2023 - Good afternoon.43Jan 1,20…

中霖教育:经济师的十个专业类别怎么选?

经济师一共包含十个专业类别,分别是工商管理、农业经济、财政税收、金融、保险、人力资源管理、旅游经济、运输经济、建筑与房地产经济、知识产权。 经济师选择报考专业时有哪些建议? 1、职业规划是选择专业的首要考虑点。未来的职业发展途径应与所选专业紧密相连…

wifi模组Ai-M62-32S的IO映射和UDP透传测试

wifi模组Ai-M62-32S的IO映射和UDP透传测试 基本IO 映射配网示例开启UDP透传示例复位AT查询wifi是否在线配置DHCP静态IP连接wifi连接UDP开启透传 基本IO 映射 对于wifi模组Ai-62-32S来说其模组 IO 引脚(从模组左上角逆时针排序,引脚序号从 1 开始&#x…

python+selenium-UI自动框架之[优化]元素查找和BasePage页面

痛点:在页面查找元素的时候会遇到找不到或者其他无法处理某个字段的情况,又或者想要在输出的log或者report里面显示这个字段名称,这时候加上字段名称就很重要! [3]pythonselenium - UI自动框架之封装查找元素https://mp.csdn.net…

前端如何去看蓝湖

首先加入团队,在内容中我们可以看到点击图片,右边出现的图 包含了像素甚至有代码,我们可以参考这个代码。 那么在使用之前我们需要调整好像素,例如我们的像素宽为375,不用去管高,然后这个宽度我们可以去自…

camera-qsc-crosstalk校准数据XTALK回写

问题背景 手机越做越紧凑,需要模组和芯片尺寸越做越小,在尺寸一定的基础上,高像素和大像素,对于手机摄像头来说,一直是一对矛盾的存在。 高像素:带来高分辨率画质大像素:带来暗态下高感光度和…

linux radix-tree 基数树实现详解

radix tree,又称做基数树,是一种适合于构建key(index)与value(item)相关联的数据结构。内核中使用非常广泛。本文主要聚焦linux内核基数树的代码实现,大量注释过的代码。 radix-tree组织结构如下: 1、数据结构 /** The bottom two bits of the slot de…

首届UTON区块链开发者计划大会在马来西亚圆满落幕

7月9日,首届UTON区块链开发者计划大会在马来西亚吉隆坡成功举办! 来自全球顶尖的行业领袖、技术精英和众多区块链爱好者参与了此次盛会,也标志着UTON区块链生态进入了一个全新的发展阶段。 会上,UTON区块链创始人之一唐毅先生以“…

Linux运维:MySQL中间件代理服务器,mycat读写分离应用实验

Mycat适用的场景很丰富,以下是几个典型的应用场景: 1.单纯的读写分离,此时配置最为简单,支持读写分离,主从切换 2.分表分库,对于超过1000万的表进行分片,最大支持1000亿的单表分片 3.多租户应…

(pyqt5)弹窗-Token验证

前言 为了保护自己的工作成果,控制在合理的范围内使用,设计一个用于Token验证的弹窗. 代码 class TokenDialog(QDialog):def __init__(self, parentNone, login_userNone, mac_addrNone, funcNone):super(TokenDialog, self).__init__(parent)self.login_user login_userself…

为什么说java只要还是泛型擦除,就不要吹自己高性能?

在开始前刚好我有一些资料,是我根据网友给的问题精心整理了一份「java的资料从专业入门到高级教程」, 点个关注在评论区回复“666”之后私信回复“666”,全部无偿共享给大家!!!关于“Java只要还是泛型擦除…

LabVIEW航空发动机试验器数据监测分析

1. 概述 为了适应航空发动机试验器的智能化发展,本文基于图形化编程工具LabVIEW为平台,结合航空发动机试验器原有的软硬件设备,设计开发了一套数据监测分析功能模块。主要阐述了数据监测分析功能设计中的设计思路和主要功能,以及…

2008年上半年软件设计师【下午题】真题及答案

文章目录 2008年上半年软件设计师下午题--真题2008年上半年软件设计师下午题--答案 2008年上半年软件设计师下午题–真题 2008年上半年软件设计师下午题–答案

拯救中国足球,要不尝试一下DDD事件风暴?

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 张逸老师写了新文章《领域建模的常见问题及解决方案》,我来谈一谈对这篇文章的感想。 (1)文章一开始,张逸老师大大地赞扬了事件风暴&am…

[leetcode]kth-smallest-element-in-a-sorted-matrix 有序矩阵中第k小元素

. - 力扣&#xff08;LeetCode&#xff09; class Solution { public:bool check(vector<vector<int>>& matrix, int mid, int k, int n) {int i n - 1;int j 0;int num 0;while (i > 0 && j < n) {if (matrix[i][j] < mid) {num i 1;j;…

基于Java技术的网上图书商城系统

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;Java技术、SpringBoot框架 工具&#xff1a;Eclipse、Navicat、Maven 系统展示 首页 用户注册界面…

机器学习与深度学习:区别与联系(含工作站硬件推荐)

一、机器学习与深度学习区别 机器学习&#xff08;ML&#xff1a;Machine Learning&#xff09;与深度学习&#xff08;DL&#xff1a;Deep Learning&#xff09;是人工智能&#xff08;AI&#xff09;领域内两个重要但不同的技术。它们在定义、数据依赖性以及硬件依赖性等方面…