人工智能编程精粹

6第一章   Unity人工智能架构模型:

人工智能: 感知(Sense)  思考(Think)  行为(Act) 循环中

感知:AI和游戏的所有接口,比如敌人是否靠近   思考:根据感知的结果,选择行为,逃跑     行为:执行行为

决策类游戏:《吃豆人》中的有限状态机

潜行感知类游戏:《黄金眼》中如果自己的伙伴呗杀害,则能狗看见,并调整自己的状态

运动操控:《星际争霸》中的自主移动和编队移动就是  A*和群控技术。

1.1 游戏人工智能的架构模型:

战略层:  一般用于团队协作,

这是大概的架构模型,具体情况不一。如棋牌游戏,只包含战略层,因为角色不需要自己移动。如基于行为的游戏则不需要战略层,纯反应型,只需要角色自己做出决定。

 运动层:主要任务是 把上层做出的决策转化为运动。

算法: 相应的运动算法。

例如:当卫兵看到玩家时,需要先走到警报按铃处,才能按按铃【播放动画】。

 决策层:根据某准则来选择行为

算法:有限状态机,行为树,复杂的如模糊状态机【概率FSM、优先级,用模糊集合来实现】、神经网络。

例如:如果角色看不到玩家就开始巡逻,否则就攻击玩家。做出决策后交给运动层【或者和动画】执行决策。

 战略层:

 战略层:  一般用于团队协作,一组角色的总体行为,AI会影响到多个角色的行为,每个角色有自己的决策层和运动算法。如敌人具有团队协作,包围和消灭玩家。技术有限状态机,行为树,复杂的如模糊状态机【概率FSM、优先级】、神经网络。

AI架构的其他部分:物理、动画仿真、等

1.2 FPS和TPS的AI解析:

FPS: 玩家主视角  TPS:第三人称,玩家控制的游戏人物游戏中可见,强调动作感。

结构: 高层决策和低层决策组成。高层用于推理决策,选择匹配的行为,底层处理最基本的任务。如,确定到目标位置的最优路径或者播放适当的动画序列。高层发出靠近敌人,底层以最佳的方式靠近敌人【悄悄过去和疯狂的跑过去】。

1.2.1 FPS/TPS的运动层:

算法:这层涉及的主要算法是寻路

1.2.2  FPS/TPS的决策层:

决定AI的执行行为,如:播放动画,音频,移动到哪儿,何时参加战斗。

算法: 大多数FPS都用有限状态机和行为树来实现。

考虑用户评估AI时是与战斗相关的决策。则除了一般的决策层外再加一个”战斗控制器“负责做出与战斗相关的决策。

当角色开始战斗时:

战斗决策器开始:

战斗决策器根据当前环境做出决策:

支撑AI的其他功能:

 视觉【角度,烟雾】  动画

第二章 实现AI角色的自主移动  操控行为

比如:避免在遇到障碍物时不停的碰撞或者卡着滑动。

对于多个单位组成的小队,“集群”  “操控行为”,工作方式时通过产生一定大小和方向的操控力,

使角色以某种方式运动。

<1.  “操作行为”包括一组基本行为,单独的AI角色,基本操作行为包括:

把基本行为以一定的方式组合,就能够实现复杂的行为。

<2. 多个AI角色,组行为包括:

总结:无论群体有多少,计算的复杂性是有限的。

本书英文术语对照表:

2.1.1  将AI角色抽象成一个质点  Vehicle类

Vehicle:基类,可移动AI角色都派生自它。实现部分是封装了一些数据用来描述被看做质点的“交通工具”

例如:车,怪物,人

代码实现:

Vehicle.cs:    实现部分是封装了一些数据用来描述被看做质点的“交通工具”,例如:车,怪物,人

2.1.2 控制AI角色移动类—— AILocomotion.cs :Vehicle.cs

AILocomotion.cs:  控制角色的具体移动,播放动画等

2.1.3 各种操作行为的基类---Steering.cs

控制AI角色的寻找、逃避、分离、队列等操作行为  都由此派生。如:从待机改变到寻找

2.2 个体AI角色的操控行为:

例如:导弹追踪和进攻玩家。

技术实现:

导弹追踪: 只是基于速度得,这个是匀速追踪的,此处还应该考虑(2)和(3)的情况。

    public Transform player;
    public Transform missile;
    private Vector3 missileDir;    //导弹的速度向量


    public float missileRate=0.02f;//应该是个随着导弹到目标 位置的距离而改变运动速度的
    private void Update()
    {
        missileDir = player.position - missile.position;
        missileDir = missileDir.normalized;  //missileDir 此处只是当前物体到目标向量的运动方向

        Vector3 normalVec = Vector3.Cross(missileDir,missile.up);
        float vecAngle = Vector2.Angle(missileDir,missile.up);
        if (normalVec.z > 0)//顺时针
        {
            missile.transform.Rotate(Vector3.forward, -vecAngle);//顺时针转
        }
        else if (normalVec.z < 0)
        {
            missile.transform.Rotate(Vector3.forward, vecAngle );//逆时针转
        }

        missileDir *= missileRate; //missileDir此处是当前物体到目标速度向量【既有方向又有大小】
        missile.position += missileDir;
    }

