A星进一步优化,让二叉堆更快,更猛。as3版


转自:http://www.cnblogs.com/pelephone/archive/2012/09/27/2704902.html

二叉堆优化提高了A星一大步,但是想要更快,更猛,还不能停步。这两天整理自己的A星,啃了些网上搜的一些大神源码,今天整理下思路分享下。

A星原理和二叉优化在天地会文章很多,这里就不多说了,要读此文先,提前是对A星有一定了解。(本文思路源于别人代码,不是我原创,呵呵)

 

优化大多是用空间换时间,把过程的计算量放到初始存入内存。下面直接进入正题吧。

一、障碍邻点的预计算。

判断周围格子是否有障碍还有计算格子的cost价值,如果在每次寻路的时候才去算就太浪费了,因为每次寻路的时候每个格子相邻的障碍是不会变的,价值cost也不变,我们大可以把这块计算抽出放到初始地图的时候计算。步骤如下:

1.给节点添加邻节点数组nodeLinks,和邻节点价值组costLinks。(也可建一个linkNode对象,对象里面有节点和价值两属性,反正两数组是个映对关系),保证nodeLinks[i]的价值是costLinks[i]

2.地图初始好的时候遍历每个格子,计算出其周围的所有非障碍的格子,并加入节点邻数组中存起。计算每个邻节点的价值,映对存入节点价值组。

3.寻路的时候判断节点四周有哪些格子直接从当前计算的节点的nodeLinks属性取即可,不需要再计算。

预计算可节省很多性能,不过缺点是初始的时候很慢。 

 

二、Array数组优化

Array的indexOf和shift方法是很吃性能的,很多人的A星在while里面有这两个东西,这是那几百毫秒慢下的原因。怀疑的同学可以自己写个1万次的大循环调用这两个方法看看。要判断节点是否在开户组里面,普通人的做法是用indexOf,其实这个方式可以换成以下处理

1.给节点添加isOpen:Boolean属性

2.每次push节点到打开数组的里面设节点的isOpen为true

3.节点移出打开列表时,将节点的isOpen属性设为false

4.判断节点是否在开启列表时只需要判断isOpen即可

判断关闭同理,另外还有会用上indexOf的地方是二叉堆里面,干掉方式略。

将关闭组添加到通过父节点添加到返回路径时,很多人会用shift,把节点一个个添到path数组里面,这也是个慢的因素。可以换成push方式,push不会改变索引,所以效率很高。将关闭节点组数据通过push加入路径后,再调用reverse把数组倒置,结果跟用shift一样,不过用时可大不一样。

 

三、打开关闭标记,换掉不用重置

用isOpen来替代indexOf大大提升了一步性能,可是每次寻路完之后会有个问题,那就是要把节点重置,就是把所有打开和关闭列表里面的节点的isOpen,isClose设false。呵呵,虽然这个重置吃的性能相当小,不过本着追求精神,我又找出了更变态的方式。步骤如下

1.每次A星计算设一唯一自增标记markIndex,每计算一次寻路,这个整型属性+1

2.把节点里面的isOpen和isClose布尔属性改成openMark,closeMark整型属性。

3.加入列表时把openmark设为本次计算的自动标记markIndex,移出列表设为-1(或随便一个不等于markIndex的数)

4.判断是否在打开列表中只需判断if(openMark==markIndex)即可

5.某次寻路计算完之后markIndex+1

因为自增标记每次计算都不一样,所以节点的openMark不需要重置,下次也能继续用。这个方式带来的性能提升很微小不多,不追求那几十毫秒的大可无视。

 

四、减少while里面get/set/function

get/set的性能其实很高,不过在项目中,大量的格子一经过while方法就是数万次的运算,虽然每次性能相差很少,但量一大起来效率就明显了。不信的人可以写个大循环在里面用getter和public属性的方式对比一下,性能相差三四倍。如果节点的f,g,h,x,y这些属性用了接口get/set,那个运算毫秒相差就很明显了。function 是代码设计不可省的东西,这一步优化只适合在A星的while中用,正常项目中可省不得。

 

五、位运算,再提一提微小的性能

在二叉堆中的除以2,num/2==num*0.5==num >> 1,这三个种计算方式最快的明显是第三种,虽然可读性差些,不过效率还是有点提升滴。另外num*2 == num<<1;也可提升些性能

 

