當前位置:首頁 » 操作系統 » a星演算法計算

a星演算法計算

發布時間: 2023-08-23 07:34:30

1. 如何基於cocos2dx3.x實現A星尋路演算法

在學習本篇教程之前,如果你有cocos2d-x的開發經驗,將會有所幫助。如果沒有也沒關系,因為你可以將這里講解的例子遷移到其他的語言或者框架中。
找到到達你鍵盤的最短路徑,開始吧!
Maze貓
首先介紹下我們將要在本篇教程中開發的簡單游戲。
前往下載本篇教程的 工程代碼 。編譯運行工程,你將看到以下畫面。

在這款游戲中,你扮演著一隻小偷貓,在一個由危險的狗守護著的地牢里小心穿行。如果你試圖穿過一隻狗,他會把你吃掉 – 除非你可以用骨頭去賄賂它!
所以在這款游戲中,你的任務是嘗試以正確的順序撿起骨頭,然後 尋找路線 穿過狗逃離。
注意到貓只能水平或者垂直的移動(例如不能斜線移動),並且會從一個方塊的中心點移動到另一個中心點。每個方塊既可以是可通行的也可以是不可通行的。
嘗試下這款游戲,看看你能否找到出路!建議你閱讀代碼以熟悉它的原理。這是一款相當普通的方塊-地圖式游戲,我們會在接下來的教程中修改它並使用上A星尋路演算法。
Maze貓和A星概覽
正如你所看到的,當你點擊地圖某處時,貓會沿著你點擊的方向跳到相鄰的方塊上。
我們想對程序做修改,讓貓持續的往你點擊的方塊方向前進,就像許多RPGs或者point-and-click冒險類游戲。
讓我們看下控制觸摸事件代碼的工作原理。如果你打開HelloWorldScene.cpp文件,你將看到像下面這樣去實現觸摸操作:
auto listener = EventListenerTouchOneByOne::create();
listener->setSwallowTouches( true );
listener->onTouchBegan = [ this ](Touch *touch, Event *event){
if (_gameOver)
{
return false ;
}
Point touchLocation = _tileMap->convertTouchToNodeSpace(touch);
_cat->moveToward(touchLocation);
return true ;
};
_eventDispatcher->(listener, this );
你可以看到這里只是對貓精靈調用了一個方法,讓貓在方塊地圖上往你點擊的地方移動。
我們現在要做的是修改在CatSprite.m文件中的以下方法,尋找到達該點的最短路徑,並且開始前進:
void CatSprite::moveToward( const Point &target)
{
}
創建ShortestPathStep類
我們開始創建一個內部類,代表路徑上的一步操作。在這種情況下,它是一個方塊和由A星演算法計算出來的的F,G和H scores。
class ShortestPathStep : public cocos2d::Object
{
public :
ShortestPathStep();
~ShortestPathStep();
static ShortestPathStep *createWithPosition( const cocos2d::Point &pos);
bool initWithPosition( const cocos2d::Point &pos);
int getFScore() const ;
bool isEqual( const ShortestPathStep *other) const ;
std::string getDescription() const ;
CC_SYNTHESIZE(cocos2d::Point, _position, Position);
CC_SYNTHESIZE( int , _gScore, GScore);
CC_SYNTHESIZE( int , _hScore, HScore);
CC_SYNTHESIZE(ShortestPathStep*, _parent, Parent);
};
現在添加以下代碼到CatSprite.cpp文件的頂部。
CatSprite::ShortestPathStep::ShortestPathStep() :
_position(Point::ZERO),
_gScore(0),
_hScore(0),
_parent(nullptr)
{
}
CatSprite::ShortestPathStep::~ShortestPathStep()
{
}
CatSprite::ShortestPathStep *CatSprite::ShortestPathStep::createWithPosition( const Point &pos)
{
ShortestPathStep *pRet = new ShortestPathStep();
if (pRet && pRet->initWithPosition(pos))
{
pRet->autorelease();
return pRet;
}
else
{
CC_SAFE_DELETE(pRet);
return nullptr;
}
}
bool CatSprite::ShortestPathStep::initWithPosition( const Point &pos)
{
bool bRet = false ;
do
{
this ->setPosition(pos);
bRet = true ;
} while (0);
return bRet;
}
int CatSprite::ShortestPathStep::getFScore() const
{
return this ->getGScore() + this ->getHScore();
}
bool CatSprite::ShortestPathStep::isEqual( const CatSprite::ShortestPathStep *other) const
{
return this ->getPosition() == other->getPosition();
}
std::string CatSprite::ShortestPathStep::getDescription() const
{
return StringUtils::format( "pos=[%.0f;%.0f] g=%d h=%d f=%d" ,
this ->getPosition().x, this ->getPosition().y,
this ->getGScore(), this ->getHScore(), this ->getFScore());
}
正如所見,這是一個很簡單的類,記錄了以下內容:
- 方塊的坐標
- G值(記住,這是開始點到當前點的方塊數量)
- H值(記住,這是當前點到目標點的方塊估算數量)
- Parent是它的上一步操作
- F值,這是方塊的和值(它是G+H的值)
這里定義了getDescription方法,以方便調試。創建了isEquals方法,當且僅當兩個ShortestPathSteps的方塊坐標相同時,它們相等(例如它們代表著相同的方塊)。
創建Open和Closed列表
打開CatSprite.h文件,添加如下代碼:
cocos2d::Vector _spOpenSteps;
cocos2d::Vector _spClosedSteps;
檢查開始和結束點
重新實現moveToward方法,獲取當前方塊坐標和目標方塊坐標,然後檢查是否需要計算一條路徑,最後測試目標方塊坐標是否可行走的(在這里只有牆壁是不可行走的)。打開CatSprite.cpp文件,修改moveToward方法,為如下:
void CatSprite::moveToward( const Point &target)
{
Point fromTileCoord = _layer->tileCoordForPosition( this ->getPosition());
Point toTileCoord = _layer->tileCoordForPosition(target);
if (fromTileCoord == toTileCoord)
{
CCLOG( "You're already there! :P" );
return ;
}
if (!_layer->isValidTileCoord(toTileCoord) || _layer->isWallAtTileCoord(toTileCoord))
{
SimpleAudioEngine::getInstance()->playEffect( "hitWall.wav" );
return ;
}
CCLOG( "From: %f, %f" , fromTileCoord.x, fromTileCoord.y);
CCLOG( "To: %f, %f" , toTileCoord.x, toTileCoord.y);
}
編譯運行,在地圖上進行點擊,如果不是點擊到牆壁的話,可以在控制台看到如下信息:
From: 24.000000, 0.000000
To: 20.000000, 0.000000
其中 **From** 就是貓的方塊坐標,**To**就是所點擊的方塊坐標。
實現A星演算法
根據演算法,第一步是添加當前坐標到open列表。還需要三個輔助方法:
- 一個方法用來插入一個ShortestPathStep對象到適當的位置(有序的F值)
- 一個方法用來計算從一個方塊到相鄰方塊的移動數值
- 一個方法是根據"曼哈頓距離"演算法,計算方塊的H值
打開CatSprite.cpp文件,添加如下方法:
void CatSprite::insertInOpenSteps(CatSprite::ShortestPathStep *step)
{
int stepFScore = step->getFScore();
ssize_t count = _spOpenSteps.size();
ssize_t i = 0;
for (; i < count; ++i)
{
if (stepFScore <= _spOpenSteps.at(i)->getFScore())
{
break ;
}
}
_spOpenSteps.insert(i, step);
}
int CatSprite::computeHScoreFromCoordToCoord( const Point &fromCoord, const Point &toCoord)
{
// 忽略了可能在路上的各種障礙
return abs(toCoord.x - fromCoord.x) + abs(toCoord.y - fromCoord.y);
}
int CatSprite::( const ShortestPathStep *fromStep, const ShortestPathStep *toStep)
{
// 因為不能斜著走,而且由於地形就是可行走和不可行走的成本都是一樣的
// 如果能夠對角移動,或者有沼澤、山丘等等,那麼它必須是不同的
return 1;
}
接下來,需要一個方法去獲取給定方塊的所有相鄰可行走方塊。因為在這個游戲中,HelloWorld管理著地圖,所以在那裡添加方法。打開HelloWorldScene.cpp文件,添加如下方法:
PointArray *HelloWorld::( const Point &tileCoord) const
{
PointArray *tmp = PointArray::create(4);
// 上
Point p(tileCoord.x, tileCoord.y - 1);
if ( this ->isValidTileCoord(p) && ! this ->isWallAtTileCoord(p))
{
tmp->addControlPoint(p);
}
// 左
p.setPoint(tileCoord.x - 1, tileCoord.y);
if ( this ->isValidTileCoord(p) && ! this ->isWallAtTileCoord(p))
{
tmp->addControlPoint(p);
}
// 下
p.setPoint(tileCoord.x, tileCoord.y + 1);
if ( this ->isValidTileCoord(p) && ! this ->isWallAtTileCoord(p))
{
tmp->addControlPoint(p);
}
// 右
p.setPoint(tileCoord.x + 1, tileCoord.y);
if ( this ->isValidTileCoord(p) && ! this ->isWallAtTileCoord(p))
{
tmp->addControlPoint(p);
}
return tmp;
}
可以繼續CatSprite.cpp中的moveToward方法了,在moveToward方法的後面,添加如下代碼:
bool pathFound = false ;
_spOpenSteps.clear();
_spClosedSteps.clear();
// 首先,添加貓的方塊坐標到open列表
this ->insertInOpenSteps(ShortestPathStep::createWithPosition(fromTileCoord));
do
{
// 得到最小的F值步驟
// 因為是有序列表,第一個步驟總是最小的F值
ShortestPathStep *currentStep = _spOpenSteps.at(0);
// 添加當前步驟到closed列表
_spClosedSteps.pushBack(currentStep);
// 將它從open列表裡面移除
// 需要注意的是,如果想要先從open列表裡面移除,應小心對象的內存
_spOpenSteps.erase(0);
// 如果當前步驟是目標方塊坐標,那麼就完成了
if (currentStep->getPosition() == toTileCoord)
{
pathFound = true ;
ShortestPathStep *tmpStep = currentStep;
CCLOG( "PATH FOUND :" );
do
{
CCLOG( "%s" , tmpStep->getDescription().c_str());
tmpStep = tmpStep->getParent(); // 倒退
} while (tmpStep); // 直到沒有上一步
_spOpenSteps.clear();
_spClosedSteps.clear();
break ;
}
// 得到當前步驟的相鄰方塊坐標
PointArray *adjSteps = _layer->(currentStep->getPosition());
for (ssize_t i = 0; i < adjSteps->count(); ++i)
{
ShortestPathStep *step = ShortestPathStep::createWithPosition(adjSteps->getControlPointAtIndex(i));
// 檢查步驟是不是已經在closed列表
if ( this ->getStepIndex(_spClosedSteps, step) != -1)
{
continue ;
}
// 計算從當前步驟到此步驟的成本
int moveCost = this ->(currentStep, step);
// 檢查此步驟是否已經在open列表
ssize_t index = this ->getStepIndex(_spOpenSteps, step);
// 不在open列表,添加它
if (index == -1)
{
// 設置當前步驟作為上一步操作
step->setParent(currentStep);
// G值等同於上一步的G值 + 從上一步到這里的成本
step->setGScore(currentStep->getGScore() + moveCost);
// H值即是從此步驟到目標方塊坐標的移動量估算值
step->setHScore( this ->computeHScoreFromCoordToCoord(step->getPosition(), toTileCoord));
// 按序添加到open列表
this ->insertInOpenSteps(step);
}
else
{
// 獲取舊的步驟,其值已經計算過
step = _spOpenSteps.at(index);
// 檢查G值是否低於當前步驟到此步驟的值
if ((currentStep->getGScore() + moveCost) < step->getGScore())
{
// G值等同於上一步的G值 + 從上一步到這里的成本
step->setGScore(currentStep->getGScore() + moveCost);
// 因為G值改變了,F值也會跟著改變
// 所以為了保持open列表有序,需要將此步驟移除,再重新按序插入
// 在移除之前,需要先保持引用
step->retain();
// 現在可以放心移除,不用擔心被釋放
_spOpenSteps.erase(index);
// 重新按序插入
this ->insertInOpenSteps(step);
// 現在可以釋放它了,因為open列表應該持有它
step->release();
}
}
}
} while (_spOpenSteps.size() > 0);
if (!pathFound)
{
SimpleAudioEngine::getInstance()->playEffect( "hitWall.wav" );
}
添加以下方法:
ssize_t CatSprite::getStepIndex( const cocos2d::Vector &steps, const CatSprite::ShortestPathStep *step)
{
for (ssize_t i = 0; i < steps.size(); ++i)
{
if (steps.at(i)->isEqual(step))
{
return i;
}
}
return -1;
}
編譯運行,在地圖上進行點擊,如下圖所示:

From: 24.000000, 0.000000
To: 23.000000, 3.000000
PATH FOUND :
pos=[23;3] g=10 h=0 f=10
pos=[22;3] g=9 h=1 f=10
pos=[21;3] g=8 h=2 f=10
pos=[20;3] g=7 h=3 f=10
pos=[20;2] g=6 h=4 f=10
pos=[20;1] g=5 h=5 f=10
pos=[21;1] g=4 h=4 f=8
pos=[22;1] g=3 h=3 f=6
pos=[23;1] g=2 h=2 f=4
pos=[24;1] g=1 h=3 f=4
pos=[24;0] g=0 h=0 f=0
注意該路徑是從後面建立的,所以必須從下往上看貓選擇了哪條路徑。
跟隨路徑前進
現在已經找到了路徑,只需讓貓跟隨前進即可。需要創建一個數組去存儲路徑,打開CatSprite.h文件,添加如下代碼:
cocos2d::Vector _shortestPath;
打開CatSprite.cpp文件,更改moveToward方法,注釋掉語句**bool pathFound = false**;,如下:
//bool pathFound = false;
替換語句**pathFound = true;**為如下:
//pathFound = true;
this ->(currentStep);
並且注釋掉下方的調試語句:
//ShortestPathStep *tmpStep = currentStep;
//CCLOG("PATH FOUND :");
//do
//{
// CCLOG("%s", tmpStep->getDescription().c_str());
// tmpStep = tmpStep->getParent(); // 倒退
/

