fix
This commit is contained in:
@@ -47,16 +47,8 @@
|
||||
- 动作(Action)
|
||||
|
||||
- 一个状态可以做出的选择。更确切地说,动作可以定义为一个函数。当接收到状态
|
||||
$$
|
||||
s$$作为输入时,
|
||||
$$
|
||||
|
||||
Actions(s)
|
||||
$$
|
||||
将返回可在状态
|
||||
$$
|
||||
|
||||
s$$ 中执行的一组操作作为输出。
|
||||
|
||||
$s$作为输入时,$Actions(s)$将返回可在状态$s$ 中执行的一组操作作为输出。
|
||||
- 例如,在一个数字华容道中,给定状态的操作是您可以在当前配置中滑动方块的方式。
|
||||
|
||||

|
||||
@@ -65,45 +57,15 @@
|
||||
|
||||
- 对在任何状态下执行任何适用操作所产生的状态的描述。
|
||||
- 更确切地说,过渡模型可以定义为一个函数。
|
||||
- 在接收到状态
|
||||
$$
|
||||
s$$和动作
|
||||
$$
|
||||
|
||||
a
|
||||
$$
|
||||
作为输入时,
|
||||
$$
|
||||
|
||||
Results(s,a)
|
||||
$$
|
||||
返回在状态
|
||||
$$
|
||||
|
||||
s
|
||||
$$
|
||||
中执行动作
|
||||
$$
|
||||
|
||||
a$$ 所产生的状态。
|
||||
- 例如,给定数字华容道的特定配置(状态
|
||||
$$
|
||||
s$$),在任何方向上移动正方形(动作
|
||||
$$
|
||||
|
||||
a$$)将导致谜题的新配置(新状态)。
|
||||
- 在接收到状态$s$和动作$a$作为输入时,$Results(s,a)$返回在状态$s$中执行动作$a$ 所产生的状态。
|
||||
- 例如,给定数字华容道的特定配置(状态$s$),在任何方向上移动正方形(动作$a$)将导致谜题的新配置(新状态)。
|
||||
|
||||

|
||||
|
||||
- 状态空间(State Space)
|
||||
|
||||
- 通过一系列的操作目标从初始状态可达到的所有状态的集合。
|
||||
- 例如,在一个数字华容道谜题中,状态空间由所有
|
||||
$$
|
||||
\frac{16!}{2}
|
||||
$$
|
||||
|
||||
种配置,可以从任何初始状态达到。状态空间可以可视化为有向图,其中状态表示为节点,动作表示为节点之间的箭头。
|
||||
- 例如,在一个数字华容道谜题中,状态空间由所有$\frac{16!}{2}$种配置,可以从任何初始状态达到。状态空间可以可视化为有向图,其中状态表示为节点,动作表示为节点之间的箭头。
|
||||
|
||||

|
||||
|
||||
@@ -130,11 +92,11 @@
|
||||
- 从初始状态到该节点的路径成本——path cost
|
||||
- 节点包含的信息使它们对于搜索算法非常有用。
|
||||
|
||||
它们包含一个状态,可以使用目标测试来检查该状态是否为最终状态。
|
||||
它们包含一个状态,可以使用目标测试来检查该状态是否为最终状态。
|
||||
|
||||
如果是,则可以将节点的路径成本与其他节点的路径代价进行比较,从而可以选择最佳解决方案。
|
||||
如果是,则可以将节点的路径成本与其他节点的路径代价进行比较,从而可以选择最佳解决方案。
|
||||
|
||||
一旦选择了节点,通过存储父节点和从父节点到当前节点的动作,就可以追溯从初始状态到该节点的每一步,而这一系列动作就是解决方案。
|
||||
一旦选择了节点,通过存储父节点和从父节点到当前节点的动作,就可以追溯从初始状态到该节点的每一步,而这一系列动作就是解决方案。
|
||||
|
||||
- 然而,节点只是一个数据结构——它们不搜索,而是保存信息。为了实际搜索,我们使用了边域(frontier),即“管理”节点的机制。边域首先包含一个初始状态和一组空的已探索项目(探索集),然后重复以下操作,直到找到解决方案:
|
||||
|
||||
@@ -158,9 +120,9 @@
|
||||
|
||||
边域从节点 A 初始化开始
|
||||
|
||||
1. 取出边域中的节点 A,展开节点 A,将节点 B 添加到边域。
|
||||
2. 取出节点 B,展开,添加......
|
||||
3. 到达目标节点,停止,返回解决方案
|
||||
a. 取出边域中的节点 A,展开节点 A,将节点 B 添加到边域。
|
||||
b. 取出节点 B,展开,添加......
|
||||
c. 到达目标节点,停止,返回解决方案
|
||||
|
||||

