A*(A星)算法Go lang實(shí)現(xiàn)

a*

A算法共虑,A(A-Star)算法是一種靜態(tài)路網(wǎng)中求解最短路徑最有效的直接搜索方法洞坑,也是解決許多搜索問題的有效算法之众。算法中的距離估算值與實(shí)際值越接近拙毫,最終搜索速度越快。
A* (A-Star)算法是一種靜態(tài)路網(wǎng)中求解最短路徑最有效的直接搜索方法酝枢,也是許多其他問題的常用啟發(fā)式算法恬偷。注意——是最有效的直接搜索算法悍手,之后涌現(xiàn)了很多預(yù)處理算法(如ALT帘睦,CH,HL等等)坦康,在線查詢效率是A*算法的數(shù)千甚至上萬倍竣付。
公式表示為: f(n)=g(n)+h(n),
其中, f(n) 是從初始狀態(tài)經(jīng)由狀態(tài)n到目標(biāo)狀態(tài)的代價(jià)估計(jì)滞欠,
g(n) 是在狀態(tài)空間中從初始狀態(tài)到狀態(tài)n的實(shí)際代價(jià)古胆,
h(n) 是從狀態(tài)n到目標(biāo)狀態(tài)的最佳路徑的估計(jì)代價(jià)。
(對(duì)于路徑搜索問題筛璧,狀態(tài)就是圖中的節(jié)點(diǎn)逸绎,代價(jià)就是距離)
h(n)的選取
保證找到最短路徑(最優(yōu)解的)條件,關(guān)鍵在于估價(jià)函數(shù)f(n)的選蓉舶(或者說h(n)的選裙啄痢)。
我們以d(n)表達(dá)狀態(tài)n到目標(biāo)狀態(tài)的距離朗儒,那么h(n)的選取大致有如下三種情況:

  • 如果h(n)< d(n)到目標(biāo)狀態(tài)的實(shí)際距離颊乘,這種情況下参淹,搜索的點(diǎn)數(shù)多,搜索范圍大乏悄,效率低浙值。但能得到最優(yōu)解。
  • 如果h(n)=d(n)檩小,即距離估計(jì)h(n)等于最短距離开呐,那么搜索將嚴(yán)格沿著最短路徑進(jìn)行, 此時(shí)的搜索效率是最高的规求。
  • 如果 h(n)>d(n)负蚊,搜索的點(diǎn)數(shù)少,搜索范圍小颓哮,效率高家妆,但不能保證得到最優(yōu)解。

A*同樣可以用于其他搜索問題冕茅,只需要對(duì)應(yīng)狀態(tài)和狀態(tài)的距離即可伤极。

package main

import (
        "container/heap"
        "fmt"
        "math"
        "strings"
)
import "strconv"

type OpenList []*_AstarPoint

func (self OpenList) Len() int           { return len(self) }
func (self OpenList) Less(i, j int) bool { return self[i].fVal < self[j].fVal }
func (self OpenList) Swap(i, j int)      { self[i], self[j] = self[j], self[i] }

func (this *OpenList) Push(x interface{}) {
        // Push and Pop use pointer receivers because they modify the slice's length,
        // not just its contents.
        *this = append(*this, x.(*_AstarPoint))
}

func (this *OpenList) Pop() interface{} {
        old := *this
        n := len(old)
        x := old[n-1]
        *this = old[0 : n-1]
        return x
}


type _Point struct {
        x    int
        y    int
        view string
}

//========================================================================================

// 保存地圖的基本信息
type Map struct {
        points [][]_Point
        blocks map[string]*_Point
        maxX   int
        maxY   int
}