2.2.1   在力的作用下靠近

指定目标位置,根据当前的速度向量,返回一个AI角色到达该目标位置的“操控力”,使AI角色自动向该位置移动。

<1.  计算AI角色在理想情况下到达目标位置的预期速度:当前位置到目标位置【向量差】。

<2.  操作向量:预期速度到AI角色当前速度【向量差】.向量是随着当前位置变化而变化。

产生靠近操控力: 

 

注意:由于是基于力和速度的,速度不会突变,因此如果一致采取靠近行为,则会使得AI从目标穿过,再返回重新接近目标,如此往返。

2.2.2 离开

AI进入一定范围时【AI的感知范围】,产生离开操控力,和接近时的预期速度的方向相反。

注意: 

Vector3.sqrMagnitude 和Vector3.Distance的比较: 

Vector3.sqrMagnitude 是指长度的平方,因为 计算向量大小的平方会比计算向量的大小要快很多,因为向量的大小由勾股定理得出,所以有开方操作,如果只是单纯的比较两个向量的大小,可以使用sqrMagnitude会快很多。而距离得比较则直接对fearDistance进行预先平方后再计算。

产生离开操控力 SteeringForFlee.cs: 

2.2.3 抵达

有时需要AI角色接近目标位置时逐渐减速,然后停止,即抵达行为。

何时开始减速:停止半径参数,进入停止半径后减小预期速度,直到减小到0.

             

SteeringForArrive.cs

2.2.4 追逐

追逐: AI直接向目标位置靠近,这样很不真实,而是应该向着未来的方向追去。这样追逐的时间才是最短的。

预测器: 假设采用线性预测器,预测间隔T时间内角色不会转向,角色经过时间T之后的未来位置可以用当前速度乘以T来确定。

让后把得到的值加到角色的当前位置上。AI用预测位置来目标,应用靠近行为。

T: 预测时间。 AI和角色之间的距离  /( AI速度+角色速度)

预期位置:

AI和角色面对面时:追逐可能提前结束,角色还没停止运动,追逐就已经结束了。

AI和逃跑角色 面对面的判定:逃避者朝向的反向和AI角色的朝向必须大约在20度范围内,才认为是面对着。

SteenringForPursuit.cs

2.2.5 逃避

逃避行为: AI 可以躲避角色。使得AI角色逃离预测位置

SteeringForEvade.cs: 

首先被追逐者从B出发,追逐者从A出发。 B激活靠近行为到D点【5s后到达】,此时关闭靠近行为,激活逃避行为。这段时间

,而追逐者是从A到C。

2.2.6 随机徘徊: 

技术实现:

 Seek 靠近法:在场景中随机放置目标位置,然后靠近,隔断时间,再继续随机目标位置。这样会使得角色突然掉头,因为目标移动到了自己的后边。不行。

随机徘徊操控行为:原理同内燃机的气缸曲轴传动相似。想想目标在圆周上不停的随机同向偏移转动。此时操控力。

    ==》      ==》   

使得目标限制在圆周上,使角色不至于突然改变路线。

利用不同的连杆长度( Wander距离 )、角色到圆心的距离(Wander半径)、目标每帧随机偏移的大小【同一目标的偏移转向一直保持不变】就可以产生不同的随机运动,巡逻的士兵,惬意吃草的牛羊。

https://blog.csdn.net/Terrell21/article/details/82430776   //参考链接

StreeringForWander.cs

2.2.7 路径跟随

路径跟随:产生一个操控力,使得AI角色可以沿着预先设置的轨迹,构成一系列路点移动。

最简单的路径跟随是把当前点设置为路点,用靠近行为产生操作力来靠近这个路点。

路径跟随分为 闭合路点和开放路点,闭合路点需要路径跟随行为和抵达行为,抵达用来停止在最后一个点

路径半径参数:当AI距离当前路点多远时,认为它已经达到路点。对路径形状有影响。

有案例工程: p33

2.2.8 避开障碍行为

效果如下图:p40 

当AI角色发现比较近的障碍物时,产生一个“排斥力”,是AI角色远离;当发现多个障碍物时,只产生躲避最近

最近的障碍物的操控力,使得AI一个接一个的躲开。

首先是发现障碍物

