深度强化学习入门:马尔可夫决策过程(井字棋案例理解)
注:笔记 来自知乎文章 深度强化学习综述(上)
Tips①:只是记录从这个文章学到的东西
Tips②:只摘选了文章中部分理论阅读整理
Tips③:重点是对文章中的一些公式进行理解,方便自己回顾
Tips④:本人也只是刚刚入门,本文如果有错误的地方,请在评论区指正,谢谢!
文章目录
- 一、马尔可夫决策过程(MDP)
- 二、智能体决策系统
一、马尔可夫决策过程(MDP)
马尔可夫过程(MP)的特点是:(环境)系统下一时刻的状态只由当前时刻的状态决定,与更早时刻无关。
马尔可夫决策过程(MDP)与马尔可夫过程(MP)不同的点在于,MDP中智能体(Agent)可以执行动作(Action),从而改变自身状态和环境状态。
强化学习其实就是让智能体学会根据环境做出一个奖励最高的决策动作,也就是下图所表示的
强化学习的问题可以抽象为一个 MDP:
MDP:{S,A,P,Ra,γ}\text{MDP}:\{ S, A, P, R_a, \gamma \} MDP:{S,A,P,Ra,γ}
其中各个属性的解释和举例如下:
S={S1,S2,⋯,Sn}S = \{ S_1, S_2, \cdots, S_n \}S={S1,S2,⋯,Sn} 表示所有状态的集合,即状态空间
sts_tst 表示 ttt 时刻的状态
有一种特殊的状态,叫做终止状态(吸收状态)A={A1,A2,⋯,An}A = \{ A_1, A_2, \cdots, A_n \}A={A1,A2,⋯,An} 表示所有动作的集合,即动作空间
pa(s,s′)p_a(s, s')pa(s,s′) 表示状态 sss 执行动作 aaa 后进入状态 s′s's′ 的概率:
pa(s,s′)=p(st+1=s∣st=s,at=a)p_a(s, s') = p(s_{t+1} = s | s_t = s , a_t = a)pa(s,s′)=p(st+1=s∣st=s,at=a)MDP 中的 PPP 可以理解成一个 (nS,nA,nS)(n_S, n_A, n_S)(nS,nA,nS) 形状的三维张量,可以称之为状态转移张量,里面任意一个元素 P[Sa][Ai][Sb]P[S_a][A_i][S_b]P[Sa][Ai][Sb] 表示了在 SaS_aSa 状态下 执行动作 AiA_iAi 后转移到 SbS_bSb 状态的概率
Ra(s,s′)R_a(s, s')Ra(s,s′) 表示状态 sss 执行动作 aaa 后进入状态 s′s's′ 后得到的即时奖励,所以 RRR 是一个奖励函数
γ\gammaγ 为奖励衰减因子。
我们往往期望状态 sss 能尽早达到我们的预期,因此 ttt 时刻的状态 sts_tst 进入 st+1s_{t+1}st+1 后得到的即时奖励将会衰减为原来的 γt\gamma^{t}γt
个人理解 + 举例:
- MDP 更多是对 环境规则、个人期望 的描述。
- MDP 中的前三项 S、A、PS、A、PS、A、P 反应的是环境状态的可能情况、动作的可能情况、执行动作后状态迁移的概率分布,这些都是由环境规则决定的,一旦一个环境的规则确定下来,那么这些其实也是确定的。
- MDP 中的后两项 R、γR、\gammaR、γ 反应的是个人期望,你希望得到一个什么样的最终状态、多快得到这个状态可以接受,你就要根据你的个人期望设置相应的 奖励函数 RRR 和 衰减因子 γ\gammaγ.
- 拿井字棋游戏举例,环境规则已经定好了(轮流下子,谁先 333 子连成一条直线则获胜),那么
- 状态 SSS 可以是所有你下子前的棋盘分布(也可以是稍作归纳后的棋谱)
- 动作空间 AAA 是你下子的 999 个位置
- 状态转移张量 PPP 则指示你,在你下子后,对方可能如何下子、使得棋盘变为何种新的状态,PPP 将告知你进入各种状态的概率。
- 举个例子,第 333 回合,棋局状态 s3s_3s3 如下:
◯◯×◯××记为状态S1% 此行注释 \def \arraystretch{2} \def \arraystretch{1.6} \begin{array}{| c | c | c | } \hline \textcircled{} & \textcircled{} & \\ \hline \times & \textcircled{} & \\ \hline & \times & \times \\ \hline \end{array} \qquad 记为状态 S_1 ◯×◯◯××记为状态S1
如果你在 s3=S1s_3 = S_1s3=S1 的环境状态下做出了动作 a3=A7a_3 = A_7a3=A7,即在从左往右、再从上往下数的第 777 个格子下 ◯\textcircled{}◯ 子,那么你可能得到的状态就是以下两种:
◯◯××◯◯××记为状态S2◯◯×◯×◯××记为状态S3\begin{aligned} \def \arraystretch{1.6} \begin{array}{| c | c | c | } \hline \textcircled{} & \textcircled{} & \times \\ \hline \times & \textcircled{} & \\ \hline \textcircled{} & \times & \times \\ \hline \end{array} \qquad 记为状态 S_2 \\ \\ \def \arraystretch{1.6} \begin{array}{| c | c | c | } \hline \textcircled{} & \textcircled{} & \\ \hline \times & \textcircled{} & \times \\ \hline \textcircled{} & \times & \times \\ \hline \end{array} \qquad 记为状态 S_3 \end{aligned} ◯×◯◯◯×××记为状态S2◯×◯◯◯×××记为状态S3
我们前面说过,状态转移张量 PPP 是一个三维的张量,我们假设这里状态空间有 nSn_SnS 个状态,这里动作空间有 999 个动作,那么我们的状态转移张量 PPP 的形状就是 (nS,9,nS)(n_S, 9, n_S)(nS,9,nS)在当前状态 s3=S1s_3 = S_1s3=S1 下,我们执行了动作 a3=A7a_3 = A_7a3=A7,那么我们访问 P[s3][a3]P[s_3][a_3]P[s3][a3] ,就可以得到下一状态 s4s_4s4 的概率分布如下(这里我们假设对方会在所有能下子的地方随机下子):
a3=A7⋯S2S3⋯S100.50.50\def \arraystretch{1.6} \begin{array}{| c | c | c | c | c | } \hline a_3 = A_7 & \cdots & S_2 & S_3 & \cdots \\ \hline S_1 & 0 & 0.5 & 0.5 & 0 \\ \hline \end{array} a3=A7S1⋯0S20.5S30.5⋯0
如果你想看清 PPP 这个张量的全貌,那大概是 999 个下面这样的表的堆叠(把左上角的动作换掉):
a=Ai时st\st+1S1S2⋯Sn∑S11S21⋮1Sn1\def \arraystretch{1.6} \begin{array}{| c | c | c | c | c | c | } \hline a =A_i 时 s_t \; \backslash \; s_{t+1} & S_1 & S_2 & \cdots & S_n & \sum \\ \hline S_1 & & & & & 1 \\ \hline S_2 & & & & & 1 \\ \hline \vdots & & & & & 1 \\ \hline S_n & & & & & 1 \\ \hline \end{array} a=Ai时st\st+1S1S2⋮SnS1S2⋯Sn∑1111
- 举个例子,第 333 回合,棋局状态 s3s_3s3 如下:
- 奖励函数 RRR 是你期望获胜而设置的,那你就需要在获胜时给予正的激励、平局或失败时给予负的激励。
- 比如你可以先将状态简单的划分为 S初态、S过程、S获胜、S平局、S失败S_{初态}、S_{过程}、S_{获胜}、S_{平局}、S_{失败}S初态、S过程、S获胜、S平局、S失败,可以设置 RRR 为一个二维表如下:
R(st,st+1)S初态S过程S获胜S平局S失败S初态\0\\\S过程\0100−20−200S获胜\\\\\S平局\\\\\S失败\\\\\\def \arraystretch{1.6} \begin{array}{| c | c | c | c | c | c | } \hline R(s_t, s_{t+1}) & S_{初态} & S_{过程} & S_{获胜} & S_{平局} & S_{失败} \\ \hline S_{初态} & \backslash & 0 & \backslash & \backslash & \backslash \\ \hline S_{过程} & \backslash & 0 & 100& -20 & -200 \\ \hline S_{获胜} & \backslash & \backslash & \backslash & \backslash & \backslash \\ \hline S_{平局} & \backslash & \backslash & \backslash & \backslash & \backslash \\ \hline S_{失败} & \backslash & \backslash & \backslash & \backslash & \backslash \\ \hline \end{array} R(st,st+1)S初态S过程S获胜S平局S失败S初态\\\\\S过程00\\\S获胜\100\\\S平局\−20\\\S失败\−200\\\
- 比如你可以先将状态简单的划分为 S初态、S过程、S获胜、S平局、S失败S_{初态}、S_{过程}、S_{获胜}、S_{平局}、S_{失败}S初态、S过程、S获胜、S平局、S失败,可以设置 RRR 为一个二维表如下:
- 衰减因子 γ\gammaγ 是你为了尽快获胜而设置的一个奖励剩余系数
- 就拿我们之前讲状态转移张量 PPP 所举的 例子 来说,我们在 s3=S1s_3 = S_1s3=S1 状态选择了 动作 a3=A7a_3 = A_7a3=A7,后续有可能的状态转移如下:
◯◯×◯××s3=S1(S过程)→a3=A7◯◯××◯◯××s4=S2(S过程)→a4=A6◯◯××◯◯◯××s5=S4(S平局)◯◯×◯×◯××s4=S3(S过程)→a4=A3◯◯◯×◯×◯××s5=S5(S获胜)\begin{aligned} \def \arraystretch{1.6} \begin{array}{| c | c | c | } \hline \textcircled{} & \textcircled{} & \\ \hline \times & \textcircled{} & \\ \hline & \times & \times \\ \hline \end{array} \\ s_3 = S_1(S_{过程}) \end{aligned} \qquad \xrightarrow[]{a_3 = A_7} \qquad \begin{aligned} \begin{aligned} \def \arraystretch{1.6} \begin{array}{| c | c | c | } \hline \textcircled{} & \textcircled{} & \times \\ \hline \times & \textcircled{} & \\ \hline \textcircled{} & \times & \times \\ \hline \end{array} \\ s_4 = S_2(S_{过程}) \end{aligned} \qquad \xrightarrow[]{a_4 = A_6} \qquad \begin{aligned} \def \arraystretch{1.6} \begin{array}{| c | c | c | } \hline \textcircled{} & \textcircled{} & \times \\ \hline \times & \textcircled{} & \textcircled{} \\ \hline \textcircled{} & \times & \times \\ \hline \end{array} \\ s_5 = S_4(S_{平局}) \end{aligned} \\ \\ \begin{aligned} \def \arraystretch{1.6} \begin{array}{| c | c | c | } \hline \textcircled{} & \textcircled{} & \\ \hline \times & \textcircled{} & \times \\ \hline \textcircled{} & \times & \times \\ \hline \end{array} \\ s_4 = S_3(S_{过程}) \end{aligned} \qquad \xrightarrow[]{a_4 = A_3} \qquad \begin{aligned} \def \arraystretch{1.6} \begin{array}{| c | c | c | } \hline \textcircled{} & \textcircled{} & \textcircled{} \\ \hline \times & \textcircled{} & \times \\ \hline \textcircled{} & \times & \times \\ \hline \end{array} \\ s_5 = S_5(S_{获胜}) \end{aligned} \end{aligned} ◯×◯◯××s3=S1(S过程)a3=A7◯×◯◯◯×××s4=S2(S过程)a4=A6◯×◯◯◯××◯×s5=S4(S平局)◯×◯◯◯×××s4=S3(S过程)a4=A3◯×◯◯◯×◯××s5=S5(S获胜)
如果我们在决策 a4a_4a4 时能到 S5S_5S5 这个获胜状态,就能获得激励 +200+200+200。然而实际上,我们在决策 a3a_3a3 时,可以选择 a3=A3a_3 = A_3a3=A3 这一更好的决策,那么我们在 t=4t = 4t=4 时刻就能获胜,而不用等到 t=5t = 5t=5 时刻。简单来说,我们是认为 ttt 越小,奖励越高的。
◯◯×◯××s3=S1(S过程)→a3=A3◯◯◯×◯××s4=S6(S获胜)\begin{aligned} \def \arraystretch{1.6} \begin{array}{| c | c | c | } \hline \textcircled{} & \textcircled{} & \\ \hline \times & \textcircled{} & \\ \hline & \times & \times \\ \hline \end{array} \\ s_3 = S_1(S_{过程}) \end{aligned} \qquad \xrightarrow[]{a_3 = A_3} \qquad \begin{aligned} \begin{aligned} \def \arraystretch{1.6} \begin{array}{| c | c | c | } \hline \textcircled{} & \textcircled{} & \textcircled{} \\ \hline \times & \textcircled{} & \\ \hline & \times & \times \\ \hline \end{array} \\ s_4 = S_6(S_{获胜}) \end{aligned} \end{aligned} ◯×◯◯××s3=S1(S过程)a3=A3◯×◯◯×◯×s4=S6(S获胜)
因此,我们需要让智能体了解到我们的期望(ttt 越小,奖励越高)。比如,我们可以设置衰减因子 γ=0.8\gamma = 0.8γ=0.8,那么通过 a3=A7、a4=A3a_3 = A_7、a_4 = A_3a3=A7、a4=A3 的方式获胜,能得到的奖励只有 0.84×200=81.920.8^4 \times 200 = 81.920.84×200=81.92,而通过 a3=A3a_3 = A_3a3=A3 的方式获胜能得到的奖励有 0.83×200=102.40.8^3 \times 200 = 102.40.83×200=102.4通过衰减因子,智能体为了最大化奖励,就会学习怎样以最小的回合数获胜。
- 就拿我们之前讲状态转移张量 PPP 所举的 例子 来说,我们在 s3=S1s_3 = S_1s3=S1 状态选择了 动作 a3=A7a_3 = A_7a3=A7,后续有可能的状态转移如下:
二、智能体决策系统
在上一章节中,我们总结了:MDP 实际上是一个关于环境规则和个人期望的一个定义。
简单来说,MDP 给出了 游戏 / 仿真实验 的一个环境,并包含了我们对于这个 游戏 / 仿真实验 最终结果的一个期望。
还是下面这张图,MDP 相当于提供了环境的一个接口,它告诉我们环境是什么(状态 sts_tst)、有哪些动作可以执行(AAA)、执行某个动作后环境会发生何种改变(PPP)、环境的改变会产生何种奖励(RRR)……
而我们强化学习的主要任务,则是训练一个智能体,让它来为我们做决策。
这个决策可以被抽象为一个函数,
{确定性策略at=π(st)不确定性策略at=a,其中π(a∣st)=p(a∣st),表示在状态st下按概率随机选取某个动作\begin{cases} 确定性策略 &a_t = \pi(s_t) \\ \\ 不确定性策略 & a_t = a, \quad 其中 \; \pi(a | s_t) = p(a | s_t),表示在状态s_t下按概率随机选取某个动作 \\ \end{cases} ⎩⎨⎧确定性策略不确定性策略at=π(st)at=a,其中π(a∣st)=p(a∣st),表示在状态st下按概率随机选取某个动作
强化学习的目标,是为了让智能体决策出的动作,引发环境状态改变后的最终状态,能达到我们的期望。
我们在马尔可夫决策过程中定义了即时奖励函数 R(s,s′)R(s, s')R(s,s′),在这里我们再定义一个状态价值函数 Vπ(s)V_{\pi}(s)Vπ(s) ,它表示我们在状态 sss 下一直按照策略 π\piπ 决策 直到终止状态所能获得的价值。
- 如果是确定性策略,并且每次执行一个动作进入的下一个状态是确定的,那么
Vπ(s)=∑t=0TγtRπ(st,st+1)V_{\pi}(s) = \sum_{t = 0}^{T} \gamma^t R_{\pi}(s_t, s_{t+1}) Vπ(s)=t=0∑TγtRπ(st,st+1) - 如果是确定性策略,并且每次执行一个动作进入的下一个状态是按概率随机的(比如下井字棋对手可能随机下子),那么
Vπ(s)=∑s′pπ(s,s′)(Rπ(s,s′)+γVπ(s′))V_{\pi}(s) = \sum_{s'} p_{\pi}(s, s')(R_{\pi}(s, s') + \gamma V_{\pi}(s')) Vπ(s)=s′∑pπ(s,s′)(Rπ(s,s′)+γVπ(s′))
简单理解就是,计算每一种可能的下一状态的奖励和后续价值,按概率加权求和(数学期望)。 - 【一般情况】如果是非确定性策略,并且每次执行一个动作进入的下一个状态是按概率随机的,那么
Vπ(s)=∑aπ(a∣s)∑s′pa(s,s′)(Ra(s,s′)+γVπ(s′))V_{\pi}(s) = \sum_{a} \pi(a | s) \sum_{s'} p_{a}(s, s')(R_{a}(s, s') + \gamma V_{\pi}(s')) Vπ(s)=a∑π(a∣s)s′∑pa(s,s′)(Ra(s,s′)+γVπ(s′))
简单理解就是,计算每一种可能决策出的动作的各个后续状态的奖励和后续价值的加权求和,得到这个动作在当前状态下的价值(也就是下面要讲的动作价值 Qπ(s,a)Q_{\pi}(s, a)Qπ(s,a)),再按决策出动作的概率加权求和(数学期望)。
特殊的情况,当到达终止状态,已经无法继续决策,不会有后续状态,也就没有价值,即终止状态的状态价值函数为0:
Vπ(send)=0V_{\pi}(s_{\text{end}}) = 0 Vπ(send)=0
和状态价值函数类似,我们可以定义动作价值函数,以【一般情况】来说,有:
Qπ(s,a)=∑s′pa(s,s′)(Ra(s,s′)+γVπ(s′))Vπ(s)=∑aπ(a∣s)Qπ(s,a)\begin{aligned} & Q_{\pi}(s, a) = \sum_{s'} p_{a}(s, s')(R_{a}(s, s') + \gamma V_{\pi}(s')) \\ \\ & V_{\pi}(s) = \sum_{a} \pi(a | s) \; Q_{\pi}(s, a) \end{aligned} Qπ(s,a)=s′∑pa(s,s′)(Ra(s,s′)+γVπ(s′))Vπ(s)=a∑π(a∣s)Qπ(s,a)
这就是贝尔曼方程,也可以写成贝尔曼期望方程的形式:
Qπ(s,a)=E(Ra(s,s′)+γVπ(s′)∣st=s,at=a)Vπ(s)=E(Ra(s,s′)+γVπ(s′)∣st=s)\begin{aligned} & Q_{\pi}(s, a) = E( \; R_{a}(s, s') + \gamma V_{\pi}(s') \; | \; s_t = s, \; a_t = a \; ) \\ \\ & V_{\pi}(s) = E( \; R_{a}(s, s') + \gamma V_{\pi}(s') \; | \; s_t = s \;) \end{aligned} Qπ(s,a)=E(Ra(s,s′)+γVπ(s′)∣st=s,at=a)Vπ(s)=E(Ra(s,s′)+γVπ(s′)∣st=s)
那么如何判断策略的优劣呢,对于两个不同的策略 π\piπ 和 π′\pi'π′,对任意状态 sss 都有 Vπ(s)≥Vπ′(s)V_{\pi}(s) \ge V_{\pi'}(s)Vπ(s)≥Vπ′(s),则称策略 π\piπ 优于策略 π′\pi'π′ .
最优动作价值函数和最优状态价值函数定义如下:
Q∗(s,a)=maxQπ(s,a)V∗(s)=maxVπ(s)\begin{aligned} & Q^*(s, a) = \max Q_{\pi}(s, a) \\ & V^*(s) = \max V_{\pi}(s) \\ \end{aligned} Q∗(s,a)=maxQπ(s,a)V∗(s)=maxVπ(s)
我们可以通过寻找最优动作价值函数找到最优策略:
π∗(s)=arg maxaQ∗(s,a)\pi^*(s) = \argmax_{a} Q^*(s, a) π∗(s)=aargmaxQ∗(s,a)
Q∗(s,a)Q^*(s, a)Q∗(s,a) 和 V∗(s)V^*(s)V∗(s) 都满足贝尔曼最优性方程。
对于最优状态价值函数,有:
V∗(s)=max∑s′pa(s,s′)(Ra(s,s′)+γV∗(s′))V^*(s) = \max \sum_{s'} p_{a}(s, s')(R_{a}(s, s') + \gamma V^*(s')) V∗(s)=maxs′∑pa(s,s′)(Ra(s,s′)+γV∗(s′))
简单理解就是,要保证一个策略最优,需要保证当前状态执行的动作带来的奖励和下一状态的最优价值函数值之和最优。
对于最优动作价值函数,有
Q∗(s,a)=∑s′pa(s,s′)(Ra(s,s′)+γQ∗(s′,a′))Q^*(s, a) = \sum_{s'} p_{a}(s, s')(R_{a}(s, s') + \gamma Q^*(s', a')) \\ Q∗(s,a)=s′∑pa(s,s′)(Ra(s,s′)+γQ∗(s′,a′))
简单理解就是,要保证一个策略最优,需要保证本次执行完动作 aaa 后,下一状态 s′s's′ 执行的动作 a′a'a′ 是最优的。
深度强化学习入门:马尔可夫决策过程(井字棋案例理解)相关推荐
- 【深度强化学习】马尔可夫决策过程(Markov Decision Process, MDP)
1. Markov Process 我们一步一步来讲解 Markov Decision Process.按顺序,从 Markov Process 到 Markov Reward Process,再到 ...
- 【githubshare】深度学习蘑菇书,覆盖了强化学习、马尔可夫决策过程、策略梯度、模仿学习
GitHub 上的深度学习技术书籍:<蘑菇书 EasyRL>,覆盖了强化学习.马尔可夫决策过程.策略梯度.模仿学习等多个知识点. GitHub:github.com/datawhalech ...
- 什么是强化学习(马尔可夫决策过程)
文章目录 什么是强化学习(马尔可夫决策过程) 1. 强化学习(概述) 2. 马尔可夫决策过程 2.1 马尔可夫假设 2.2 马尔可夫决策过程 2.3 状态值函数(state-value functio ...
- 【强化学习】《动手学强化学习》马尔可夫决策过程
[强化学习]<动手学强化学习>马尔可夫决策过程 一.随机过程.马尔可夫过程.马尔可夫奖励过程 二.马尔可夫决策过程 三.蒙特卡洛方法 四.最优策略与贝尔曼最优方程 一.随机过程.马尔可夫过 ...
- 强化学习之——马尔可夫决策过程原理
强化学习之--马尔可夫决策过程原理 1.1 MDP:策略与环境模型 我们以蛇棋为模型引入--蛇棋的关键问题在于:哪些因素决定了蛇棋最终获得分数的多少? 两个因素 选择什么样的手法投掷(也就是投3以内的 ...
- mdp框架_强化学习-MDP(马尔可夫决策过程)算法原理
1. 前言 前面的强化学习基础知识介绍了强化学习中的一些基本元素和整体概念.今天讲解强化学习里面最最基础的MDP(马尔可夫决策过程). 2. MDP定义 MDP是当前强化学习理论推导的基石,通过这套框 ...
- 强化学习笔记-马尔可夫决策过程
前言 本文首先介绍了三个基本概念:马尔可夫性.马尔可夫过程和马尔可夫决策过程.接着引入贝尔曼方程,给出了值函数.状态行为函数.最优值函数.最优状态行为函数的推导公式以及它们之间的关系. 解释马尔可夫性 ...
- 强化学习之马尔可夫决策过程—机器学习公开课第十五讲
本篇笔记对应的是公开课的第十五讲,主要内容包括 马尔可夫决策过程MDP(Markov Decision Process).价值函数(Value Function).价值迭代(Value Iterati ...
- 强化学习_02_DataWhale马尔可夫决策过程习题
习题 1-1 为什么在马尔可夫奖励过程中需要有折扣因子(discount factor)? 马尔可夫过程是带环的,需要避免无穷的奖励 我们没办法完美模拟环境,对未来的预估不一定准确.折扣因子可以将这个 ...
最新文章
- canvas是什么牌子的包_Jack Spade DIPPED INDUSTRIAL CANVAS UTILITY BRIEF 男款帆布手提包
- Windows Phone开发(25):启动器与选择器之WebBrowserTask
- 机器学习模型 非线性模型_调试机器学习模型的终极指南
- 应付账款账龄分析模板_企业财务报表分析论文应如何着手?
- JavaScript-严格检查模式
- 【有容云案例系列】基于Jenkins和Kubernetes的CI工作流
- easyui datagrid 列隐藏和显示
- 1054. 求平均值
- (转)Ubuntu10.04编译FFmpeg
- Linux系统中 安装Vmware Toolst工具
- java web 前端学习路线
- JavaScript return的作用
- ../bin/testCurveFitting 出现的错误以及解决办法
- python用嵌套if结构开发一个输入(input)快递价格的计算器
- Java操作Oracle数据库——ARRAY TABLE类型批量数据处理区别比较
- python3网络爬虫(堆糖网)
- JavaScript(基础知识)
- js 格式化prettier配置_使用Prettier eslint pre-commit进行js代码自动检测,格式化统一风格...
- 熔断机制什么意思_指数熔断机制是什么意思
- 四个角不是直角的四边形_四边形的特点是有四条直的边和四个直角对吗
热门文章
- 唱情歌 设计日记(一):电脑,手机,电视机,和家
- 您要允许以下程序对计算机进行更改吗,win10提示“您要允许以下程序对此计算机进行更改吗”如何解决...
- c++ 读取内存数据 基址_手机相册里面照片多太占内存,但又不想删掉,该怎么办?...
- Android应用:横竖屏切换总结
- Android之仿淘宝商品详情浏览效果
- CN_奇偶校验_奇校验码和偶校验码的概念和实例
- 没希望的外贸客户被我拉了回来,再一次下单!
- 书画家点赞!基于飞桨绘制中国水墨山水画
- Python列表练习题
- turtlebot3 多机器人协作,机器人跟随