路径规划之 A* 算法

算法介绍

A*(念做:A Star)算法是一种很常用的路径查找和图形遍历算法。它有较好的性能和准确度。本文在讲解算法的同时也会提供Python语言的代码实现,并会借助matplotlib库动态的展示算法的运算过程。

A*算法最初发表于1968年,由Stanford研究院的Peter Hart, Nils Nilsson以及Bertram Raphael发表。它可以被认为是Dijkstra算法的扩展。

由于借助启发函数的引导,A*算法通常拥有更好的性能。

广度优先搜索

为了更好的理解A*算法,我们首先从广度优先(Breadth First)算法讲起。

正如其名称所示,广度优先搜索以广度做为优先级进行搜索。

从起点开始,首先遍历起点周围邻近的点,然后再遍历已经遍历过的点邻近的点,逐步的向外扩散,直到找到终点。

这种算法就像洪水(Flood fill)一样向外扩张,算法的过程如下图所示:

breadth_first.gif

在上面这幅动态图中,算法遍历了图中所有的点,这通常没有必要。对于有明确终点的问题来说,一旦到达终点便可以提前终止算法,下面这幅图对比了这种情况:

在执行算法的过程中,每个点需要记录达到该点的前一个点的位置 -- 可以称之为父节点。这样做之后,一旦到达终点,便可以从终点开始,反过来顺着父节点的顺序找到起点,由此就构成了一条路径。

Dijkstra算法

Dijkstra算法是由计算机科学家Edsger W. Dijkstra在1956年提出的。

Dijkstra算法用来寻找图形中节点之间的最短路径。

考虑这样一种场景,在一些情况下,图形中相邻节点之间的移动代价并不相等。例如,游戏中的一幅图,既有平地也有山脉,那么游戏中的角色在平地和山脉中移动的速度通常是不相等的。

在Dijkstra算法中,需要计算每一个节点距离起点的总移动代价。同时,还需要一个优先队列结构。对于所有待遍历的节点,放入优先队列中会按照代价进行排序。

在算法运行的过程中,每次都从优先队列中选出代价最小的作为下一个遍历的节点。直到到达终点为止。

下面对比了不考虑节点移动代价差异的广度优先搜索与考虑移动代价的Dijkstra算法的运算结果:

dijkstra.gif

当图形为网格图,并且每个节点之间的移动代价是相等的,那么Dijkstra算法将和广度优先算法变得一样。

最佳优先搜索

在一些情况下,如果我们可以预先计算出每个节点到终点的距离,则我们可以利用这个信息更快的到达终点。

其原理也很简单。与Dijkstra算法类似,我们也使用一个优先队列,但此时以每个节点到达终点的距离作为优先级,每次始终选取到终点移动代价最小(离终点最近)的节点作为下一个遍历的节点。这种算法称之为最佳优先(Best First)算法。

这样做可以大大加快路径的搜索速度,如下图所示:

best_first.gif

但这种算法会不会有什么缺点呢?答案是肯定的。

因为,如果起点和终点之间存在障碍物,则最佳优先算法找到的很可能不是最短路径,下图描述了这种情况。

best_first_2.gif

A*算法

对比了上面几种算法,最后终于可以讲解本文的重点:A*算法了。

下面的描述我们将看到,A*算法实际上是综合上面这些算法的特点于一身的。

A*算法通过下面这个函数来计算每个节点的优先级。

 

f(n)=g(n)+h(n)

其中:

  • f(n) 是节点n的综合优先级。当我们选择下一个要遍历的节点时,我们总会选取综合优先级最高(值最小)的节点。
  • g(n) 是节点n距离起点的代价。
  • h(n) 是节点n距离终点的预计代价,这也就是A*算法的启发函数。关于启发函数我们在下面详细讲解。

A*算法在运算过程中,每次从优先队列中选取f(n)值最小(优先级最高)的节点作为下一个待遍历的节点。

另外,A*算法使用两个集合来表示待遍历的节点,与已经遍历过的节点,这通常称之为open_setclose_set

完整的A*算法描述如下:

* 初始化open_set和close_set;
* 将起点加入open_set中,并设置优先级为0(优先级最高);
* 如果open_set不为空,则从open_set中选取优先级最高的节点n:* 如果节点n为终点,则:* 从终点开始逐步追踪parent节点,一直达到起点;* 返回找到的结果路径,算法结束;* 如果节点n不是终点,则:* 将节点n从open_set中删除,并加入close_set中;* 遍历节点n所有的邻近节点:* 如果邻近节点m在close_set中,则:* 跳过,选取下一个邻近节点* 如果邻近节点m也不在open_set中,则:* 设置节点m的parent为节点n* 计算节点m的优先级* 将节点m加入open_set中

启发函数

上面已经提到,启发函数会影响A*算法的行为。

  • 在极端情况下,当启发函数h(n)始终为0,则将由g(n)决定节点的优先级,此时算法就退化成了Dijkstra算法。
  • 如果h(n)始终小于等于节点n到终点的代价,则A*算法保证一定能够找到最短路径。但是当h(n)的值越小,算法将遍历越多的节点,也就导致算法越慢。
  • 如果h(n)完全等于节点n到终点的代价,则A*算法将找到最佳路径,并且速度很快。可惜的是,并非所有场景下都能做到这一点。因为在没有达到终点之前,我们很难确切算出距离终点还有多远。
  • 如果h(n)的值比节点n到终点的代价要大,则A*算法不能保证找到最短路径,不过此时会很快。
  • 在另外一个极端情况下,如果h(n)相较于g(n)大很多,则此时只有h(n)产生效果,这也就变成了最佳优先搜索。

由上面这些信息我们可以知道,通过调节启发函数我们可以控制算法的速度和精确度。因为在一些情况,我们可能未必需要最短路径,而是希望能够尽快找到一个路径即可。这也是A*算法比较灵活的地方。

对于网格形式的图,有以下这些启发函数可以使用:

  • 如果图形中只允许朝上下左右四个方向移动,则可以使用曼哈顿距离(Manhattan distance)。
  • 如果图形中允许朝八个方向移动,则可以使用对角距离。
  • 如果图形中允许朝任何方向移动,则可以使用欧几里得距离(Euclidean distance)。

关于距离

曼哈顿距离

如果图形中只允许朝上下左右四个方向移动,则启发函数可以使用曼哈顿距离,它的计算方法如下图所示:

计算曼哈顿距离的函数如下,这里的D是指两个相邻节点之间的移动代价,通常是一个固定的常数。

function heuristic(node) =dx = abs(node.x - goal.x)dy = abs(node.y - goal.y)return D * (dx + dy)

对角距离

如果图形中允许斜着朝邻近的节点移动,则启发函数可以使用对角距离。它的计算方法如下:

计算对角距离的函数如下,这里的D2指的是两个斜着相邻节点之间的移动代价。如果所有节点都正方形,则其值就是2∗D。

function heuristic(node) =dx = abs(node.x - goal.x)dy = abs(node.y - goal.y)return D * (dx + dy) + (D2 - 2 * D) * min(dx, dy)

欧几里得距离

如果图形中允许朝任意方向移动,则可以使用欧几里得距离。

欧几里得距离是指两个节点之间的直线距离,因此其计算方法也是我们比较熟悉的:(p2.x−p1.x)2+(p2.y−p1.y)2。其函数表示如下:

function heuristic(node) =dx = abs(node.x - goal.x)dy = abs(node.y - goal.y)return D * sqrt(dx * dx + dy * dy)

算法实现

虽然前面介绍了很多内容,但实际上A*算法并不复杂,实现起来也比较简单。

下面我们给出一个Python语言的代码示例。

之所以使用Python语言是因为我们可以借助matplotlib库很方便的将结果展示出来。在理解了算法之后,通过其他语言实现也并非难事。

算法的源码可以到我的github上下载:paulQuei/a-star-algorithm。

我们的算法演示的是在一个二维的网格图形上从起点找寻终点的求解过程。

坐标点与地图

首先,我们创建一个非常简单的类来描述图中的点,相关代码如下:

# point.pyimport sysclass Point:def __init__(self, x, y):self.x = xself.y = yself.cost = sys.maxsize

接着,我们实现一个描述地图结构的类。为了简化算法的描述:

我们选定左下角坐标[0, 0]的点是算法起点,右上角坐标[size - 1, size - 1]的点为要找的终点。

为了让算法更有趣,我们在地图的中间设置了一个障碍,并且地图中还会包含一些随机的障碍。该类的代码如下:

# random_map.pyimport numpy as npimport pointclass RandomMap:def __init__(self, size=50): ①self.size = sizeself.obstacle = size//8 ②self.GenerateObstacle() ③def GenerateObstacle(self):self.obstacle_point = []self.obstacle_point.append(point.Point(self.size//2, self.size//2))self.obstacle_point.append(point.Point(self.size//2, self.size//2-1))# Generate an obstacle in the middlefor i in range(self.size//2-4, self.size//2): ④self.obstacle_point.append(point.Point(i, self.size-i))self.obstacle_point.append(point.Point(i, self.size-i-1))self.obstacle_point.append(point.Point(self.size-i, i))self.obstacle_point.append(point.Point(self.size-i, i-1))for i in range(self.obstacle-1): ⑤x = np.random.randint(0, self.size)y = np.random.randint(0, self.size)self.obstacle_point.append(point.Point(x, y))if (np.random.rand() > 0.5): # Random boolean ⑥for l in range(self.size//4):self.obstacle_point.append(point.Point(x, y+l))passelse:for l in range(self.size//4):self.obstacle_point.append(point.Point(x+l, y))passdef IsObstacle(self, i ,j): ⑦for p in self.obstacle_point:if i==p.x and j==p.y:return Truereturn False

这段代码说明如下:

  1. 构造函数,地图的默认大小是50x50;
  2. 设置障碍物的数量为地图大小除以8;
  3. 调用GenerateObstacle生成随机障碍物;
  4. 在地图的中间生成一个斜着的障碍物;
  5. 随机生成其他几个障碍物;
  6. 障碍物的方向也是随机的;
  7. 定义一个方法来判断某个节点是否是障碍物;

算法主体

有了基本的数据结构之后,我们就可以开始实现算法主体了。

这里我们通过一个类来封装我们的算法。

首先实现一些算法需要的基本函数,它们如下:

# a_star.pyimport sys
import timeimport numpy as npfrom matplotlib.patches import Rectangleimport point
import random_mapclass AStar:def __init__(self, map):self.map=mapself.open_set = []self.close_set = []def BaseCost(self, p):x_dis = p.xy_dis = p.y# Distance to start pointreturn x_dis + y_dis + (np.sqrt(2) - 2) * min(x_dis, y_dis)def HeuristicCost(self, p):x_dis = self.map.size - 1 - p.xy_dis = self.map.size - 1 - p.y# Distance to end pointreturn x_dis + y_dis + (np.sqrt(2) - 2) * min(x_dis, y_dis)def TotalCost(self, p):return self.BaseCost(p) + self.HeuristicCost(p)def IsValidPoint(self, x, y):if x < 0 or y < 0:return Falseif x >= self.map.size or y >= self.map.size:return Falsereturn not self.map.IsObstacle(x, y)def IsInPointList(self, p, point_list):for point in point_list:if point.x == p.x and point.y == p.y:return Truereturn Falsedef IsInOpenList(self, p):return self.IsInPointList(p, self.open_set)def IsInCloseList(self, p):return self.IsInPointList(p, self.close_set)def IsStartPoint(self, p):return p.x == 0 and p.y ==0def IsEndPoint(self, p):return p.x == self.map.size-1 and p.y == self.map.size-1

这里的函数说明如下:

  • __init__:类的构造函数。
  • BaseCost:节点到起点的移动代价,对应了上文的g(n)。
  • HeuristicCost:节点到终点的启发函数,对应上文的h(n)。由于我们是基于网格的图形,所以这个函数和上一个函数用的是对角距离。
  • TotalCost:代价总和,即对应上面提到的f(n)。
  • IsValidPoint:判断点是否有效,不在地图内部或者障碍物所在点都是无效的。
  • IsInPointList:判断点是否在某个集合中。
  • IsInOpenList:判断点是否在open_set中。
  • IsInCloseList:判断点是否在close_set中。
  • IsStartPoint:判断点是否是起点。
  • IsEndPoint:判断点是否是终点。

有了上面这些辅助函数,就可以开始实现算法主逻辑了,相关代码如下:

# a_star.py
def RunAndSaveImage(self, ax, plt):start_time = time.time()start_point = point.Point(0, 0)start_point.cost = 0self.open_set.append(start_point)while True:index = self.SelectPointInOpenList()if index < 0:print('No path found, algorithm failed!!!')returnp = self.open_set[index]rec = Rectangle((p.x, p.y), 1, 1, color='c')ax.add_patch(rec)self.SaveImage(plt)if self.IsEndPoint(p):return self.BuildPath(p, ax, plt, start_time)del self.open_set[index]self.close_set.append(p)# Process all neighborsx = p.xy = p.yself.ProcessPoint(x-1, y+1, p)self.ProcessPoint(x-1, y, p)self.ProcessPoint(x-1, y-1, p)self.ProcessPoint(x, y-1, p)self.ProcessPoint(x+1, y-1, p)self.ProcessPoint(x+1, y, p)self.ProcessPoint(x+1, y+1, p)self.ProcessPoint(x, y+1, p)

这段代码应该不需要太多解释了,它就是根据前面的算法逻辑进行实现。为了将结果展示出来,我们在算法进行的每一步,都会借助于matplotlib库将状态保存成图片。

上面这个函数调用了其他几个函数代码如下:

# a_star.py
def SaveImage(self, plt):millis = int(round(time.time() * 1000))filename = './' + str(millis) + '.png'plt.savefig(filename)def ProcessPoint(self, x, y, parent):if not self.IsValidPoint(x, y):return # Do nothing for invalid pointp = point.Point(x, y)if self.IsInCloseList(p):return # Do nothing for visited pointprint('Process Point [', p.x, ',', p.y, ']', ', cost: ', p.cost)if not self.IsInOpenList(p):p.parent = parentp.cost = self.TotalCost(p)self.open_set.append(p)def SelectPointInOpenList(self):index = 0selected_index = -1min_cost = sys.maxsizefor p in self.open_set:cost = self.TotalCost(p)if cost < min_cost:min_cost = costselected_index = indexindex += 1return selected_indexdef BuildPath(self, p, ax, plt, start_time):path = []while True:path.insert(0, p) # Insert firstif self.IsStartPoint(p):breakelse:p = p.parentfor p in path:rec = Rectangle((p.x, p.y), 1, 1, color='g')ax.add_patch(rec)plt.draw()self.SaveImage(plt)end_time = time.time()print('===== Algorithm finish in', int(end_time-start_time), ' seconds')

这三个函数应该是比较容易理解的:

  • SaveImage:将当前状态保存到图片中,图片以当前时间命名。
  • ProcessPoint:针对每一个节点进行处理:如果是没有处理过的节点,则计算优先级设置父节点,并且添加到open_set中。
  • SelectPointInOpenList:从open_set中找到优先级最高的节点,返回其索引。
  • BuildPath:从终点往回沿着parent构造结果路径。然后从起点开始绘制结果,结果使用绿色方块,每次绘制一步便保存一个图片。

测试入口

最后是程序的入口逻辑,使用上面写的类来查找路径:

# main.pyimport numpy as np
import matplotlib.pyplot as pltfrom matplotlib.patches import Rectangleimport random_map
import a_starplt.figure(figsize=(5, 5))map = random_map.RandomMap() ①ax = plt.gca()
ax.set_xlim([0, map.size]) ②
ax.set_ylim([0, map.size])for i in range(map.size): ③for j in range(map.size):if map.IsObstacle(i,j):rec = Rectangle((i, j), width=1, height=1, color='gray')ax.add_patch(rec)else:rec = Rectangle((i, j), width=1, height=1, edgecolor='gray', facecolor='w')ax.add_patch(rec)rec = Rectangle((0, 0), width = 1, height = 1, facecolor='b')
ax.add_patch(rec) ④rec = Rectangle((map.size-1, map.size-1), width = 1, height = 1, facecolor='r')
ax.add_patch(rec) ⑤plt.axis('equal') ⑥
plt.axis('off')
plt.tight_layout()
#plt.show()a_star = a_star.AStar(map)
a_star.RunAndSaveImage(ax, plt) ⑦

这段代码说明如下:

  1. 创建一个随机地图;
  2. 设置图像的内容与地图大小一致;
  3. 绘制地图:对于障碍物绘制一个灰色的方块,其他区域绘制一个白色的的方块;
  4. 绘制起点为蓝色方块;
  5. 绘制终点为红色方块;
  6. 设置图像的坐标轴比例相等并且隐藏坐标轴;
  7. 调用算法来查找路径;

由于我们的地图是随机的,所以每次运行的结果可能会不一样,下面是我的电脑上某次运行的结果:

a_star.gif

如果感兴趣这篇文章中的动图是如何制作的,请看我的另外一篇文章:使用Matplotlib绘制3D图形 - 制作动图。

算法变种

A*算法有不少的变种,这里我们介绍最主要的几个。

更多的内容请以访问维基百科:A* Variants。

ARA*

ARA 全称是Anytime Repairing A*,也称为Anytime A

与其他Anytime算法一样,它具有灵活的时间成本,即使在它结束之前被中断,也可以返回路径查找或图形遍历问题的有效解决方案。方法是在逐步优化之前生成快速,非最优的结果。

在现实世界的规划问题中,问题的解决时间往往是有限的。与时间相关的规划者对这种情况都会比较熟悉:他们能够快速找到可行的解决方案,然后不断努力改进,直到时间用完为止。

启发式搜索ARA*算法,它根据可用的搜索时间调整其性能边界。它首先使用松散边界快速找到次优解,然后在时间允许的情况下逐渐收紧边界。如果有足够的时间,它会找到可证明的最佳解决方方案。在改进其约束的同时,ARA*重复使用以前的搜索工作,因此,比其他随时搜索方法更有效。

与A*算法不同,Anytime A*算法最重要的功能是,它们可以被停止,然后可以随时重启。该方法使用控制管理器类来处理时间限制以及停止和重新启动A*算法以找到初始的,可能是次优的解决方案,然后继续搜索改进的解决方案,直到达到可证明的最佳解决方案。

关于ARA*的更多内容可以阅读这篇论文:

  • ARA - Anytime A with Provable Bounds on Sub-Optimality。

D*

D*是Dynamic A*的简写,其算法和A*类似,不同的是,其代价的计算在算法运行过程中可能会发生变化。

D*包含了下面三种增量搜索算法:

  • 原始的D*由Anthony Stentz发表。
  • Focussed D*由Anthony Stentz发表,是一个增量启发式搜索算法,结合了A*和原始D*的思想。
  • D Lite是由Sven Koenig和Maxim Likhachev基于LPA构建的算法。

所有三种搜索算法都解决了相同的基于假设的路径规划问题,包括使用自由空间假设进行规划。在这些环境中,机器人必须导航到未知地形中的给定目标坐标。它假设地形的未知部分(例如:它不包含障碍物),并在这些假设下找到从当前坐标到目标坐标的最短路径。

然后机器人沿着路径行进。当它观察到新的地图信息(例如以前未知的障碍物)时,它会将信息添加到其地图中,并在必要时将新的最短路径从其当前坐标重新添加到给定的目标坐标。它会重复该过程,直到达到目标坐标或确定无法达到目标坐标。在穿越未知地形时,可能经常发现新的障碍,因此重新计划需要很快。增量(启发式)搜索算法通过使用先前问题的经验来加速搜索当前问题,从而加速搜索类似搜索问题的序列。假设目标坐标没有改变,则所有三种搜索算法都比重复的A*搜索更有效。

D*及其变体已广泛用于移动机器人和自动车辆导航。当前系统通常基于D* Lite而不是原始D*或Focussed D*。

关于D*的更多内容可以阅读这两篇文章:

  • Project "Fast Replanning (Incremental Heuristic Search)"
  • Real-Time Replanning in Dynamic and Unknown Environments

Field D*

Field D*扩展了D*和D* Lite,是一种基于插值( interpolation-based )的规划算法,它使用线性插值来有效地生成低成本路径,从而消除不必要的转向。

在给定线性插值假设的情况下,路径是最优的,并且在实践中非常有效。该算法目前被各种现场机器人系统使用。

关于Field D*的详细内容可以看下面这篇论文:

  • Field D*: An Interpolation-based Path Planner and Replanner

Block A*

Block A*扩展自A*,但它操作是一块(block)单元而不是单个单元。

其open_set中的每个条目都是已到达但尚未扩展的块,或者需要重新扩展的块。

open_set中块的优先级称为其堆值(heap value)。与A*类似,Block A*中的基本循环是删除具有最低堆值的条目并将其展开。在扩展期间使用LDDB来计算正在扩展的块中的边界单元的g值。

LDDB是一种新型数据库,它包含了本地邻域边界点之间的距离。

关于Block A*的更多内容可以看下面这篇论文:

  • Block A*: Database-Driven Search with Applications in Any-angle Path-Planning

 