<1. ahead。因为速度向量就是AI的行进方向,所以产生一个新的向量Ahead,方向和速度向量一致,但是长度不同。

ahead也可以称为AI角色的视线范围。

<2. 每个障碍物都用一个几何形状来表示,这里采用包围求来标识常见中的每个障碍。

    判断aHead向量与障碍物的包围球/包围盒是否相交。这里采用一个更加简化的方法。

    再需要一个aHead2,aHead的长度是aHead的一半。Max_SEE_AHEAD*0.5

<3. 检测

比较两个向量ahead和ahead2的终点与球心的距离d,如果d<=R【球】或者d2<=R【球】,则向量在包围球内。注: ahead<球的半径。

如果和aHead和aHead2都相交,则先选择最近的那个障碍物。

但是我觉着这种检测是否相交的方式有问题,如果在刚开始时检测时,如下图情况,是障碍物但是不能判断出,其他情况下则没问题   又或者 是障碍物本身的碰撞体大小,因为模型中心点到障碍物刚好没有碰撞,但是模型自身的碰撞体还是会和障碍物发生碰撞

可以沿着模型两侧,发射两条ahead,来检测相交或者 判断和球体相交。

需要用向量和球的相交来判断检测:

案例:

射线和圆/球的相交线检测   p271//避开障碍物时,需要判断射线与球的相交性问题

判断射线L和此球是否相交:

t的变化长度是0~L,判断t是否有解。

圆心c,半径r,d是单位向量,t从0变化到L【t为交点】,L为射线长度,所要求的是交点t的值。

0t的长度:    t=a-f0

向量e: ,将e投影到d上,这个向量的长度为a,计算公式 a=e*d.

根据小三角形中:

大三角形:

==》代入==》

==》因为f=a-t

==》如果根号下值为负,t没有解,则射线和圆不相交,否则相交,向量在圆内。

==》如果,则射线的起点在圆内。

此外不同的测试则有不同的行为。

<4. 计算AI角色推离障碍物的操控力。

aHead【AI视线的终点-球心】

 【MAX_AVOID_FORCE决定力的大小

因为当AI正在受操控力远离时,也有可能发生碰撞。优化如下:

根据AI角色的当前速度调整aHead向量

代码:工程P35

说明: 对T和L形状的障碍物工作的不是很好。对于游戏要求不能任何碰撞时,则不能这样操控行为,应该采用寻路算法,事先根据障碍物计算出一条静态的可行走路线。操控行为突出群体中的个体,生动。A*如果AI太多时,效率太低

2.3 群体的操控行为

2.3.1 组行为

模仿群体的几种操控行为

邻域: 角色的可视范围 ,可视域也可根据特殊的需求来动态调整,

            图a或者图b.只有灰色半径区域内的其他角色才是邻居。

2.3.2 检测附加的AI角色

2.3.3 与群中邻居保持适当距离---分离

角色之间保持一定的距离,避免多个角色相互挤到一起。

计算分离操控力,首先搜索指定区域内的其他邻居,对每个邻居计算AI角色到该邻居的向量r【归一化】得到排斥力方向。

因为距离的大小与排斥力成反比,所以需要r【归一化后的】/|r|  ,把所有邻居的排斥力相加,得到分离行为的总操控力。

2.3.4  群中邻居朝向都一致-----队列行为

上图中黑色三角表示AI角色,灰色斜线表示它当前的运动方向。黑色线表示邻居的平均朝向。

计算: 迭代所有邻居,计算AI角色朝向向量的平均值和速度向量的平均值,平均向量减去当前朝向向量, 得到该AI角色想要的朝向,得到队列操控力。

注意: 

2.3.5  成群聚集在一起----聚集行为

聚集行为产生一个使AI角色移向邻居的质心得操控力。

实现: 迭代所有邻居求出AI角色位置得平均值,再使用靠近行为,将平均值作为目标。

红圈:表示位置平均值。

代码: 

由开始的分散,一段时间后聚集起来【聚集量和检测半径【邻居域】的大小有关】

2.4 个体与群体的操控行为组合

1. 示例: 

2. 解决方案

把每个操作力加起来,由于AI角色有最大操作力限制,所以不能简单的加起来。而因为采用 ”加权截断总和“,

”加权截断总和“会为每个操作力乘以一个权重,再加起来,将结果截断到允许的最大操控力。

缺点: 操控力冲突时,会有问题。例如:分离的操控力较大,避开的操控力较小,则AI角色撞到障碍物。

2.5 几种操控行为的编程解析:

2.5.1 模拟鸟群:

案例; P52

由于AI角色是有视域的,有可能出现一个交通工具和其他群集”隔绝“而停止不动,为了避免此情况则加入随机徘徊

人类群体: Pathfinding Project 插件中的RVO  人群中彼此推搡