func NewMap(charMap []string) (m Map) {
        m.points = make([][]_Point, len(charMap))
        m.blocks = make(map[string]*_Point, len(charMap)*2)
        for x, row := range charMap {
                cols := strings.Split(row, " ")
                m.points[x] = make([]_Point, len(cols))
                for y, view := range cols {
                        m.points[x][y] = _Point{x, y, view}
                        if view == "X" {
                                m.blocks[pointAsKey(x, y)] = &m.points[x][y]
                        }
                } // end of cols
        } // end of row

        m.maxX = len(m.points)
        m.maxY = len(m.points[0])

        return m
}

func (this *Map) getAdjacentPoint(curPoint *_Point) (adjacents []*_Point) {
        if x, y := curPoint.x, curPoint.y-1; x >= 0 && x < this.maxX && y >= 0 && y < this.maxY {
                adjacents = append(adjacents, &this.points[x][y])
        }
        if x, y := curPoint.x+1, curPoint.y-1; x >= 0 && x < this.maxX && y >= 0 && y < this.maxY {
                adjacents = append(adjacents, &this.points[x][y])
        }
        if x, y := curPoint.x+1, curPoint.y; x >= 0 && x < this.maxX && y >= 0 && y < this.maxY {
                adjacents = append(adjacents, &this.points[x][y])
        }
        if x, y := curPoint.x+1, curPoint.y+1; x >= 0 && x < this.maxX && y >= 0 && y < this.maxY {
                adjacents = append(adjacents, &this.points[x][y])
        }
        if x, y := curPoint.x, curPoint.y+1; x >= 0 && x < this.maxX && y >= 0 && y < this.maxY {
                adjacents = append(adjacents, &this.points[x][y])
        }
        if x, y := curPoint.x-1, curPoint.y+1; x >= 0 && x < this.maxX && y >= 0 && y < this.maxY {
                adjacents = append(adjacents, &this.points[x][y])
        }
        if x, y := curPoint.x-1, curPoint.y; x >= 0 && x < this.maxX && y >= 0 && y < this.maxY {
                adjacents = append(adjacents, &this.points[x][y])
        }
        if x, y := curPoint.x-1, curPoint.y-1; x >= 0 && x < this.maxX && y >= 0 && y < this.maxY {
                adjacents = append(adjacents, &this.points[x][y])
        }
        return adjacents
}

func (this *Map) PrintMap(path *SearchRoad) {
        fmt.Println("map's border:", this.maxX, this.maxY)
        for x := 0; x < this.maxX; x++ {
                for y := 0; y < this.maxY; y++ {
                        if path != nil {
                                if x == path.start.x && y == path.start.y {
                                        fmt.Print("S")
                                        goto NEXT
                                }
                                if x == path.end.x && y == path.end.y {
                                        fmt.Print("E")
                                        goto NEXT
                                }
                                for i := 0; i < len(path.TheRoad); i++ {
                                        if path.TheRoad[i].x == x && path.TheRoad[i].y == y {
                                                fmt.Print("*")
                                                goto NEXT
                                        }
                                }
                        }
                        fmt.Print(this.points[x][y].view)
                NEXT:
                }
                fmt.Println()
        }
}

func pointAsKey(x, y int) (key string) {
        key = strconv.Itoa(x) + "," + strconv.Itoa(y)
        return key
}

//========================================================================================

type _AstarPoint struct {
        _Point
        father *_AstarPoint
        gVal   int
        hVal   int
        fVal   int
}

func NewAstarPoint(p *_Point, father *_AstarPoint, end *_AstarPoint) (ap *_AstarPoint) {
        ap = &_AstarPoint{*p, father, 0, 0, 0}
        if end != nil {
                ap.calcFVal(end)
        }
        return ap
}

func (this *_AstarPoint) calcGVal() int {
        if this.father != nil {
                deltaX := math.Abs(float64(this.father.x - this.x))
                deltaY := math.Abs(float64(this.father.y - this.y))
                if deltaX == 1 && deltaY == 0 {
                        this.gVal = this.father.gVal + 10
                } else if deltaX == 0 && deltaY == 1 {
                        this.gVal = this.father.gVal + 10
                } else if deltaX == 1 && deltaY == 1 {
                        this.gVal = this.father.gVal + 14
                } else {
                        panic("father point is invalid!")
                }
        }
        return this.gVal
}