|
||||
|
||||
@@ -191,7 +153,7 @@
|
||||
|
||||

|
||||
|
||||
- 代码实现
|
||||
- 代码实现
|
||||
|
||||
```python
|
||||
def remove(self):
|
||||
@@ -223,7 +185,7 @@ def remove(self):
|
||||
|
||||

|
||||
|
||||
- 代码实现
|
||||
- 代码实现
|
||||
|
||||
```python
|
||||
def remove(self):
|
||||
@@ -240,37 +202,25 @@ def remove(self):
|
||||
- 广度优先和深度优先都是不知情的搜索算法。也就是说,这些算法没有利用他们没有通过自己的探索获得的关于问题的任何知识。然而,大多数情况下,关于这个问题的一些知识实际上是可用的。例如,当人类进入一个路口时,人类可以看到哪条路沿着解决方案的大致方向前进,哪条路没有。人工智能也可以这样做。一种考虑额外知识以试图提高性能的算法被称为知情搜索算法。
|
||||
- 贪婪最佳优先搜索(Greedy Best-First Search)
|
||||
|
||||
- 贪婪最佳优先搜索扩展最接近目标的节点,如启发式函数
|
||||
$$
|
||||
h(n)
|
||||
$$
|
||||
|
||||
所确定的。顾名思义,该函数估计下一个节点离目标有多近,但可能会出错。贪婪最佳优先算法的效率取决于启发式函数的好坏。例如,在迷宫中,算法可以使用启发式函数,该函数依赖于可能节点和迷宫末端之间的曼哈顿距离。曼哈顿距离忽略了墙壁,并计算了从一个位置到目标位置需要向上、向下或向两侧走多少步。这是一个简单的估计,可以基于当前位置和目标位置的
|
||||
$$
|
||||
(x,y)
|
||||
$$
|
||||
|
||||
坐标导出。
|
||||
- 贪婪最佳优先搜索扩展最接近目标的节点,如启发式函数$h(n)$所确定的。顾名思义,该函数估计下一个节点离目标有多近,但可能会出错。贪婪最佳优先算法的效率取决于启发式函数的好坏。例如,在迷宫中,算法可以使用启发式函数,该函数依赖于可能节点和迷宫末端之间的曼哈顿距离。曼哈顿距离忽略了墙壁,并计算了从一个位置到目标位置需要向上、向下或向两侧走多少步。这是一个简单的估计,可以基于当前位置和目标位置的$(x,y)$坐标导出。
|
||||
|
||||