2.5.2 多AI角色障碍赛

要求: 不仅像前边介绍的单个AI角色躲避障碍物【球体】行为,还要AI角色彼此避开。

案例;P54

缺点: 障碍物和障碍物之间的距离不能太近,不然障碍物1的斥力下,会弹开到障碍物2

AI的可视距离要控制好。此方法判断检测,并不是完全的不碰撞。多次测试,来调整靠得比较近障碍物的位置。

2.5.3 实现动物迁徙中的跟随领队行为

案例: P59

目的1: 接近领队,但是位置落后。不能用靠近和追逐来代替的原因是,追逐是抓住目标,靠近是占据目标的位置。

    ==》在领队的视域内,开始躲避行为   

通过组合行为实现:

<1. 找到 追随点behind

<2 . 以behind点为目标,使用 ”到达行为“ 

<3. 为了避免过于拥挤,再加上 ” 分离行为“

<4. 逃避行为,领队突然改变方向,则AI跟随者不能挡住领队的方向。

 先检测是否AI跟随者挡住了领队【即在领队的视线内】。

基于领队的档期啊速度和方向,找到前方的一个点ahead 。领队的ahead和AI跟随者距离小于某值,则认为在时限内,需离开。

2.5.4  排队行为通过狭窄通道

应用: 从宽广的路到狭窄的路时的有序进入。 例如: 一小队AI通过唯一的门进来捕捉玩家。

操控行为: 

AI角色通过判断自己前方是否有其他AI角色,来判断是否前进还是等待。计算前方AI角色的方法和碰撞避免行为中相同的方法。

<1. 计算前方的ahead,检测是否被AI角色挡住,等待

<2. 停止等待

因为操控行为都是基于力的变化,甚至力为0,AI角色速度不一定为0.

方法一: 和抵达行为一样,停止半径参数,进入停止半径后减小预期速度,直到减小到0.

方法二: 直接把速度乘以比例因子(0.2),但是在没有障碍物时,速度户迅速恢复到原来的速度。

<3. 在加上靠近和避开障碍

2.6 操作行为的快速实现  UnityStreer

工程介绍

2.7操作行为变成的其他问题

  计时器  协成

<1. 操作力的更新频率: 

   反馈太快会引起振荡,太慢会失控

<2. AI角色的“思考速度”

   不能高于更新速度

   不能和跟新频率相同:例如:一个汽车不能频繁的变道

操控行为突出群体中的个体,生动。A*如果AI太多时,效率太低

 第三章     A*寻路最短路径并且躲开障碍物

RTS(即时战略游戏)

3.1.1 基本术语

地图

目标估计: 估算代价的方式

代价:影响寻路的因素,例如:时间、金钱、地形、距离

节点: 记录搜索进度。且节点中存放了关键信息。 父节点,g、h、f

g: 从起始节点到当前节点的代价

h: 从当前节点到目标节点的估计代价。

f=g+h  ;f值越小,路径越好

导航图: 用来表示游戏环境的图。

   分三种:基于单元的导航图    可视点导航图    导航网格

3.1.2 方式一: 创建基于单元的导航图

这种导航图结构很规划,易于动态更新,如动态增加建筑物或者其他障碍物。

应用场景:基于单元的导航图比较适合在塔防游戏、即时战略或动态更新场景的游戏中使用。

要求:网格精细   得记录不同地形的代价消耗  

3.1.2  方式二: 创建可视点导航图/路点图

场景设计者在场景中手工放置一些可视点。测试可视点的可视性,限制点之间的长度,如果可以看见,则连起来表示路。

缺点:

场景大时,手工放置比较麻烦,且起点和终点既不是路径点也不在边上的时候,需要先找到路径点在进行寻路,走成Z形,不自然。

如果路径【即边】上有尖锐的拐角,即时视线可以到达。

如果设置了100个路径点,则可能需要测试99*100条路径。

3.1.4 创建导航网格

三角形质心之间的长度作为节点作为节点之间的代价g. 把所有中心点连起来就是从起点到目标点的连线。但是这条线不太理想,AI行走显的比较笨重。通过“视线确定法”,通过向前跳到视线所及的最远途径点,检测列表中的下几个路径点,使路径更加光滑自然。

优点: 由于同一个三角形任意两点都是精确的寻找到从起点到目标点的路径。所以精确可达到。

多边形面积可以任意大,因此可以较少数量的多边形表示出很大的可惜走区域。游戏场景本身就是由多边形构成的。根据算法将可行走区域划分为多边形。不需要人工干预。

缺点: 生成导航网格需要时间,因此更加适用于静态场景。