func (this *_AstarPoint) calcHVal(end *_AstarPoint) int {
        this.hVal = int(math.Abs(float64(end.x-this.x)) + math.Abs(float64(end.y-this.y)))
        return this.hVal
}

func (this *_AstarPoint) calcFVal(end *_AstarPoint) int {
        this.fVal = this.calcGVal() + this.calcHVal(end)
        return this.fVal
}

//========================================================================================

type SearchRoad struct {
        theMap  *Map
        start   _AstarPoint
        end     _AstarPoint
        closeLi map[string]*_AstarPoint
        openLi  OpenList
        openSet map[string]*_AstarPoint
        TheRoad []*_AstarPoint
}

func NewSearchRoad(startx, starty, endx, endy int, m *Map) *SearchRoad {
        sr := &SearchRoad{}
        sr.theMap = m
        sr.start = *NewAstarPoint(&_Point{startx, starty, "S"}, nil, nil)
        sr.end = *NewAstarPoint(&_Point{endx, endy, "E"}, nil, nil)
        sr.TheRoad = make([]*_AstarPoint, 0)
        sr.openSet = make(map[string]*_AstarPoint, m.maxX+m.maxY)
        sr.closeLi = make(map[string]*_AstarPoint, m.maxX+m.maxY)

        heap.Init(&sr.openLi)
        heap.Push(&sr.openLi, &sr.start) // 首先把起點(diǎn)加入開放列表
        sr.openSet[pointAsKey(sr.start.x, sr.start.y)] = &sr.start
        // 將障礙點(diǎn)放入關(guān)閉列表
        for k, v := range m.blocks {
                sr.closeLi[k] = NewAstarPoint(v, nil, nil)
        }

        return sr
}

func (this *SearchRoad) FindoutRoad() bool {
        for len(this.openLi) > 0 {
                // 將節(jié)點(diǎn)從開放列表移到關(guān)閉列表當(dāng)中。
                x := heap.Pop(&this.openLi)
                curPoint := x.(*_AstarPoint)
                delete(this.openSet, pointAsKey(curPoint.x, curPoint.y))
                this.closeLi[pointAsKey(curPoint.x, curPoint.y)] = curPoint

                //fmt.Println("curPoint :", curPoint.x, curPoint.y)
                adjacs := this.theMap.getAdjacentPoint(&curPoint._Point)
                for _, p := range adjacs {
                        //fmt.Println("\t adjact :", p.x, p.y)
                        theAP := NewAstarPoint(p, curPoint, &this.end)
                        if pointAsKey(theAP.x, theAP.y) == pointAsKey(this.end.x, this.end.y) {
                                // 找出路徑了, 標(biāo)記路徑
                                for theAP.father != nil {
                                        this.TheRoad = append(this.TheRoad, theAP)
                                        theAP.view = "*"
                                        theAP = theAP.father
                                }
                                return true
                        }

                        _, ok := this.closeLi[pointAsKey(p.x, p.y)]
                        if ok {
                                continue
                        }

                        existAP, ok := this.openSet[pointAsKey(p.x, p.y)]
                        if !ok {
                                heap.Push(&this.openLi, theAP)
                                this.openSet[pointAsKey(theAP.x, theAP.y)] = theAP
                        } else {
                                oldGVal, oldFather := existAP.gVal, existAP.father
                                existAP.father = curPoint
                                existAP.calcGVal()
                                // 如果新的節(jié)點(diǎn)的G值還不如老的節(jié)點(diǎn)就恢復(fù)老的節(jié)點(diǎn)
                                if existAP.gVal > oldGVal {
                                        // restore father
                                        existAP.father = oldFather
                                        existAP.gVal = oldGVal
                                }
                        }

                }
        }

        return false
}

//========================================================================================