|
||||
|
||||
- 然而,重要的是要强调,与任何启发式算法一样,它可能会出错,并导致算法走上比其他情况下更慢的道路。不知情的搜索算法有可能更快地提供一个更好的解决方案,但它比知情算法更不可能这样。
|
||||
- 然而,重要的是要强调,与任何启发式算法一样,它可能会出错,并导致算法走上比其他情况下更慢的道路。不知情的搜索算法有可能更快地提供一个更好的解决方案,但它比知情算法更不可能这样。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
- $$
|
||||
A^*$$搜索
|
||||
|
||||
- 作为贪婪最佳优先算法的一种发展,$$A^*$$搜索不仅考虑了从当前位置到目标的估计成本$$h(n)$$,还考虑了直到当前位置为止累积的成本$$g(n)$$。通过组合这两个值,该算法可以更准确地确定解决方案的成本并在旅途中优化其选择。该算法跟踪(到目前为止的路径成本+到目标的估计成本,$$g(n)+h(n)$$),一旦它超过了之前某个选项的估计成本,该算法将放弃当前路径并返回到之前的选项,从而防止自己沿着$$h(n)$$错误地标记为最佳的却长而低效的路径前进。
|
||||
|
||||
- 然而,由于这种算法也依赖于启发式,所以它依赖它所使用的启发式。在某些情况下,它可能比贪婪的最佳第一搜索甚至不知情的算法效率更低。对于最佳的$$A^*$$搜索,启发式函数$$h(n)$$应该:
|
||||
|
||||
- $A^*$搜索
|
||||
|
||||
- 作为贪婪最佳优先算法的一种发展,$A^*$搜索不仅考虑了从当前位置到目标的估计成本$h(n)$,还考虑了直到当前位置为止累积的成本$g(n)$。通过组合这两个值,该算法可以更准确地确定解决方案的成本并在旅途中优化其选择。该算法跟踪(到目前为止的路径成本+到目标的估计成本,$g(n)+h(n)$),一旦它超过了之前某个选项的估计成本,该算法将放弃当前路径并返回到之前的选项,从而防止自己沿着$h(n)$错误地标记为最佳的却长而低效的路径前进。
|
||||
|
||||
- 然而,由于这种算法也依赖于启发式,所以它依赖它所使用的启发式。在某些情况下,它可能比贪婪的最佳第一搜索甚至不知情的算法效率更低。对于最佳的$A^*$搜索,启发式函数$h(n)$应该:
|
||||
|
||||
- 可接受,从未高估真实成本。
|
||||
|
||||
- 一致性,这意味着从新节点到目标的估计路径成本加上从先前节点转换到该新节点的成本应该大于或等于先前节点到目标的估计路径成本。用方程的形式表示,$$h(n)$$是一致的,如果对于每个节点$$n$$和后续节点$$n'$$,从$$n$$到$$n'$$的步长为$$c$$,满足$$h(n)≤h(n')+c$$。
|
||||
$$
|
||||
|
||||
- 一致性,这意味着从新节点到目标的估计路径成本加上从先前节点转换到该新节点的成本应该大于或等于先前节点到目标的估计路径成本。用方程的形式表示,$h(n)$是一致的,如果对于每个节点n$和后续节点n'$,从n$到$n'$的步长为c$,满足$h(n)≤h(n')+c$。
|
||||
|
||||

|
||||
|
||||
@@ -282,72 +232,45 @@ def remove(self):
|
||||
|
||||
- 极大极小算法(Minimax)
|
||||
|
||||
- 作为对抗性搜索中的一种算法,Minimax 将获胜条件表示为
|
||||
$$
|
||||
(-1)
|
||||
$$
|
||||
- 作为对抗性搜索中的一种算法,Minimax 将获胜条件表示为$(-1)$表示为一方,$(+1)$表示为另一方。进一步的行动将受到这些条件的驱动,最小化的一方试图获得最低分数,而最大化的一方则试图获得最高分数。
|
||||
|
||||
<img src="static/FYu3bQwCZofBgsxKDJiciTR7nzc.png" style="zoom:150%;" />
|
||||
|
||||
- 井字棋 AI 为例
|
||||
|
||||
- $s_0$: 初始状态(在我们的情况下,是一个空的3X3棋盘)
|
||||
|
||||

|
||||
|
||||
表示为一方,
|
||||
$$
|
||||
(+1)
|
||||
$$
|
||||
|
||||
表示为另一方。进一步的行动将受到这些条件的驱动,最小化的一方试图获得最低分数,而最大化的一方则试图获得最高分数。
|
||||
|
||||

|
||||
|
||||
- 井字棋 AI 为例
|
||||
|
||||
- $$
|
||||
s_0$$: 初始状态(在我们的情况下,是一个空的3X3棋盘)
|
||||
$$
|
||||
|
||||

|
||||
|
||||
```
|
||||
- $$Players(s)$$: 一个函数,在给定状态$$s$$的情况下,返回轮到哪个玩家(X或O)。
|
||||
```
|
||||
|
||||

|
||||
|
||||
```
|
||||
- $$Actions(s)$$: 一个函数,在给定状态$$s$$的情况下,返回该状态下的所有合法动作(棋盘上哪些位置是空的)。
|
||||
```
|
||||
|
||||

|
||||
|
||||
```
|
||||
- $$Result(s, a)$$: 一个函数,在给定状态$$s$$和操作$$a$$的情况下,返回一个新状态。这是在状态$$s$$上执行动作$$a$$(在游戏中移动)所产生的棋盘。
|
||||
```
|
||||
|
||||

|
||||
|
||||
```
|
||||
- $$Terminal(s)$$: 一个函数,在给定状态$$s$$的情况下,检查这是否是游戏的最后一步,即是否有人赢了或打成平手。如果游戏已结束,则返回True,否则返回False。
|
||||
```
|
||||
|
||||

|
||||
|
||||
```
|
||||
- $$Utility(s)$$: 一个函数,在给定终端状态s的情况下,返回状态的效用值:$$-1、0或1$$。
|
||||
```
|
||||
- $$Players(s)$$: 一个函数,在给定状态$$s$$的情况下,返回轮到哪个玩家(X或O)。
|
||||
|
||||

|
||||
|
||||
- $Actions(s)$: 一个函数,在给定状态$$s$$的情况下,返回该状态下的所有合法动作(棋盘上哪些位置是空的)。
|
||||
|
||||

|
||||
|
||||
- $Result(s, a)$: 一个函数,在给定状态$$s$$和操作$$a$$的情况下,返回一个新状态。这是在状态$$s$$上执行动作$$a$$(在游戏中移动)所产生的棋盘。
|
||||
|
||||

|
||||
|
||||
- $Terminal(s)$: 一个函数,在给定状态$$s$$的情况下,检查这是否是游戏的最后一步,即是否有人赢了或打成平手。如果游戏已结束,则返回True,否则返回False。
|
||||
|
||||

|
||||
|
||||
- $Utility(s)$: 一个函数,在给定终端状态s的情况下,返回状态的效用值:$$-1、0或1$$。
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
- 算法的工作原理:
|
||||
|
||||
- 该算法递归地模拟从当前状态开始直到达到终端状态为止可能发生的所有游戏状态。每个终端状态的值为
|
||||
$$
|
||||
(-1)、0或(+1)
|
||||
$$
|
||||
|
||||
。
|
||||
- 该算法递归地模拟从当前状态开始直到达到终端状态为止可能发生的所有游戏状态。每个终端状态的值为$(-1)$、$0$或$(+1)$。
|
||||
|
||||

|
||||
|
||||
```
|
||||
- 根据轮到谁的状态,算法可以知道当前玩家在最佳游戏时是否会选择导致状态值更低或更高的动作。
|
||||
```
|
||||
- 根据轮到谁的状态,算法可以知道当前玩家在最佳游戏时是否会选择导致状态值更低或更高的动作。
|
||||
|
||||
通过这种方式,在最小化和最大化之间交替,算法为每个可能的动作产生的状态创建值。举一个更具体的例子,我们可以想象,最大化的玩家在每一个回合都会问:“如果我采取这个行动,就会产生一个新的状态。如果最小化的玩家发挥得最好,那么该玩家可以采取什么行动来达到最低值?”
|
||||
|
||||
@@ -361,102 +284,30 @@ def remove(self):
|
||||
|
||||
- 具体算法:
|
||||
|
||||
- 给定状态 $$s$$
|
||||
- 给定状态 $s$
|
||||
|
||||
- 最大化玩家在
|
||||
$$
|
||||
Actions(s)
|
||||
$$
|
||||
- 最大化玩家在$$Actions(s)$$中选择动作$$a$$,该动作产生$$Min-value(Result(s,a))$$ 的最高值。
|
||||
- 最小化玩家在$$Actions(s)$$中选择动作$$a$$,该动作产生$$Max-value(Result(s,a))$$ 的最小值。
|
||||
|
||||
中选择动作
|
||||
$$
|
||||
a$$,该动作产生
|
||||
$$
|
||||
|
||||
Min-value(Result(s,a))$$ 的最高值。
|
||||
- 最小化玩家在
|
||||
$$
|
||||
Actions(s)
|
||||
$$
|
||||
|
||||
中选择动作
|
||||
$$
|
||||
a$$,该动作产生
|
||||
$$
|
||||
|
||||
Max-value(Result(s,a))$$ 的最小值。
|
||||
- Function Max-Value(state):
|
||||
|
||||
- $$
|
||||
v=-\infty
|
||||
$$
|
||||
- if
|
||||
$$
|
||||
Terminal(state)
|
||||
$$
|
||||
|
||||
:
|
||||
|
||||
- return
|
||||
$$
|
||||
Utility(state)
|
||||
$$
|
||||
- for
|
||||
$$
|
||||
action
|
||||
$$
|
||||
|
||||
in
|
||||
$$
|
||||
Actions(state)
|
||||
$$
|
||||
|
||||
:
|
||||
|
||||
- $$
|
||||
v = Max(v, Min-Value(Result(state, action)))
|
||||
$$
|
||||
- $$v=-\infty$$
|
||||
- if $$Terminal(state)$$:
|
||||
- return $$Utility(state)$$
|
||||
- for $$action$$ in $$Actions(state)$$:
|
||||
- $$v = Max(v, Min-Value(Result(state, action)))$$
|
||||
- return $$v$$
|
||||
- Function Min-Value(state):
|
||||
|
||||
- $$
|
||||
v=\infty
|
||||
$$
|
||||
- if
|
||||
$$
|
||||
Terminal(state)
|
||||
$$
|
||||
|
||||
:
|
||||
|
||||
- return
|
||||
$$
|
||||
Utility(state)
|
||||
$$
|
||||
- for
|
||||
$$
|
||||
action
|
||||
$$
|
||||
|
||||
in
|
||||
$$
|
||||
Actions(state)
|
||||
$$
|
||||
|
||||
:
|
||||
|
||||
- $$
|
||||
v = Min(v, Max-Value(Result(state, action)))
|
||||
$$
|
||||
- $$v=\infty$$
|
||||
- if $$Terminal(state)$$:
|
||||
- return $$Utility(state)$$
|
||||
- for $$action$$ in $$Actions(state)$$:
|
||||
- $$v = Min(v, Max-Value(Result(state, action)))$$
|
||||
- return $$v$$
|
||||
|
||||
不会理解递归?也许你需要看看这个:[阶段二:递归操作](https://hdu-cs-lecture-notes.feishu.cn/wiki/wikcnwIRBW8rXspHQvigmYXZSWd)
|
||||
|
||||
- $$
|
||||
\alpha$$-$$\beta$$剪枝(Alpha-Beta Pruning)
|
||||
|
||||
- $$\alpha$$-$$\beta$$剪枝(Alpha-Beta Pruning)
|
||||
- 作为一种优化Minimax的方法,Alpha-Beta剪枝跳过了一些明显不利的递归计算。在确定了一个动作的价值后,如果有初步证据表明接下来的动作可以让对手获得比已经确定的动作更好的分数,那么就没有必要进一步调查这个动作,因为它肯定比之前确定的动作不利。
|
||||
|
||||
- 这一点最容易用一个例子来说明:最大化的玩家知道,在下一步,最小化的玩家将试图获得最低分数。假设最大化玩家有三个可能的动作,第一个动作的值为4。然后玩家开始为下一个动作生成值。要做到这一点,如果当前玩家做出这个动作,玩家会生成最小化者动作的值,并且知道最小化者会选择最低的一个。然而,在完成最小化器所有可能动作的计算之前,玩家会看到其中一个选项的值为3。这意味着没有理由继续探索最小化玩家的其他可能行动。尚未赋值的动作的值无关紧要,无论是10还是(-10)。如果该值为10,则最小化器将选择最低选项3,该选项已经比预先设定的4差。如果尚未估价的行动结果是(-10),那么最小化者将选择(-10)这一选项,这对最大化者来说更加不利。因此,在这一点上为最小化者计算额外的可能动作与最大化者无关,因为最大化玩家已经有了一个明确的更好的选择,其值为4。
|
||||
$$
|
||||
|
||||
@@ -464,15 +315,5 @@ def remove(self):
|
||||
|
||||
- 深度限制的极大极小算法(Depth-Limited Minimax)
|
||||
|
||||
- 总共有
|
||||
$$
|
||||
255168
|
||||
$$
|
||||
|
||||
个可能的井字棋游戏,以及有
|
||||
$$
|
||||
10^{29000}
|
||||
$$
|
||||
|
||||
个可能的国际象棋中游戏。到目前为止,最小最大算法需要生成从某个点到<strong>终端条件</strong>的所有假设游戏状态。虽然计算所有的井字棋游戏状态对现代计算机来说并不是一个挑战,但目前用来计算国际象棋是不可能的。
|
||||
- 深度限制的 Minimax 算法在停止之前只考虑预先定义的移动次数,而从未达到终端状态。然而,这不允许获得每个动作的精确值,因为假设的游戏还没有结束。为了解决这个问题,深度限制 Minimax 依赖于一个评估函数,该函数从给定状态估计游戏的预期效用,或者换句话说,为状态赋值。例如,在国际象棋游戏中,效用函数会将棋盘的当前配置作为输入,尝试评估其预期效用(基于每个玩家拥有的棋子及其在棋盘上的位置),然后返回一个正值或负值,表示棋盘对一个玩家对另一个玩家的有利程度。这些值可以用来决定正确的操作,并且评估函数越好,依赖它的 Minimax 算法就越好。
|
||||
- 总共有$$255168$$个可能的井字棋游戏,以及有$$10^{29000}$$个可能的国际象棋中游戏。到目前为止,最小最大算法需要生成从某个点到<strong>终端条件</strong>的所有假设游戏状态。虽然计算所有的井字棋游戏状态对现代计算机来说并不是一个挑战,但目前用来计算国际象棋是不可能的。
|
||||
- 深度限制的 Minimax 算法在停止之前只考虑预先定义的移动次数,而从未达到终端状态。然而,这不允许获得每个动作的精确值,因为假设的游戏还没有结束。为了解决这个问题,深度限制 Minimax 依赖于一个评估函数,该函数从给定状态估计游戏的预期效用,或者换句话说,为状态赋值。例如,在国际象棋游戏中,效用函数会将棋盘的当前配置作为输入,尝试评估其预期效用(基于每个玩家拥有的棋子及其在棋盘上的位置),然后返回一个正值或负值,表示棋盘对一个玩家对另一个玩家的有利程度。这些值可以用来决定正确的操作,并且评估函数越好,依赖它的 Minimax 算法就越好。
|
||||
Reference in New Issue
Block a user