A* Pathfinding插件: 为了还能狗在动态的游戏过程中生成动态导航网格,在网格上挖洞,为添加物腾出空间,再对相关的三角形进行修补。注意只能动态增加,不能动态删除和动态移动障碍物。无法动态增加可行走区域。

也可以把三角形合并为凸多边形,优化节点数。

3.2 A*寻路是如何工作的?

Close表  以考察过的节点 即当算法已经检查过与它相邻的点的f,g,h值,把他放到Open以待考察。起始时表为空。

Open表   待考察节点,起始时仅包含起始节点

每次迭代时,把Open表中具有最小代价值(f最小)的节点取出进行检查,如果这个节点不是目标节点,那么考虑该节点的所有8个相邻节点。对每个相邻节点的处理规则:

重复规则,直到到达目标,如果Open表在没到达前就变空了,则没有可达路径。

3.2.2 用一个实例来完全理解A*寻路算法

<1. 

      取起始点加入到OPen中。

      考察这个点相邻的八个点【即计算相邻点的f,g,h】,检查完后把这个起始点加入到Close中 . 

      记录每个节点的连接,即当前位置之前经过的那个节点。

       

<2. 

   计算Open表中8个节点的行走代价,选择最小的作为这一轮的查考对象.

   计算从起始点移动到当前点的移动代价(记录为g),还要计算当前到目标点的代价(h),总移动代价f=g+h.

   计算g: 取它父节点的g,以找相对于父节点的连接方式,增加相应的值。例如:对角线连接,增加1.414,直线连接增加1.(或者     根据和父节点的连接关系。算出节点走回到起始位置所需要的移动代价)。

   计算h: 由于没有到达目标点,所以只能估算h,即启发方式,采用欧立德几何【两节点之间的距离】或者曼哈顿距离等。此处采用曼哈顿距离。即当前格子到目标点。先水平后垂直方向的数量和作为h的估算值。【假设没有障碍物的情况】

   计算f=g+h.首先应该检查移动代价最低的那些节点(第一步图中的选中)。如果有多个节点最有相等的最小值,可以任选一个进行检查。

以上步骤结束后,则选中(3,D)为代价最小的节点,接下来检查这个节点:障碍物无需检查,其他七个节点中3个已经在Open或者Close中,但是之前记录的g值是通过其他路径得到的,因此本轮需要再次计算通过当前节点(3,D)所得到的新g值。见第2步图示。检查结束后,把(3,D)加入到Close中。

<3. 

   首先检查Open表,找出其中具有最低代价的节点,即(3,E),(2,D),选择任意一个,例如(3,E),且相邻节点也不需要更新信息,把这个节点加入到Close中。

<4. 

   上图中,再次检查Open表,选中最低代价节点(2,D),检查8个相邻点,(2,C)、(3,C)在Open表中;(3,D)、(3,E)在Close中,检查后无需更新信息。对于(1,E)因为有障碍物(2,E)的存在,因此不是直接到达的。检查(1,C)和(1,D)。 此时相邻点检查完毕,将(2,D)d点移出Open,加入Close中。

<5. 

 上图,检查Open,任意选择一个最小代价的,即(3,C)进行检查其相邻点。更新需要更新的g,h ; 并且这个点的父节点变为(3,C) ;

再计算还没进行检查的点(2,B)的g,h,把它加入到Open中。相邻节点检查完毕后,把它移入到Close中。

下图所示: 

重复上述步骤,直到目标点也进入到Open中,沿着箭头回溯,找到从起点到目标点的路径。箭头方向看向选中点,注意对于Open表中重复的相邻点的箭头方向是不更新,只更新g,h。

最终得到下图: 

3.3 用A*寻路实现战术寻路

当AI角色不想选择最短路径,而是考虑其他元素,隐蔽性,安全性,善于某种地形等该怎么选?为不同区域赋予不同的带价值。

对于下边的起始点到目标,在路上有火力布置。黑色强遮挡火力程度大,灰色墙遮挡火力较小。

左图是未采用战术的正常A*寻路,右图是战术后的,最终路径采用战术的更加安全: 

战术规则:

    ==》    

3.5 A*寻路的适用性

起点到终点之间没有障碍物,终点直接在事视野范围内 ,则完全不必用A*。战斗中AI

第4章 AI角色对游戏世界的感知:

感知的小号较大,不能在每帧中进行。

视觉,听觉,死去的同伴活敌人,生命值,距离等来做出决策

4.1 AI角色对环境信息的感知方式

 轮询【积极的观察世界】和事件驱动【坐等消息】

 利用物理引擎: 如果想知道附近是否有AI。为AI添加一个大半径的Colider组件,选中isTrigger,检测时调用OnTriggerEnter().

事件驱动,驱动中心做检测是否发生,然后通知角色。多中检测管理中心,管理特定的检测,碰撞,声音,开关状态。