目前能想到的提速度方法就这样,先开篇日记记录一下,以后找到新的再补。源码还没整理好,暂时先这么上了

  • 6
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
下面是一个 TypeScript 实现的双向 A* 算法,其中使用二叉堆作为优先队列: ```typescript interface Node { x: number; y: number; f: number; g: number; h: number; parent: Node | null; } function aStar(start: Node, end: Node, map: number[][]): Node | null { const openList1: Node[] = [start]; const openList2: Node[] = [end]; const closeList1: Node[] = []; const closeList2: Node[] = []; const heap1 = new BinaryHeap<Node>((a, b) => a.f < b.f); const heap2 = new BinaryHeap<Node>((a, b) => a.f < b.f); heap1.push(start); heap2.push(end); while (heap1.size() > 0 && heap2.size() > 0) { const current1 = heap1.pop()!; const current2 = heap2.pop()!; closeList1.push(current1); closeList2.push(current2); if (closeList2.some(node => node.x === current1.x && node.y === current1.y)) { const node = closeList2.find(node => node.x === current1.x && node.y === current1.y); return node?.parent ? node.parent : node; } if (closeList1.some(node => node.x === current2.x && node.y === current2.y)) { const node = closeList1.find(node => node.x === current2.x && node.y === current2.y); return node?.parent ? node.parent : node; } const neighbors1 = getNeighbors(current1, map); neighbors1.forEach(neighbor => { if (closeList1.some(node => node.x === neighbor.x && node.y === neighbor.y)) return; if (!openList1.some(node => node.x === neighbor.x && node.y === neighbor.y)) { neighbor.g = current1.g + 1; neighbor.h = manhattanDistance(neighbor, end); neighbor.f = neighbor.g + neighbor.h; neighbor.parent = current1; openList1.push(neighbor); heap1.push(neighbor); } else { const existingNode = openList1.find(node => node.x === neighbor.x && node.y === neighbor.y); if (existingNode && current1.g + 1 < existingNode.g) { existingNode.g = current1.g + 1; existingNode.f = existingNode.g + existingNode.h; existingNode.parent = current1; heap1.update(existingNode); } } }); const neighbors2 = getNeighbors(current2, map); neighbors2.forEach(neighbor => { if (closeList2.some(node => node.x === neighbor.x && node.y === neighbor.y)) return; if (!openList2.some(node => node.x === neighbor.x && node.y === neighbor.y)) { neighbor.g = current2.g + 1; neighbor.h = manhattanDistance(neighbor, start); neighbor.f = neighbor.g + neighbor.h; neighbor.parent = current2; openList2.push(neighbor); heap2.push(neighbor); } else { const existingNode = openList2.find(node => node.x === neighbor.x && node.y === neighbor.y); if (existingNode && current2.g + 1 < existingNode.g) { existingNode.g = current2.g + 1; existingNode.f = existingNode.g + existingNode.h; existingNode.parent = current2; heap2.update(existingNode); } } }); } return null; } function getNeighbors(node: Node, map: number[][]): Node[] { const neighbors: Node[] = []; const { x, y } = node; if (y > 0 && map[y - 1][x] !== 1) neighbors.push({ x, y: y - 1, f: 0, g: 0, h: 0, parent: null }); if (y < map.length - 1 && map[y + 1][x] !== 1) neighbors.push({ x, y: y + 1, f: 0, g: 0, h: 0, parent: null }); if (x > 0 && map[y][x - 1] !== 1) neighbors.push({ x: x - 1, y, f: 0, g: 0, h: 0, parent: null }); if (x < map[0].length - 1 && map[y][x + 1] !== 1) neighbors.push({ x: x + 1, y, f: 0, g: 0, h: 0, parent: null }); return neighbors; } function manhattanDistance(node1: Node, node2: Node): number { return Math.abs(node1.x - node2.x) + Math.abs(node1.y - node2.y); } class BinaryHeap<T> { private heap: T[] = []; private compareFn: (a: T, b: T) => boolean; constructor(compareFn: (a: T, b: T) => boolean) { this.compareFn = compareFn; } size(): number { return this.heap.length; } push(item: T): void { this.heap.push(item); this.bubbleUp(this.heap.length - 1); } pop(): T | undefined { if (this.heap.length === 0) return undefined; const root = this.heap[0]; const last = this.heap.pop()!; if (this.heap.length > 0) { this.heap[0] = last; this.bubbleDown(0); } return root; } update(item: T): void { const index = this.heap.indexOf(item); if (index >= 0) { this.bubbleUp(index); this.bubbleDown(index); } } private bubbleUp(index: number): void { const item = this.heap[index]; while (index > 0) { const parentIndex = Math.floor((index - 1) / 2); const parent = this.heap[parentIndex]; if (this.compareFn(item, parent)) { this.heap[index] = parent; index = parentIndex; } else break; } this.heap[index] = item; } private bubbleDown(index: number): void { const item = this.heap[index]; const length = this.heap.length; while (index < length) { const leftIndex = index * 2 + 1; const rightIndex = index * 2 + 2; let leftChild: T, rightChild: T; let swapIndex = -1; if (leftIndex < length) { leftChild = this.heap[leftIndex]; if (this.compareFn(leftChild, item)) swapIndex = leftIndex; } if (rightIndex < length) { rightChild = this.heap[rightIndex]; if (this.compareFn(rightChild, item) && this.compareFn(rightChild, leftChild)) swapIndex = rightIndex; } if (swapIndex === -1) break; this.heap[index] = this.heap[swapIndex]; index = swapIndex; } this.heap[index] = item; } } ``` 这里的 `Node` 接口定义了一个节点,包含了 x、y、f、g、h 和 parent 属性,分别表示节点的横纵坐标、总代价、从起点到该节点的代价、从该节点到终点的估价和父节点。`aStar` 函数实现了双向 A* 算法,其中使用两个优先队列(二叉堆)来维护两个方向的搜索。`getNeighbors` 函数用于获取一个节点的邻居节点。`manhattanDistance` 函数用于计算两个节点之间的曼哈顿距离。`BinaryHeap` 类是一个二叉堆的实现,用于实现优先队列。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值