2. 搜索演算法中,A演算法A*演算法的區別(急)

a*演算法:a*(a-star)演算法是一種靜態路網中求解最短路徑最有效的直接搜索方法。估價值與實際值越接近,估價函數取得就越好
a*
(a-star)演算法是一種靜態路網中求解最短路最有效的直接搜索方法。
注意是最有效的直接搜索演算法。之後涌現了很多預處理演算法(alt,ch,hl等等),在線查詢效率是a*演算法的數千甚至上萬倍。
公式表示為:
f(n)=g(n)+h(n),
其中
f(n)
是從初始點經由節點n到目標點的估價函數,
g(n)
是在狀態空間中從初始節點到n節點的實際代價,
h(n)
是從n到目標節點最佳路徑的估計代價。
保證找到最短路徑(最優解的)條件,關鍵在於估價函數f(n)的選取:
估價值h(n)<=
n到目標節點的距離實際值,這種情況下,搜索的點數多,搜索范圍大,效率低。但能得到最優解。並且如果h(n)=d(n),即距離估計h(n)等於最短距離,那麼搜索將嚴格沿著最短路徑進行,
此時的搜索效率是最高的。
如果
估價值>實際值,搜索的點數少,搜索范圍小,效率高,但不能保證得到最優解。

3. 計算機求百錢買百雞問題採用的演算法是