4.1.3 触发器

脚步声:压力触发器  

4.2 常用感知类型的实现

视觉、听觉、记忆

视觉: 近距离,角度大,距离近[有余光],远距离角度小距离远,或者还可以有第六感。每个视锥体由角度和最大距离来定义

视觉: 不能穿透障碍物,而且不能简单地判断是否在视锥体范围内,还需要进行视线测试(LOS),才能确定。或者亮度也影响可视性。当AI距离玩家比较远时,LOD可以减少感测。AI也不能过于聪明,突然跑来AI把玩家打死,玩家会比较沮丧,可以在玩家先看到AI的情况下,AI才能看到玩家。

4.2.5 听觉感知

当一个声音被创建时: 加一个强度属性,随着距离的增加,声音强度衰减,而每个AI角色有自己的听力阈值(<则听不到)。

听觉的特殊性是会消失。比如2s后

4.2.6 触觉感知

碰撞体,isTrigger,   可以触发的半径

4.2.7 记忆感知

让AI具有记忆: 记忆列表保存最近感知到的对象、类型,感知到的事件、还能再记忆中保留的时间。一段时间每感知到时,则从记忆列表中删除。

第5章 AI角色自主决策有限状态机

第6章 AI角色的复杂决策——行为树

采用4种节点:顺序节点、选择节点、条件节点、行为节点。

英文版 第5章 图的秘密生命

路径点叫做节点/顶点     

连接节点的线叫做边或者弧

图的联通性: 当任意一个节点都可以找到一条路径到达所有其他的节点时,我们认为这个图是联通的。

非连通图: 

数学表述: 图G  E:边的集合   N: 顶点的集合N    G={N,E}

带权的图: 权包含了从一个节点移动到另一个节点所需要的开销信息。【类似于寻路中的代价值】

5.1.3 图密室

稀疏图: 边和节点的比率决定了一张图是稀疏的还是致密的。致密图和稀疏图的有效率的实现方法不一定相同。

5.1.4  有向图

有向图: 节点的连接方式是有方向的,定义有向图的两个节点称为有序对,源节点和目标节点。例如:同一缆车只能单向运动。

两个节点之间来回运动开销可能不同。例如: 上下坡

把任意无向图可以看成有向图,即两节点互相有向。

5.1.5 游戏AI中的图

<1. 图在游戏中的应用: 导航图

<2. 依赖图【红色警戒】

   根据不同的建筑物,材料,技术。

例如:

--1.玩家已经指导弓箭手是比较有利的,它会查找依赖图,并且得出结论:生产弓箭手,得有兵站,制造技术,木材厂。

如果自己没有木材厂,则应该多造兵,更有利于自己。

--2. 

根据这些可以决策自己应该先派刺客刺杀对方的铁匠。

如果建立每一个资源的开销都被指定到依赖图的各条边上,那么AI可以利用着一信息来计算产生资源最佳路径。

3. 状态图【类似与动画状态机】

4.图的数据表示: 用邻接矩阵表示图的所有节点的连接方式,浪费了空间来存储空连接,则存储空间正比于(N*N)

而使用邻接表,来存储稀疏图,则存储空间正比于(N+E)  【点的数目和边的数目】

   ===》    

5.3 图搜索算法: p161

5.3.1 盲目搜索: 不考虑相关边的开销

到主题公园,想知道所有的娱乐项目。

1.深度优先搜索【DFS】:

只能保证连通图的所有的节点和边都被访问到,非连通图则还要保证算法包含每个子图的一个源节点。搜索时尽可能的深入一个图,死胡同时再回到上一个较浅的节点。

路径的长度无关紧要。因为深度搜索找的不是最短路径,可能会绕很大一圈才找到目标点。

<1. 【细黑线表示还没探索的】,走到D的终点时【Preate Ship】,发现没有可以从Preate Ship出发的新的边了【到达终端节点】,则返回到上一层3D Camera,探索还没被探索的边。

 ==》==》    

==》最终得到

  

DFS的优化: 

缺点: 一些图非常深,则DFS可能非常容易的再错误的的路径上陷的很深。最坏的情况可能无法从错误的搜索中恢复过来,被永久卡住。

因此限制深度搜索的深度,但是限制多少才合适呢==》因此需要迭代加深深度优先搜索==》分配搜索用的时间

2. 广度优先算法

从源节点开始,先找到和源节点相连的所有边,再选中其中一边的节点,检查从它出发的边的每一个节点。【类似A*的寻找最短路径】

c#版本数据结构 

图: 

在一个含有n个顶点的无向完全图,有n(n-1)/2条边。 n个顶点的有向完全图中有n(n-1)条弧。

节点/顶点的度: 指依附于节点的边数。

