天神降临,大家过来膜拜吧!
oh yeah!
转载请声明出处,例子代码可以免费随意使用,但请保留或注明作者信息.
这里的算法说是终极优化, 我挑战了一下,
http://eidiot.net/2007/04/17/a-star-pathfinding/
最终结果比较他快三倍, 我站在高高处,藐视了 一下作者.
优化思路:
a.开包(open list)算法优化:
线性查找-->
二叉堆(binary heap 容器:Array->Vector)->
二分排序插入/删除(容器:Array)
b. 障碍点容器优化:
dictionary(1维) ->dictionary(2维)-- array(1维)->array(2维)->bitmapdata->bytearray(8位对齐)->byteArrayarray(32位对齐)->vector. <int>
c. 闭包(close list)容器优化:
dictionary(1维) -> array(1维)->bitmapdata->vector. <int>
d. 过程生成的路点对象内存管理优化:
AS动态对象(Object)->非动态对象(自定义PathNode对象)->使用bytearray自己管理内存分配对象-->使用Vector自已管理内存分配对象.
e. 评估函数:
标准(F = G+H, G=Parent.G+DIR_G, H = (ABS(X1-X)+ABS(Y1-Y) )*10 )
上述优化基本上是 左->右 == 慢->快
测试结果部分
单位:毫秒
地图大小:500x500地图:
从41条测试线路的平均值结果, 足足快3倍, 比较未优化但是快了25倍以上
未优化 10886 ms
所谓终极优化 1361.414634 ms
二分插入排序 ByteArray做内存管理 (32bit对齐 ) 479.195122 ms
二分插入排序Vector做内存管理 447.2439024 ms
startX | startY | endX | endY | 距离 | 未优化 | 所谓 终极 优化 | 二分插入排序ByteArray做内存管理(32bit对齐) | 二分插入排序 Vector做内存管理 |
36 | 32 | 113 | 125 | 120.7394 | 17 | 8 | 10 | 11 |
113 | 125 | 34 | 29 | 124.3262 | 16 | 7 | 11 | 11 |
34 | 29 | 469 | 463 | 614.4762 | 9133 | 1338 | 544 | 493 |
469 | 463 | 152 | 155 | 441.9876 | 超时 | 8536 | 2810 | 2560 |
152 | 155 | 169 | 165 | 19.72308 | 17763 | 2743 | 957 | 894 |
169 | 165 | 465 | 463 | 420.0238 | 1159 | 219 | 101 | 97 |
465 | 463 | 165 | 143 | 438.6342 | 超时 | 8385 | 2594 | 2470 |
165 | 143 | 220 | 146 | 55.08176 | 19685 | 2958 | 1031 | 965 |
220 | 146 | 229 | 139 | 11.40175 | 725 | 234 | 124 | 116 |
229 | 139 | 111 | 477 | 358.0056 | 827 | 168 | 72 | 70 |
111 | 477 | 327 | 350 | 250.5694 | 1705 | 364 | 164 | 158 |
327 | 350 | 319 | 340 | 12.80625 | 210 | 71 | 38 | 38 |
319 | 340 | 464 | 477 | 199.4843 | 51 | 20 | 18 | 17 |
464 | 477 | 71 | 26 | 598.2057 | 20912 | 2370 | 801 | 774 |
71 | 26 | 465 | 468 | 592.1149 | 6158 | 922 | 362 | 348 |
465 | 468 | 119 | 188 | 445.1022 | 超时 | 7749 | 2499 | 2372 |
119 | 188 | 219 | 314 | 160.8602 | 3625 | 804 | 309 | 286 |
219 | 314 | 79 | 401 | 164.8302 | 28 | 11 | 10 | 12 |
79 | 401 | 229 | 309 | 175.9659 | 141 | 35 | 24 | 23 |
229 | 309 | 91 | 432 | 184.8594 | 41 | 13 | 13 | 12 |
91 | 432 | 65 | 446 | 29.52965 | 2 | 1 | 7 | 6 |
65 | 446 | 227 | 310 | 211.5183 | 100 | 21 | 16 | 14 |
227 | 310 | 62 | 444 | 212.5582 | 78 | 15 | 13 | 14 |
62 | 444 | 354 | 26 | 509.8902 | 644 | 89 | 38 | 36 |
354 | 26 | 479 | 27 | 125.004 | 32 | 14 | 14 | 13 |
479 | 27 | 27 | 469 | 632.193 | 352 | 48 | 29 | 29 |
27 | 469 | 10 | 18 | 451.3203 | 3733 | 936 | 384 | 354 |
10 | 18 | 473 | 463 | 642.1791 | 9152 | 1331 | 534 | 493 |
473 | 463 | 100 | 260 | 424.6622 | 5947 | 782 | 268 | 255 |
100 | 260 | 234 | 152 | 172.1046 | 2182 | 531 | 241 | 226 |
234 | 152 | 248 | 135 | 22.02272 | 超时 | 0 | 113 | 0 |
234 | 152 | 249 | 137 | 21.2132 | 598 | 193 | 113 | 104 |
249 | 137 | 120 | 234 | 161.4001 | 5200 | 951 | 401 | 377 |
120 | 234 | 154 | 203 | 46.01087 | 1557 | 418 | 214 | 201 |
154 | 203 | 404 | 237 | 252.3014 | 4173 | 882 | 357 | 335 |
404 | 237 | 11 | 15 | 451.3679 | 8876 | 1331 | 572 | 543 |
11 | 15 | 230 | 409 | 450.7738 | 55030 | 7070 | 2308 | 2179 |
230 | 409 | 240 | 97 | 312.1602 | 1541 | 325 | 161 | 152 |
240 | 97 | 25 | 19 | 228.7116 | 1819 | 375 | 181 | 167 |
25 | 19 | 148 | 156 | 184.1141 | 29 | 10 | 12 | 9 |
148 | 156 | 169 | 188 | 38.27532 | 23085 | 3540 | 1179 | 1103 |
平均ms | 10886 | 1361.415 | 479.2 | 447 |
代码:FindPathImplVetorMem.as
/**
@author aerror
*/
package
{
import flash.utils.ByteArray;
import flash.utils.Endian;
public class FindPathImplVetorMem
{
public static const BLOCK_TYPE_NULL:int = 0;
public static const BLOCK_TYPE_STATIC:int = 1;
public static const BLOCK_TYPE_ENTRY:int = 2;
public static const BLOCK_TYPE_ITEM:int = 4;
private var obstacles:Vector.<int>;
private var iwidth:uint;
private var iheight:uint;
private var nearby:Vector.<int>;
private var nearbyStraight:Vector.<int>;
private var nearbyCross:Vector.<int>;
private static const STATE_NULL:uint = 0;
private static const STATE_CLOSE:uint = 1;
/**
*
*
* */
public function FindPathImplVetorMem()
{
nearbyStraight = new Vector.<int>();
nearbyStraight.push(
-1,-1,14,
0,-1,10,
1,-1,14,
1,0,10,
1,1,14,
0,1,10,
-1,1,14,
-1,0,10
);
nearbyCross = new Vector.<int>();
nearbyCross.push(
-1,-1,10,
0,-1,14,
1,-1,10,
1,0,14,
1,1,10,
0,1,14,
-1,1,10,
-1,0,14
);
nearby = nearbyStraight;
}
public function initSys():void
{
}
public function clearBlock():void
{
if(obstacles!=null)
{
//obstacles.dispose();
obstacles = null;
}
}
public function initMap(_mapCol:int, _mapRow:int, bytes:ByteArray):void
{
clearBlock();
this.iwidth = _mapCol;
this.iheight = _mapRow;
obstacles = new Vector.<int>(iwidth*iheight);
var num:int = bytes.length/6;
for(var i:int=0;i<num;i++)
{
var x:int= bytes.readUnsignedShort();
var y:int= bytes.readUnsignedShort();
var t:int = bytes.readUnsignedShort();
obstacles[x+y*_mapRow] = t;
if(t==0)
{
trace("BLOCK_TYPE_NULL inited");
return;
}
}
}
public function checkBlock(cellX:int, cellY:int, cellType:int):Boolean
{
if(obstacles!=null && obstacles[cellX + cellY*iwidth] ==cellType)
{
return true;
}
return false;
}
public function addBlock(cellX:int, cellY:int,blockType:int):void
{
if(obstacles==null)
{
return;
}
obstacles[cellX + cellY*iwidth] = blockType;
}
public function removeBlock(cellX:int, cellY:int,blockType:int):void
{
if(obstacles==null )
{
return;
}
obstacles[cellX + cellY*iwidth] = BLOCK_TYPE_NULL;
}
public function findPath(startX:int, startY:int, endX:int, endY:int, distance:int ):ByteArray
{
var ret:ByteArray = new ByteArray();
ret.endian = Endian.LITTLE_ENDIAN;
var width:uint = iwidth;
var height:uint = iheight;
var nodelist:PathNodeFactoryVector = new PathNodeFactoryVector(width * height);
var key:uint;
var opneListSortedArr:Array = new Array();
var opneNum:int=0;
var minIndex:uint = 0;
var stateMap:Vector.<uint>= new Vector.<uint>(width * height);
var startPoint:uint = nodelist.createNode(startX,startY,0,0,PathNodeFactoryVector.NULL);
key = x + width * y;
opneListSortedArr.push(startPoint);
stateMap[key]=startPoint;
opneNum = 1;
minIndex = 0;
var found_end_point:uint = PathNodeFactoryVector.NULL;
var x:int;
var y:int;
var g:int;
var h:Number;
var f:int;
var i:int;
var min:uint = PathNodeFactoryVector.NULL;
while(true)
{
var min_key:uint;
var min_x:uint;
var min_y:uint;
var min_f:uint;
var min_g:uint;
//find the min g+h point
//
if(opneNum >0)
{
//remove from openlist;
min = opneListSortedArr.shift();
min_x = nodelist.getNodeX(min);
min_y = nodelist.getNodeY(min);
min_g = nodelist.getNodeG(min);
min_key = min_x + width * min_y;
opneNum--;
}
else
{
trace("FIND PATH FAILED");
break;
}
//add to close list;
stateMap[min_key] = STATE_CLOSE;// = min;
//add nearby point to openlist
for( i = 0;i<8;i++)
{
x = min_x + nearby[i*3];
y = min_y + nearby[i*3+1];
key = x + width * y;
//calc h
//
h = ((endX<x)?(x-endX):(endX-x)) +
((endY<y)?(y-endY):(endY-y));
//we find the path when openlist contains the endpoint
//
if(h<=distance)
{
found_end_point = nodelist.createNode(x,y,0,0,min);
break;
}
h = h*10;
if(x >= width || x < 0 //out of bounds ?
|| y >= height || y < 0//out of bounds ?
|| obstacles[key]!=BLOCK_TYPE_NULL //is obstacle ?
)
{
continue;
}
// if in close list?
var state:uint = stateMap[key];
if(state==STATE_CLOSE)
{
continue;
}
//calc g
//
g = (nearby[i*3+2]+min_g);
f = g+h;
//check if new point or old
//
var newPt:uint;
//the old point
//key key its g value,update if new g is less
//
if(state==STATE_NULL)
{
//the new point
//
newPt = nodelist.createNode(x,y,g,f,min);
stateMap[key] = newPt;
nodelist.sortInsert(opneListSortedArr,newPt,0,opneNum);
opneNum++;
}
else if(g<nodelist.getNodeG(state))
{
newPt = nodelist.createNode(x,y,g,f,min);
nodelist.sortRemove(opneListSortedArr,state,0,opneNum);
stateMap[key] = newPt;
nodelist.sortInsert(opneListSortedArr,newPt,0,opneNum-1);
}
}
//just break the upper loop
//
if(found_end_point !=PathNodeFactoryVector.NULL)
{
break;
}
}
//stateMap.dispose();
//stateMap =null;
//encode it to bytearray. :(
//
if(found_end_point !=PathNodeFactoryVector.NULL)
{
var pt:uint = found_end_point;
var num:int=0;
while(pt!=PathNodeFactoryVector.NULL)
{
num++;
pt =nodelist.getNodeParent(pt);
}
ret.length = num*4;
pt = found_end_point;
i =0;
while(pt!=PathNodeFactoryVector.NULL)
{
ret.position = (num-i-1)*4;
ret.writeShort(nodelist.getNodeX(pt));
ret.writeShort(nodelist.getNodeY(pt));
i++;
pt = nodelist.getNodeParent(pt);
}
ret.position = 0;
}
//clear used memory
//
trace("used length :" + nodelist.getUsedLength() + "open num" + opneListSortedArr.length);
nodelist.clear();
stateMap.length = 0;
opneListSortedArr.length =0;
return ret;
}
}
}
PathNodeFactoryVector.as
/**
@author aerror
*/
package
{
public class PathNodeFactoryVector
{
public static const NULL:uint = 0;
private var mem:Vector.<uint>;
private var writePosition:uint =0;
public function PathNodeFactoryVector(max:uint)
{
mem = new Vector.<uint>(max*4+4);
writePosition = 4;
}
public function getNodeX(id:uint):uint
{
return mem[id]&0xffff;
}
public function getNodeY(id:uint):uint
{
return mem[id]>>16 &0xffff;
}
public function getNodeG(id:uint):uint
{
return mem[id+1];
}
public function getNodeF(id:uint):uint
{
return mem[id+2];
}
public function getNodeParent(id:uint):uint
{
return mem[id+3];
}
public function createNode(_x:uint,_y:uint,_g:uint, _f:uint,_linkParent:uint):uint
{
mem[writePosition + 0] = (_x | _y<<16 );
mem[writePosition + 1]= _g;
mem[writePosition + 2]= _f;
mem[writePosition + 3]= _linkParent;
writePosition += 4;
return writePosition-4;
}
public function checkOK(arr:Array):Boolean
{
for(var i:int=0;i < arr.length-1;i++)
{
if(getNodeF(arr[i]) >getNodeF(arr[i+1]))
{
return false;
}
}
return true;
}
public function sortInsert(arr:Array, newPt:uint, begin:int, end:int):void
{
//optimize for empty array
if(arr.length==0)
{
arr.push(newPt);
return;
}
var new_F:uint = getNodeF(newPt);
var cur_F:uint = getNodeF(arr[begin]);
//optimize for sorted inserting with less
if(new_F <= cur_F )
{
arr.splice(0,0,newPt);
return;
}
//optimize for sorted inserting with larger
cur_F = getNodeF(arr[end-1]);
if(new_F >cur_F)
{
arr.splice(end,0,newPt);
return;
}
else if(new_F == cur_F)
{
arr.splice(end-1,0,newPt);
return;
}
while(true)
{
if(begin==end)
{
cur_F = getNodeF(arr[begin]);
if(new_F > cur_F)
arr.splice(begin+1,0,newPt);
else
arr.splice(begin,0,newPt);
return ;
}
//find a place
var mid:int = begin+(end-begin)/2;
cur_F =getNodeF(arr[mid]);
if(new_F < cur_F)
{
end = mid;
}
else if(new_F > cur_F)
{
begin = mid +1;
}
else
{
arr.splice(mid,0,newPt);
return;
}
}
}
public function sortRemove(arr:Array, oldPt:uint, begin:int, end:int):void
{
var old_f:uint = getNodeF(oldPt);
var i:int;
while(true)
{
//find a place
var mid:int = begin+(end-begin)/2;
var cur_f:uint = getNodeF(arr[mid]);
if(old_f < cur_f)
{
end = mid;
}
else if(old_f > cur_f)
{
begin = mid +1;
}
else
{
for(i =mid; i<end ; i++)
{
if(oldPt==arr[i])
{
arr.splice(i,1);
return ;
}
}
for(i = mid-1; i>=0 ; i--)
{
if(oldPt==arr[i])
{
arr.splice(i,1);
return ;
}
}
return ;
}
}
}
public function clear():void
{
this.mem.length = 0;
}
public function getUsedLength():uint
{
return this.writePosition;
}
public function dumpArray(opneListSortedArr:Array):void
{
for(var j:int =0;j<opneListSortedArr.length;j++)
{
trace(getNodeF(opneListSortedArr[j]));
}
}
}
}