演算法如下:

int main()

{

int x, y, z;

for (int k = 1; k <= 3; k++)

{

x = 4 * k;

y = 25 - 7 * k;

z = 75 + 3 * k;

printf("公雞:%d只,母雞:%d只,小雞:%d只 ", x, y, z);

(3)a星演算法計算擴展閱讀:

A*搜尋演算法

俗稱A星演算法。這是一種在圖形平面上,有多個節點的路徑,求出最低通過成本的演算法。常用於游戲中的NPC的移動計算,或線上游戲的BOT的移動計算上。該演算法像Dijkstra演算法一樣,可以找到一條最短路徑;也像BFS一樣,進行啟發式的搜索。

Beam Search

束搜索(beam search)方法是解決優化問題的一種啟發式方法,它是在分枝定界方法基礎上發展起來的,它使用啟發式方法估計k個最好的路徑,僅從這k個路徑出發向下搜索,即每一層只有滿意的結點會被保留,其它的結點則被永久拋棄,從而比分枝定界法能大大節省運行時間。

束搜索於20 世紀70年代中期首先被應用於人工智慧領域,1976 年Lowerre在其稱為HARPY的語音識別系統中第一次使用了束搜索方法。他的目標是並行地搜索幾個潛在的最優決策路徑以減少回溯,並快速地獲得一個解。

二分取中查找演算法

一種在有序數組中查找某一特定元素的搜索演算法。搜索過程從數組的中間元素開始,如果中間元素正好是要查找的元素,則搜索過程結束;

如果某一特定元素大於或者小於中間元素,則在數組大於或小於中間元素的那一半中查找,而且跟開始一樣從中間元素開始比較。這種搜索演算法每一次比較都使搜索范圍縮小一半。

4. 矩陣a*演算法是什麼

矩陣A*表示A矩陣的伴隨矩陣。

伴隨矩陣的定義:某矩陣A各元素的代數餘子式,組成一個新的矩陣後再進行一下轉置,叫做A的伴隨矩陣。

某元素代數餘子式就是去掉矩陣中某元素所在行和列元素後的形成矩陣的行列式,再乘上-1的(行數+列數)次方。

伴隨矩陣的求發:當矩陣是大於等於二階時:

主對角元素是將原矩陣該元素所在行列去掉再求行列式。

非主對角元素是原矩陣該元素的共軛位置的元素去掉所在行列求行列式乘以(-1)^(x+y) x,y為該元素的共軛位置的元素的行和列的序號,序號從1開始的。

主對角元素實際上是非主對角元素的特殊情況,因為x=y,所以(-1)^(x+y)=(-1)^(2x)=1,一直是正數,沒必要考慮主對角元素的符號問題。

5. 人工智慧 A*演算法原理

A 演算法是啟發式演算法重要的一種,主要是用於在兩點之間選擇一個最優路徑,而A 的實現也是通過一個估值函數

上圖中這個熊到樹葉的 曼哈頓距離 就是藍色線所表示的距離,這其中不考慮障礙物,假如上圖每一個方格長度為1,那麼此時的熊的曼哈頓距離就為9.
起點(X1,Y1),終點(X2,Y2),H=|X2-X1|+|Y2-Y1|
我們也可以通過幾何坐標點來算出曼哈頓距離,還是以上圖為例,左下角為(0,0)點,熊的位置為(1,4),樹葉的位置為(7,1),那麼H=|7-1|+|1-4|=9。

還是以上圖為例,比如剛開始熊位置我們會加入到CLOSE列表中,而熊四周它可以移動到的點位我們會加入到OPEN列表中,並對熊四周的8個節點進行F=G+H這樣的估值運算,然後在這8個節點中選中一個F值為最小的節點,然後把再把這個節點從OPEN列表中刪除,加入到Close列表中,從接著在對這個節點的四周8個節點進行一個估值運算,再接著依次運算,這樣說大家可能不是太理解,我會在下邊做詳細解釋。

從起點到終點,我們通過A星演算法來找出最優路徑

我們把每一個方格的長度定義為1,那從起始點到5位置的代價就是1,到3的代價為1.41,定義好了我們接著看上圖,接著運算

第一步我們會把起始點四周的點加入OPEN列表中然後進行一個估值運算,運算結果如上圖,這其中大家看到一個小箭頭都指向了起點,這個箭頭就是指向父節點,而open列表的G值都是根據這個進行計算的,意思就是我從上一個父節點運行到此處時所需要的總代價,如果指向不一樣可能G值就不一樣,上圖中我們經過計算發現1點F值是7.41是最小的,那我們就選中這個點,並把1點從OPEN列表中刪除,加入到CLOSE列表中,但是我們在往下運算的時候發現1點的四周,2點,3點和起始點這三個要怎麼處理,首先起始點已經加入到了CLOSE,他就不需要再進行這種運算,這就是CLOSE列表的作用,而2點和3點我們也可以對他進行運算,2點的運算,我們從1移動到2點的時候,他需要的代價也就是G值會變成2.41,而H值是不會變的F=2.41+7=9.41,這個值我們發現大於原來的的F值,那我們就不能對他進行改變(把父節點指向1,把F值改為9.41,因為我們一直追求的是F值最小化),3點也同理。

在對1點四周進行運算後整個OPEN列表中有兩個點2點和3點的F值都是7.41,此時我們系統就可能隨機選擇一個點然後進行下一步運算,現在我們選中的是3點,然後對3點的四周進行運算,結果是四周的OPEN點位如果把父節點指向3點值時F值都比原來的大,所以不發生改變。我們在看整個OPEN列表中,也就2點的7.41值是最小的,那我們就選中2點接著運算。

我們在上一部運算中選中的是1點,上圖沒有把2點加入OPEN列表,因為有障礙物的阻擋從1點他移動不到2點,所以沒有把2點加入到OPEN列表中,整個OPEN列表中3的F=8是最小的,我們就選中3,我們對3點四周進行運算是我們發現4點經過計算G=1+1=2,F=2+6=8所以此時4點要進行改變,F變為8並把箭頭指向3點(就是把4點的父節點變為3),如下圖

我們就按照這種方法一直進行運算,最後 的運算結果如下圖

而我們通過目標點位根據箭頭(父節點),一步一步向前尋找最後我們發現了一條指向起點的路徑,這個就是我們所需要的最優路徑。 如下圖的白色選中區域

但是我們還要注意幾點

最優路徑有2個

這是我對A*演算法的一些理解,有些地方可能有BUG,歡迎大家指出,共同學習。

熱點內容
php表單自動提交 發布:2025-03-07 08:56:09 瀏覽:504
安卓怎麼連接電腦用滑鼠 發布:2025-03-07 08:52:55 瀏覽:311
大數據與資料庫的關系 發布:2025-03-07 08:48:20 瀏覽:288
取冪C語言 發布:2025-03-07 08:43:10 瀏覽:488
高考解壓性 發布:2025-03-07 08:43:10 瀏覽:690
搜狐廣告伺服器是什麼 發布:2025-03-07 08:36:45 瀏覽:147
csgo穩定fps要什麼配置 發布:2025-03-07 08:35:01 瀏覽:404
matlab粒子群優化演算法 發布:2025-03-07 08:13:49 瀏覽:249
編譯原理翻譯 發布:2025-03-07 08:08:01 瀏覽:592
安卓光遇測試服為什麼伺服器錯誤 發布:2025-03-07 08:05:53 瀏覽:551