顶点的度是依附于顶点的边数:记作TD(v) 。无向图中等于顶点的边数。有向图中等于顶点的入度【顶点为弧头的弧的个数】和出度【顶点为弧尾的弧 的个数】之和。

子图: 图G1顶点和对于顶点的边都在另一个图G2中,则G1是G2的子图。

简单路径: 顶点不重复    回路:第一个和最后一个顶点重复【仅有这个重复点为简单回路】。   

连通分量:图G连通边组成的图就是这个图G的连通分量。

强连通图:在有向图中,任意顶点之间都有路径,则是强连通图。

强连通分量: 有向图的强连通子图就是强连通分量。

连通图G的生成树: G的包含其全部顶点的一个极小连通子图,极小连通子图,包含全部顶点,保证连通的前提下包含原图最少的边。

图的存储结构: 

邻接矩阵:

用两个数组表示图,一个数组是一维数组存储图中节点的信息。另一个是二维,即矩阵,存储顶点之间相邻的信息,也就是边的信息【能否连】。这就是邻接矩阵。

邻接表:

是图的一种顺序存储和链式存储相结合的数据结构。

顺序存储指图中的顶点信息用一个数组来存储,一个元素是一个节点。一个节点有两个域数据域(data)存放于顶点相关的信息。一个是引用域。存放该节点邻接表的第一个地址。【data,邻接表的首地址】

顶点的邻接表是把所有邻接于某顶点的顶点构成一个表。链式存储。【存储序号,【权值info】,next下一个节点的地址】

的邻接表==》

图的深度遍历和广度遍历 : 递归的过程

用无向图的邻接表结构来实现图的深度优先遍历算法:

设置是否遍历标志符:visited [i-1]

邻接矩阵:时间 复杂度为O(n2),n为图的顶点数

邻接表作为存储结构时:深度优先遍历图的时间复杂度为O(n+e)  ,e为图中边或弧的数目

1. 最短路径概念: 

2. 狄克斯特拉算法: 

https://blog.csdn.net/swustzhaoxingda/article/details/84318570  //详细解释

https://blog.csdn.net/swustzhaoxingda/article/details/84318570  //透彻理解该算法

提出了一个按路径 长度递增的顺序逐步产生最短路径的构造算法。

集合 S 和 T,S存放以找到最短路径的顶点,T存放未找到最短路径的顶点。初始状态时,集合S中只包含源点,

从集合T中选择到该原点路径长度最短的顶点u加到集合S中。每加入一个新的u都要修改源点到集合T中剩余顶点的当前最短路径长度值。T中各顶点新的最短路径长度值为  原来的当前最短路径长度值 与 从源点过顶点u到达该顶点的新的最短路径长度中的较小者。重复,直到T中顶点全部加到集合S中为止。【路径如果是通的则先更新,再找路径中的最小路径(除已选的)】

例: 对图(a)进行狄克斯特拉算法:找出A到其余顶点的最短路径。

第一步:列出顶点A到其余顶点的长度 为 0、∞、5、30、 ∞、∞。选中路径最小的C。S{A、C}

第二步: 找到顶点C,列出原点过顶点C到各顶点【必须是单向能到的顶点C只要到F和B】的路径值   过C->B=20  过C->F=12,如果小于原来的最短路径的长度值,则更新,这一步更新 A过C->B、A过C->F。==>更新后 0、20、5、30、 ∞、12==>除去已选的选择路径最小的,即F.   S{A、C、F}

第三步: 找到过顶点C、F后。发现到顶点D的路径长度更新为22【A到F的最短值+F->D=22】,到E的更新为30【12+18】.==>更新后 0、20、522、 30、12 ==> 过C、F,即D和E中后找出最短路径的顶点为D。 

 此处A->CFD > A->CB    应该先更新A->CB22   ==> S{A、C、F、B}  ==>0、205、22、 30、12 =

第四步: 找到过顶点C、F、D后只剩下最后一个顶点E没有找到最短路径。此时没加入S中的节点只有B、E.检查B和E的路径值,选择B作为u,过B,发现到顶点E的值是28,更新E为28. 

此处A->CFD【22】 < A->CBE【28】    应该先更新A->CFD  ==> S{A、C、F、B、D、E}

对带负权的图,应该用Floyd算法

拓扑排序:

AOV 网是顶点之 间存在优先关系的有向图。AOV 网中,不应该出现有向环路,因为有环意味着某项活动以自己作为 先决条件,这样就进入了死循环。

拓扑排序是解决 AOV 网中是否存在环路的有效手段, 若拓扑有序序列中包含 AOV 网中所有的顶点,则 AOV 网中不存在环路,否则 存在环路。有向无环图,其全部顶点都可以排成一个拓扑序列。