原文链接
本文为云栖社区原创内容,未经允许不得转载。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

王思聪究竟上了多少次热搜?

戳蓝字“CSDN云计算”关注我们哦&#xff01;作者 | 朱小五责编 | 阿秃王思聪又又又上了微博热搜——然而这次却不是关于娱乐圈。最近几天&#xff0c;王思聪与他的“限消令”接连登上热搜榜&#xff0c;引发吃瓜群众们广泛热议。知乎的段子手们也纷纷发挥自己的想象力。小五本…

2018年,自然语言处理很全的应用与合作

2018年见证了 NLP 许多新的应用发展。Elvis Saravia 是计算语言学专家&#xff0c;也是2019 计算语言学会年度大会北美分部的项目委员之一。他在一份报告中总结出&#xff0c;NLP 不仅在聊天机器人和机器学习中有所突破&#xff0c;也在医疗健康、金融、法律和广告等行业中有崭…

如何把springboot项目部署到tomcat上

文章目录一、 企业发布场景1. 首次发布2. 非首次发布3. 全量发布和增量发布概念和区别二、springboot部署tomcat2.1. 创建Web初始化类2.2. 修改打包方式2.3. 项目发布目录2.4. 启动tomcat2.5. 浏览器验证一、 企业发布场景 1. 首次发布 项目上线第一次会采用全量发布 【编译】…

OceanBase迁移服务:向分布式架构升级的直接路径

2019年1月4日&#xff0c;OceanBase迁移服务解决方案在ATEC城市峰会中正式发布。蚂蚁金服资深技术专家师文汇和技术专家韩谷悦共同分享了OceanBase迁移服务的重要特性和业务实践。 蚂蚁数据库架构的三代升级史 在过去的十多年时间里&#xff0c;蚂蚁在整个基础数据库架构上一…

被嫌弃的互联网的 “一生”(上)

戳蓝字“CSDN云计算”关注我们哦&#xff01;作者 | 小灰责编 | 阿秃在人类的历史长河中&#xff0c;我们这一代人是最幸运的一代&#xff0c;因为我们生活在一个智慧飞扬的时代。这个时代最伟大的发明是什么&#xff1f;或许每个人心中都有不同的答案。在小灰看来&#xff0c;…

Mars 是什么、能做什么、如何做的——记 Mars 在 PyCon China 2018 上的分享

最近&#xff0c;在 PyCon China 2018 的北京主会场、成都和杭州分会场都分享了我们最新的工作 Mars&#xff0c;基于矩阵的统一计算框架。本文会以文字的形式对 PyCon 中国上的分享再进行一次阐述。 听到 Mars&#xff0c;很多第一次听说的同学都会灵魂三问&#xff1a;Mars …

Failed to bind properties under mybatis-plus.configuration.result-maps[0]

Failed to bind properties under mybatis-plus.configuration.incomplete-result-maps[0].assistant.configuration.mapped-statements[0].parameter-map.parameter-mappings[0] to org.apache.ibatis.mapping.ParameterMapping解决方案&#xff1a; 鉴于Spring Boot 2.2.0 和…

为什么要学Python 编程?(附Python学习路线)

为何程序员多数会选择 Python 作为入门级语言&#xff1f;在此&#xff0c;估计不少开发者都会予以反驳&#xff0c;自己明明就没有选择 Python&#xff0c;不能一概而论。下面&#xff0c;我们就用数据一窥如今最流行的编程语言。今年的 3 月份&#xff0c;国外招聘网站 Hacke…

“资源添加到Web应用程序[]的缓存中,因为在清除过期缓存条目后可用空间仍不足 - 请考虑增加缓存的最大空间”

解决办法&#xff1a; 在 /conf/context.xml 的 前添加以下内容&#xff1a; <Resources cachingAllowed"true" cacheMaxSize"100000" />

报告!这群阿里工程师在偷偷养猪

今天下午&#xff0c;期盼已久的阿里巴巴技术脱贫大会就要开始了。 很多人都知道&#xff0c;我们在1年前就投入100亿元人民币成立阿里巴巴脱贫基金。从教育到健康&#xff0c;再到女性、生态和电商扶贫&#xff0c;这五个方向分别由五位阿里合伙人直接牵头。 很多人不知道的…