func main() {
        presetMap := []string{
                ". . . . . . . . . . . . . . . . . . . . . . . . . . .",
                ". . . . . . . . . . . . . . . . . . . . . . . . . . .",
                ". . . . . . . . . . . . . . . . . . . . . . . . . . .",
                "X . X X X X X X X X X X X X X X X X X X X X X X X X X",
                ". . . . . . . . . . . . . . . . . . . . . . . . . . .",
                ". . . . . . . . . . . . . . . . . . . . . . . . . . .",
                ". . . . . . . . . . . . . . . . . . . . . . . . . . .",
                ". . . . . . . . . . . . . . . . . . . . . . . . . . .",
                ". . . . . . . . . . . . . . . . . . . . . . . . . . .",
                ". . . . . . . . . . . . . . . . . . . . . . . . . . .",
                ". . . . . . . . . . . . . . . . . . . . . . . . . . .",
                "X X X X X X X X X X X X X X X X X X X X X X X X . X X",
                ". . . . . . . . . . . . . . . . . . . . . . . . . . .",
                ". . . . . . . . . . . . . . . . . . . . . . . . . . .",
                ". . . . . . . . . . . . . . . . . . . . . . . . . . .",
                ". . . . . . . . . . . . . . . . . . . . . . . . . . .",
                ". . . . . . . . . . . . . . . . . . . . . . . . . . .",
                ". . . . . . . . . . . . . . . . . . . . . . . . . . .",
                ". . . . . . . . . . . . . . . . . . . . . . . . . . .",
        }
        m := NewMap(presetMap)
        m.PrintMap(nil)

        searchRoad := NewSearchRoad(0, 0, 18, 10, &m)
        if searchRoad.FindoutRoad() {
                fmt.Println("找到了姨伤, 你看哨坪!")
                m.PrintMap(searchRoad)
        } else {
                fmt.Println("找不到路徑!")
        }
}

原文地址:http://www.byteedu.com/forum.php?mod=viewthread&tid=436&page=1&extra=#pid552

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末乍楚,一起剝皮案震驚了整個(gè)濱河市当编,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌徒溪,老刑警劉巖忿偷,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異臊泌,居然都是意外死亡鲤桥,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門渠概,熙熙樓的掌柜王于貴愁眉苦臉地迎上來茶凳,“玉大人,你說我怎么就攤上這事播揪≈” “怎么了?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵猪狈,是天一觀的道長(zhǎng)箱沦。 經(jīng)常有香客問我,道長(zhǎng)罪裹,這世上最難降的妖魔是什么饱普? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任运挫,我火速辦了婚禮,結(jié)果婚禮上套耕,老公的妹妹穿的比我還像新娘谁帕。我一直安慰自己,他們只是感情好冯袍,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布匈挖。 她就那樣靜靜地躺著,像睡著了一般康愤。 火紅的嫁衣襯著肌膚如雪儡循。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天征冷,我揣著相機(jī)與錄音择膝,去河邊找鬼。 笑死检激,一個(gè)胖子當(dāng)著我的面吹牛肴捉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播叔收,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼齿穗,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了饺律?” 一聲冷哼從身側(cè)響起窃页,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎复濒,沒想到半個(gè)月后脖卖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡芝薇,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年胚嘲,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了作儿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片洛二。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖攻锰,靈堂內(nèi)的尸體忽然破棺而出晾嘶,到底是詐尸還是另有隱情,我是刑警寧澤娶吞,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布垒迂,位于F島的核電站,受9級(jí)特大地震影響妒蛇,放射性物質(zhì)發(fā)生泄漏机断。R本人自食惡果不足惜楷拳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望吏奸。 院中可真熱鬧欢揖,春花似錦、人聲如沸奋蔚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽泊碑。三九已至坤按,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間馒过,已是汗流浹背臭脓。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留腹忽,地道東北人谢鹊。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像留凭,于是被迫代替她去往敵國(guó)和親佃扼。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354