下图是一个有向环路,则无法实施【比如教学课程编排的优先级】

有向图的拓扑有序序列有多个,对上图举个例子:

检查图中是否有环:是否是拓扑结构:

模糊逻辑:

把面包切成中等厚度,少量的糖。

比如对距离术语编码 :近="0~2m"  中等=“2~5”m  ,现在距离是4.99m。如果用离散间隔来表示则不正确。应该使用模糊集合。

把这些值【少量的】分配到近似程序的集合,这个过程称为模糊化。

10.1 模糊集合

例如:智商

此时如果一个89的被归为笨,而90的被认为是聪明的,这也是荒唐的。这也是普通集合表示的不足。

模糊集合:通过一个隶属函数定义。从完全在这个集合之外的区域过渡在这个集合内的区域。使得一个值部分隶属于这个集合。

还要根据不同的参考框架【比如亚洲人的身高,欧美人的身高】

单隶属函数图:并非正真的模糊。

智商问题的模糊表示:由三角形单隶属函数组成。

上图显示了智商115的,聪明的隶属度是0.75,平均的隶属度为0.25.【数学表示为“与”运算】

10.2.2 模糊集合运算符

模糊集合在数学上等价于“与”运算。

下图“平均”人的模糊集合和“聪明人”的模糊集合相“与”:

一般人集合和聪明人集合做“与”运算:

   =0.25

一般人集合和聪明人集合做“或”运算:

  =0.75

10.2.3 限制词

限制词是一元运算符,用来修饰模糊集合的含义。常用的限制词为“非常地和“相当地”,用“非常地则标准变的比较严格。

10.4 模糊规则

10.4.1 为武器的选择设计模糊语言变量

10.4.3.4 去模糊化

把模糊集合转换成一个普通值得过程。

最大值均值(MOM)法

中心法:把推理结果合成单一的模糊现状图, 通过S.P采样。计算诶个采样点对整体隶属度得贡献之和,再除以采样得隶属度之和。

计算分子: 10*0.33+20*0.33+....100*0.67=334.8

计算分母:0.33+0.33+....0.67=5.4

得到普通值: 334.6/5.4=62.

树和二叉树:

节点的度:节点所拥有子树的个数

树的度: 树中各节点度的最大值,下图度为3

双亲(Parent):结点的上层结点叫该结点的双亲

祖先(Ancestor):从根到该结点所经分支上的所有结点。

堂兄弟(Sibling):同一层的双亲是不同的结点。,G 和 H 互为 堂兄弟。 

二叉树: 

二叉树的形式定义: BT=(D,R)  D是节点的有限集合。 R是节点之间关系的有限集合。

满二叉树:

除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树。

图三

完全二叉树:

每个节点和深度k有一定的对于关系

完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。

二叉树的性质:

5.2.3 二叉树的存储结构

对于完全二叉树: 完全二叉树可以计算任意节点i的双亲节点和左右孩子节点。按照上下左右的顺序存储到一维数组中。可以根据序号直接查找双亲和子孙节点的信息。

对于非完全二叉树: 因为下标之间的关系不能反映二叉树中节点之间的逻辑关系。所以需要改造,增加空节点。对不是完全二叉树来顺序存储太浪费空间,引出二叉表链表存储结构。

图5.8

2. 二叉树的二叉表链表存储结构

指二叉链表存储结构: 指二叉树的节点有三个域:一个数据域和两个引用域。

n个节点,则n-1个引用域被使用,n+1个引用域是空的。二叉链表可以容易访问子孙节点,但是访问祖先节点很困难。引出三叉链表存储结构。

二叉树的三叉链表存储结构: 

有四个域,数据域、左右孩子节点地址、双亲节点地址。

5.2.4 二叉链表存储结构类实现。P135

5.2.4 二叉树的遍历【一般用于动态查找表】

D、L、R  根节点  左节点、右节点

遍历方式: DLR(先序遍历)、LDR(中序遍历)、LRD(后序遍历)【还有层序遍历根节点、上下、左右】

5.3  树与森林

哈夫曼树(Huffman Tree),又叫最优二叉树,指的是对于一组具有确定权值 的叶子结点的具有最小带权路径长度的二叉树

排序:

查找:

是否有关键码,主关键码还是次关键码

静态查找: 查找给定关键码,不改变数据结构。这个表是静态查找表 。比如: 验证是否存在

动态查找: 查找到后插入或者删除。

查找平均长度: 查找关键码比较的平均次数。衡量查找算法

有序表的折半查找:

在有序表中取之间的记录作为比较对象。关键码等于中间记录码,则查找成功.r若是<,则在中间记录的左半区找,否则在右区找。

动态查找表:二叉排序树

猜你喜欢

转载自blog.csdn.net/qq_35433081/article/details/93992404