七大新品集中亮相,腾讯云AI大数据全线升级!

近日腾讯云在北京举行大数据AI新品发布会。会上&#xff0c;腾讯云带来了在大数据与AI领域的最新研究成果&#xff0c;包括AI换脸甄别技术AntiFakes、腾讯星图以及企业画像平台等七大重磅新品&#xff0c;并对AI、大数据产品进行全线升级&#xff0c;致力于为用户带来更精细化的…

解决CentOS7本机时间与实际时间相差8小时的问题

查看当前日期时间&#xff1a; timedatectl删除原来的时间日期配置 rm -rf /etc/localtime链接指向新的时间日期配置 ln -sv /usr/share/zoneinfo/Universal /etc/localtime设置完成后查看当前时间日期&#xff1a; 如果不生效请重启 reboot

阿里开发者们的第15个感悟:做一款优秀大数据引擎,要找准重点解决的业务场景

2015年12月20日&#xff0c;云栖社区上线。2018年12月20日&#xff0c;云栖社区3岁。 阿里巴巴常说“晴天修屋顶”。 在我们看来&#xff0c;寒冬中&#xff0c;最值得投资的是学习&#xff0c;是增厚的知识储备。 所以社区特别制作了这个专辑——分享给开发者们20个弥足珍贵的…

解决vsftpd 读取目录列表失败的问题

文章目录1. 问题现象2. 解决方案(重启时效)3. 重启失效解决1. 问题现象 使用第三方FTP软件filezilla进行登陆&#xff0c;出现如下错误&#xff1a; 状态: 正在连接 192.168.1.6:21... 状态: 连接建立&#xff0c;等待欢迎消息... 响应: 220 (vsFTPd 2.2.2) 命令: …

阿里开发者们的第16个感悟:让阅读源码成为习惯

2015年12月20日&#xff0c;云栖社区上线。2018年12月20日&#xff0c;云栖社区3岁。 阿里巴巴常说“晴天修屋顶”。 在我们看来&#xff0c;寒冬中&#xff0c;最值得投资的是学习&#xff0c;是增厚的知识储备。 所以社区特别制作了这个专辑——分享给开发者们20个弥足珍贵的…

腾讯云全面更新数据智能服务全景图!

近日在腾讯云AI大数据新品发布会上&#xff0c;腾讯云副总裁王龙向听众全面介绍了当前腾讯云数据智能服务的全景布局。针对目前整体AI行业的发展趋势&#xff0c;他表示过去一招鲜的发展模式已经难以为继&#xff0c;取而代之的是真正能够产生价值的、端到端的、全面的AI解决方…

揭秘人工智能(系列):人工智能带来的网络安全威胁

历史表明&#xff0c;网络安全威胁随着新的技术进步而增加。关系数据库带来了SQL注入攻击&#xff0c;Web脚本编程语言助长了跨站点脚本攻击&#xff0c;物联网设备开辟了创建僵尸网络的新方法。而互联网打开了潘多拉盒子的数字安全弊病&#xff0c;社交媒体创造了通过微目标内…

支付宝工程师创造出了一个可以“拷贝”支付宝的神器

mPaaS是源于支付宝的移动开发平台&#xff0c;从最初的金融级移动开发平台&#xff0c;逐渐演进成集开发、测试、发布、分析、运营于一体的 App 全生命周期管理平台&#xff0c;服务了广发银行、12306、上海地铁等标杆级客户&#xff0c;帮助客户完成技术升级与业务增长。 “拷…

Kubernetes API 与 Operator,不为人知的开发者战争

如果我问你&#xff0c;如何把一个 etcd 集群部署在 Google Cloud 或者阿里云上&#xff0c;你一定会不假思索的给出答案&#xff1a;当然是用 etcd Operator&#xff01; 实际上&#xff0c;几乎在一夜之间&#xff0c;Kubernetes Operator 这个新生事物&#xff0c;就成了开…

面试编程岗,期望40K,为什么老板给我翻倍?

临近年底&#xff0c;互联网正在经历优化升级&#xff0c;不少公司出现了裁员新闻&#xff0c;也有很多人纷纷转型、跳槽。那么 IT领域内&#xff0c;什么样的工作比较好&#xff1f;最近笔者看笔者在网上看到一个被裁员的帖子&#xff0c;但仍被震撼到了&#xff0c;也许答案不…