1. 程式人生 > >我見過的最容易讀懂的 a*演算法(A*尋路初探)

我見過的最容易讀懂的 a*演算法(A*尋路初探)

http://blog.vckbase.com/panic/archive/2005/03/20/3778.html

A*尋路初探

原文:Patrick Lester
翻譯:Panic
2005年3月18日

譯者序
  
很久以前就知道了A*演算法,但是從未認真讀過相關的文章,也沒有看過程式碼,只是腦子裡有個模糊的概念。這次決定從頭開始,研究一下這個被人推崇備至的簡單方法,作為學習人工智慧的開始。
這篇文章非常知名,國內應該有不少人翻譯過它,我沒有查詢,覺得翻譯本身也是對自身英文水平的鍛鍊。經過努力,終於完成了文件,也明白的A*演算法的原理。毫無疑問,作者用形象的描述,簡潔詼諧的語言由淺入深的講述了這一神奇的演算法,相信每個讀過的人都會對此有所認識(如果沒有,那就是偶的翻譯太差了--b)。
  以下是翻譯的正文。(由於本人使用ultraedit編輯,所以沒有對原文中的各種連結加以處理(除了圖表),也是為了避免未經許可連結的嫌疑,有興趣的讀者可以參考原文。

會者不難,A*(唸作A星)演算法對初學者來說的確有些難度。

  這篇文章並不試圖對這個話題作權威的陳述。取而代之的是,它只是描述演算法的原理,使你可以在進一步的閱讀中理解其他相關的資料。
  最後,這篇文章沒有程式細節。你儘可以用任意的計算機程式語言實現它。如你所願,我在文章的末尾包含了一個指向例子程式的連結。 壓縮包包括C++和Blitz Basic兩個語言的版本,如果你只是想看看它的執行效果,裡面還包含了可執行檔案。

我們正在提高自己。讓我們從頭開始......

序:搜尋區域

  假設有人想從A點移動到一牆之隔的B點,如下圖,綠色的是起點A,紅色是終點B,藍色方塊是中間的牆。

圖1


[圖1]

  你首先注意到,搜尋區域被我們劃分成了方形網格。像這樣,簡化搜尋區域,是尋路的第一步。這一方法把搜尋區域簡化成了一個二維陣列。陣列的每一個元素是網格的一個方塊,方塊被標記為可通過的和不可通過的。路徑被描述為從A到B我們經過的方塊的集合。一旦路徑被找到,我們的人就從一個方格的中心走向另一個,直到到達目的地。
  這些中點被稱為“節點”。當你閱讀其他的尋路資料時,你將經常會看到人們討論節點。為什麼不把他們描述為方格呢?因為有可能你的路徑被分割成其他不是方格的結構。他們完全可以是矩形,六角形,或者其他任意形狀。節點能夠被放置在形狀的任意位置-可以在中心,或者沿著邊界,或其他什麼地方。我們使用這種系統,無論如何,因為它是最簡單的。

開始搜尋


  正如我們處理上圖網格的方法,一旦搜尋區域被轉化為容易處理的節點,下一步就是去引導一次找到最短路徑的搜尋。在A*尋路演算法中,我們通過從點A開始,檢查相鄰方格的方式,向外擴充套件直到找到目標。

我們做如下操作開始搜尋:

  1. 從點A開始,並且把它作為待處理點存入一個“開啟列表”。開啟列表就像一張購物清單。儘管現在列表裡只有一個元素,但以後就會多起來。你的路徑可能會通過它包含的方格,也可能不會。基本上,這是一個待檢查方格的列表。
  2. 尋找起點周圍所有可到達或者可通過的方格,跳過有牆,水,或其他無法通過地形的方格。也把他們加入開啟列表。為所有這些方格儲存點A作為“父方格”。當我們想描述路徑的時候,父方格的資料是十分重要的。後面會解釋它的具體用途。
  3. 從開啟列表中刪除點A,把它加入到一個“關閉列表”,列表中儲存所有不需要再次檢查的方格。

  在這一點,你應該形成如圖的結構。在圖中,暗綠色方格是你起始方格的中心。它被用淺藍色描邊,以表示它被加入到關閉列表中了。所有的相鄰格現在都在開啟列表中,它們被用淺綠色描邊。每個方格都有一個灰色指標反指他們的父方格,也就是開始的方格。

圖2
[圖2]

  接著,我們選擇開啟列表中的臨近方格,大致重複前面的過程,如下。但是,哪個方格是我們要選擇的呢?是那個F值最低的。

路徑評分

選擇路徑中經過哪個方格的關鍵是下面這個等式:

F = G + H

這裡:

  • G = 從起點A,沿著產生的路徑,移動到網格上指定方格的移動耗費。
  • H = 從網格上那個方格移動到終點B的預估移動耗費。這經常被稱為啟發式的,可能會讓你有點迷惑。這樣叫的原因是因為它只是個猜測。我們沒辦法事先知道路徑的長度,因為路上可能存在各種障礙(牆,水,等等)。雖然本文只提供了一種計算H的方法,但是你可以在網上找到很多其他的方法。

  我們的路徑是通過反覆遍歷開啟列表並且選擇具有最低F值的方格來生成的。文章將對這個過程做更詳細的描述。首先,我們更深入的看看如何計算這個方程。
  正如上面所說,G表示沿路徑從起點到當前點的移動耗費。在這個例子裡,我們令水平或者垂直移動的耗費為10,對角線方向耗費為14。我們取這些值是因為沿對角線的距離是沿水平或垂直移動耗費的的根號2(別怕),或者約1.414倍。為了簡化,我們用10和14近似。比例基本正確,同時我們避免了求根運算和小數。這不是隻因為我們怕麻煩或者不喜歡數學。使用這樣的整數對計算機來說也更快捷。你不就就會發現,如果你不使用這些簡化方法,尋路會變得很慢。
  既然我們在計算沿特定路徑通往某個方格的G值,求值的方法就是取它父節點的G值,然後依照它相對父節點是對角線方向或者直角方向(非對角線),分別增加14和10。例子中這個方法的需求會變得更多,因為我們從起點方格以外獲取了不止一個方格。
  H值可以用不同的方法估算。我們這裡使用的方法被稱為曼哈頓方法,它計算從當前格到目的格之間水平和垂直的方格的數量總和,忽略對角線方向。然後把結果乘以10。這被成為曼哈頓方法是因為它看起來像計算城市中從一個地方到另外一個地方的街區數,在那裡你不能沿對角線方向穿過街區。很重要的一點,我們忽略了一切障礙物。這是對剩餘距離的一個估算,而非實際值,這也是這一方法被稱為啟發式的原因。想知道更多?你可以在這裡找到方程和額外的註解。
  F的值是G和H的和。第一步搜尋的結果可以在下面的圖表中看到。F,G和H的評分被寫在每個方格里。正如在緊挨起始格右側的方格所表示的,F被列印在左上角,G在左下角,H則在右下角。

圖3
[圖3]

  現在我們來看看這些方格。寫字母的方格里,G = 10。這是因為它只在水平方向偏離起始格一個格距。緊鄰起始格的上方,下方和左邊的方格的G值都等於10。對角線方向的G值是14。
  H值通過求解到紅色目標格的曼哈頓距離得到,其中只在水平和垂直方向移動,並且忽略中間的牆。用這種方法,起點右側緊鄰的方格離紅色方格有3格距離,H值就是30。這塊方格上方的方格有4格距離(記住,只能在水平和垂直方向移動),H值是40。你大致應該知道如何計算其他方格的H值了~。
每個格子的F值,還是簡單的由G和H相加得到

繼續搜尋

  為了繼續搜尋,我們簡單的從開啟列表中選擇F值最低的方格。然後,對選中的方格做如下處理:

 4.把它從開啟列表中刪除,然後新增到關閉列表中。
 5.檢查所有相鄰格子。跳過那些已經在關閉列表中的或者不可通過的(有牆,水的地形,或者其他    無法通過的地形),把他們新增進開啟列表,如果他們還不在裡面的話。把選中的方格作為新的方格的父節點。
 6.如果某個相鄰格已經在開啟列表裡了,檢查現在的這條路徑是否更好。換句話說,檢查如果我們用新的路徑到達它的話,G值是否會更低一些。如果不是,那就什麼都不做。
另一方面,如果新的G值更低,那就把相鄰方格的父節點改為目前選中的方格(在上面的圖表中,把箭頭的方向改為指向這個方格)。最後,重新計算F和G的值。如果這看起來不夠清晰,你可以看下面的圖示。

  好了,讓我們看看它是怎麼運作的。我們最初的9格方格中,在起點被切換到關閉列表中後,還剩8格留在開啟列表中。這裡面,F值最低的那個是起始格右側緊鄰的格子,它的F值是40。因此我們選擇這一格作為下一個要處理的方格。在緊隨的圖中,它被用藍色突出顯示。

圖4
[圖4]

  首先,我們把它從開啟列表中取出,放入關閉列表(這就是他被藍色突出顯示的原因)。然後我們檢查相鄰的格子。哦,右側的格子是牆,所以我們略過。左側的格子是起始格。它在關閉列表裡,所以我們也跳過它。
  其他4格已經在開啟列表裡了,於是我們檢查G值來判定,如果通過這一格到達那裡,路徑是否更好。我們來看選中格子下面的方格。它的G值是14。如果我們從當前格移動到那裡,G值就會等於20(到達當前格的G值是10,移動到上面的格子將使得G值增加10)。因為G值20大於14,所以這不是更好的路徑。如果你看圖,就能理解。與其通過先水平移動一格,再垂直移動一格,還不如直接沿對角線方向移動一格來得簡單。
  當我們對已經存在於開啟列表中的4個臨近格重複這一過程的時候,我們發現沒有一條路徑可以通過使用當前格子得到改善,所以我們不做任何改變。既然我們已經檢查過了所有鄰近格,那麼就可以移動到下一格了。
  於是我們檢索開啟列表,現在裡面只有7格了,我們仍然選擇其中F值最低的。有趣的是,這次,有兩個格子的數值都是54。我們如何選擇?這並不麻煩。從速度上考慮,選擇最後新增進列表的格子會更快捷。這種導致了尋路過程中,在靠近目標的時候,優先使用新找到的格子的偏好。但這無關緊要。(對相同數值的不同對待,導致不同版本的A*演算法找到等長的不同路徑。)

那我們就選擇起始格右下方的格子,如圖。

圖5
[圖5]

  這次,當我們檢查相鄰格的時候,發現右側是牆,於是略過。上面一格也被略過。我們也略過了牆下面的格子。為什麼呢?因為你不能在不穿越牆角的情況下直接到達那個格子。你的確需要先往下走然後到達那一格,按部就班的走過那個拐角。(註解:穿越拐角的規則是可選的。它取決於你的節點是如何放置的。)
  這樣一來,就剩下了其他5格。當前格下面的另外兩個格子目前不在開啟列表中,於是我們新增他們,並且把當前格指定為他們的父節點。其餘3格,兩個已經在關閉列表中(起始格,和當前格上方的格子,在表格中藍色高亮顯示),於是我們略過它們。最後一格,在當前格的左側,將被檢查通過這條路徑,G值是否更低。不必擔心,我們已經準備好檢查開啟列表中的下一格了。

我們重複這個過程,知道目標格被新增進開啟列表,就如在下面的圖中所看到的。

圖6
[圖6]

  注意,起始格下方格子的父節點已經和前面不同的。之前它的G值是28,並且指向右上方的格子。現在它的G值是20,指向它上方的格子。這在尋路過程中的某處發生,當應用新路徑時,G值經過檢查變得低了-於是父節點被重新指定,G和F值被重新計算。儘管這一變化在這個例子中並不重要,在很多場合,這種變化會導致尋路結果的巨大變化。
  那麼,我們怎麼確定這條路徑呢?很簡單,從紅色的目標格開始,按箭頭的方向朝父節點移動。這最終會引導你回到起始格,這就是你的路徑!看起來應該像圖中那樣。從起始格A移動到目標格B只是簡單的從每個格子(節點)的中點沿路徑移動到下一個,直到你到達目標點。就這麼簡單。

圖7
[圖7]

A*方法總結

好,現在你已經看完了整個說明,讓我們把每一步的操作寫在一起:
 

  1. 把起始格新增到開啟列表。
  2. 重複如下的工作:
    a) 尋找開啟列表中F值最低的格子。我們稱它為當前格。
    b) 把它切換到關閉列表。
    c) 對相鄰的8格中的每一個?
    • 如果它不可通過或者已經在關閉列表中,略過它。反之如下。
    • 如果它不在開啟列表中,把它新增進去。把當前格作為這一格的父節點。記錄這一格的F,G,和H值。
    • 如果它已經在開啟列表中,用G值為參考檢查新的路徑是否更好。更低的G值意味著更好的路徑。如果是這樣,就把這一格的父節點改成當前格,並且重新計算這一格的G和F值。如果你保持你的開啟列表按F值排序,改變之後你可能需要重新對開啟列表排序。
    d) 停止,當你
    • 把目標格新增進了開啟列表,這時候路徑被找到,或者
    • 沒有找到目標格,開啟列表已經空了。這時候,路徑不存在。
  3. 儲存路徑。從目標格開始,沿著每一格的父節點移動直到回到起始格。這就是你的路徑。

題外話

  離題一下,見諒,值得一提的是,當你在網上或者相關論壇看到關於A*的不同的探討,你有時會看到一些被當作A*演算法的程式碼而實際上他們不是。要使用A*,你必須包含上面討論的所有元素--特定的開啟和關閉列表,用F,G和H作路徑評價。有很多其他的尋路演算法,但他們並不是A*,A*被認為是他們當中最好的。Bryan Stout在這篇文章後面的參考文件中論述了一部分,包括他們的一些優點和缺點。有時候特定的場合其他演算法會更好,但你必須很明確你在作什麼。好了,夠多的了。回到文章。

實現的註解

  現在你已經明白了基本原理,寫你的程式的時候還得考慮一些額外的東西。下面這些材料中的一些引用了我用C++和Blitz Basic寫的程式,但對其他語言寫的程式碼同樣有效。

  1. 維護開啟列表:這是A*尋路演算法最重要的組成部分。每次你訪問開啟列表,你都需要尋找F值最低的方格。有幾種不同的方法實現這一點。你可以把路徑元素隨意儲存,當需要尋找F值最低的元素的時候,遍歷開啟列表。這很簡單,但是太慢了,尤其是對長路徑來說。這可以通過維護一格排好序的列表來改善,每次尋找F值最低的方格只需要選取列表的首元素。當我自己實現的時候,這種方法是我的首選。
      在小地圖。這種方法工作的很好,但它並不是最快的解決方案。更苛求速度的A*程式設計師使用叫做“binary heap”的方法,這也是我在程式碼中使用的方法。憑我的經驗,這種方法在大多數場合會快2~3倍,並且在長路經上速度呈幾何級數提升(10倍以上速度)。如果你想了解更多關於binary heap的內容,查閱我的文章:Using Binary Heaps in A* Pathfinding
  2. 其他單位:如果你恰好看了我的例子程式碼,你會發現它完全忽略了其他單位。我的尋路者事實上可以相互穿越。取決於具體的遊戲,這也許可以,也許不行。如果你打算考慮其他單位,希望他們能互相繞過,我建議在尋路演算法中忽略其他單位,寫一些新的程式碼作碰撞檢測。當碰撞發生,你可以生成一條新路徑或者使用一些標準的移動規則(比如總是向右,等等)直到路上沒有了障礙,然後再生成新路徑。為什麼在最初的路徑計算中不考慮其他單位呢?那是因為其他單位會移動,當你到達他們原來的位置的時候,他們可能已經離開了。這有可能會導致奇怪的結果,一個單位突然轉向,躲避一個已經不在那裡的單位,並且會撞到計算完路徑後,衝進它的路徑中的單位。
      然而,在尋路演算法中忽略其他物件,意味著你必須編寫單獨的碰撞檢測程式碼。這因遊戲而異,所以我把這個決定權留給你。參考文獻列表中,Bryan Stout的文章值得研究,裡面有一些可能的解決方案(像魯棒追蹤,等等)。
  3. 一些速度方面的提示:當你開發你自己的A*程式,或者改寫我的,你會發現尋路佔據了大量的CPU時間,尤其是在大地圖上有大量物件在尋路的時候。如果你閱讀過網上的其他材料,你會明白,即使是開發了星際爭霸或帝國時代的專家,這也無可奈何。如果你覺得尋路太過緩慢,這裡有一些建議也許有效:
    • 使用更小的地圖或者更少的尋路者。
    • 不要同時給多個物件尋路。取而代之的是把他們加入一個佇列,把尋路過程分散在幾個遊戲週期中。如果你的遊戲以40週期每秒的速度執行,沒人能察覺。但是他們會發覺遊戲速度突然變慢,當大量尋路者計算自己路徑的時候。
    • 儘量使用更大的地圖網格。這降低了尋路中搜索的總網格數。如果你有志氣,你可以設計兩個或者更多尋路系統以便使用在不同場合,取決於路徑的長度。這也正是專業人士的做法,用大的區域計算長的路徑,然後在接近目標的時候切換到使用小格子/區域的精細尋路。如果你對這個觀點感興趣,查閱我的文章 :Two-Tiered A* Pathfinding
    • 使用路徑點系統計算長路徑,或者預先計算好路徑並加入到遊戲中。
    • 預處理你的地圖,表明地圖中哪些區域是不可到達的。我把這些區域稱作“孤島”。事實上,他們可以是島嶼或其他被牆壁包圍等無法到達的任意區域。A*的下限是,當你告訴它要尋找通往那些區域的路徑時,它會搜尋整個地圖,直到所有可到達的方格/節點都被通過開啟列表和關閉列表的計算。這會浪費大量的CPU時間。可以通過預先確定這些區域(比如通過flood-fill或類似的方法)來避免這種情況的發生,用某些種類的陣列記錄這些資訊,在開始尋路前檢查它。在我Blitz版本的程式碼中,我建立了一個地圖前處理器來作這個工作。它也標明瞭尋路演算法可以忽略的死端,這進一步提高了尋路速度。
  4. 不同的地形損耗:在這個教程和我附帶的程式中,地形只有兩種-可通過的和不可通過的。但是你可能會需要一些可通過的地形,但是移動耗費更高-沼澤,小山,地牢的樓梯,等等。這些都是可通過但是比平坦的開闊地移動耗費更高的地形。類似的,道路應該比自然地形移動耗費更低。
      這個問題很容易解決,只要在計算任何地形的G值的時候增加地形損耗就可以了。簡單的給它增加一些額外的損耗就可以了。由於A*演算法已經按照尋找最低耗費的路徑來設計,所以很容易處理這種情況。在我提供的這個簡單的例子裡,地形只有可通過和不可通過兩種,A*會找到最短,最直接的路徑。但是在地形耗費不同的場合,耗費最低的路徑也許會包含很長的移動距離-就像沿著路繞過沼澤而不是直接穿過它。
      一種需額外考慮的情況是被專家稱之為“influence mapping”的東西(暫譯為影響對映圖)。就像上面描述的不同地形耗費一樣,你可以建立一格額外的分數系統,並把它應用到尋路的AI中。假設你有一張有大批尋路者的地圖,他們都要通過某個山區。每次電腦生成一條通過那個關口的路徑,它就會變得更擁擠。如果你願意,你可以建立一個影響對映圖對有大量屠殺事件的格子施以不利影響。這會讓計算機更傾向安全些的路徑,並且幫助它避免總是僅僅因為路徑短(但可能更危險)而持續把隊伍和尋路者送到某一特定路徑。
  5. 處理未知區域:你是否玩過這樣的PC遊戲,電腦總是知道哪條路是正確的,即使它還沒有偵察過地圖?對於遊戲,尋路太好會顯得不真實。幸運的是,這是一格可以輕易解決的問題。
      答案就是為每個不同的玩家和電腦(每個玩家,而不是每個單位--那樣的話會耗費大量的記憶體)建立一個獨立的“knownWalkability”陣列,每個陣列包含玩家已經探索過的區域,以及被當作可通過區域的其他區域,直到被證實。用這種方法,單位會在路的死端徘徊並且導致錯誤的選擇直到他們在周圍找到路。一旦地圖被探索了,尋路就像往常那樣進行。
  6. 平滑路徑:儘管A*提供了最短,最低代價的路徑,它無法自動提供看起來平滑的路徑。看一下我們的例子最終形成的路徑(在圖7)。最初的一步是起始格的右下方,如果這一步是直接往下的話,路徑不是會更平滑一些嗎?
      有幾種方法來解決這個問題。當計算路徑的時候可以對改變方向的格子施加不利影響,對G值增加額外的數值。也可以換種方法,你可以在路徑計算完之後沿著它跑一遍,找那些用相鄰格替換會讓路徑看起來更平滑的地方。想知道完整的結果,檢視 Marco Pinter 發表在 Gamasutra.com 的 一篇文章:Toward More Realistic Pathfinding (免費,但是需要註冊)。
  7. 非方形搜尋區域:在我們的例子裡,我們使用簡單的2D方形圖。你可以不使用這種方式。你可以使用不規則形狀的區域。想想冒險棋的遊戲,和遊戲中那些國家。你可以設計一個像那樣的尋路關卡。為此,你可能需要建立一個國家相鄰關係的表格,和從一個國家移動到另一個的G值。你也需要估算H值的方法。其他的事情就和例子中完全一樣了。當你需要向開啟列表中新增新元素的時候,不需使用相鄰的格子,取而代之的是從表格中尋找相鄰的國家。
      類似的,你可以為一張確定的地形圖建立路徑點系統,路徑點一般是路上,或者地牢通道的轉折點。作為遊戲設計者,你可以預設這些路徑點。兩個路徑點被認為是相鄰的如果他們之間的直線上沒有障礙的話。在冒險棋的例子裡,你可以儲存這些相鄰資訊在某個表格裡,當需要在開啟列表中新增元素的時候使用它。然後你就可以記錄關聯的G值(可能使用兩點間的直線距離),H值(可以使用到目標點的直線距離),其他都按原先的做就可以了。
另一個在非方形區域搜尋RPG地圖的例子,檢視我的文章:Two-Tiered A* Pathfinding

進一步的閱讀

  好,現在你對一些進一步的觀點有了初步認識。這時,我建議你研究我的原始碼。包裡面包含兩個版本,一個是用C++寫的,另一個用Blitz Basic。順便說一句,兩個版本都註釋詳盡,容易閱讀,這裡是連結。

例子程式碼:A* Pathfinder (2D) Version 1.71

  如果你既不用C++也不用Blitz Basic,在C++版本里有兩個小的可執行檔案。Blitz Basic可以在從Blitz Basic網站免費下載的 litz Basic 3D(不是Blitz Plus)演示版上執行。Ben O''Neill提供一個聯機演示可以在這裡找到。

你也該看看以下的網頁。讀了這篇教程後,他們應該變得容易理解多了。

  • Amit 的 A* 頁面:這是由Amit Patel製作,被廣泛引用的頁面,如果你沒有事先讀這篇文章,可能會有點難以理解。值得一看。尤其要看Amit關於這個問題的自己的看法。
  • Smart Moves:智慧尋路:Bryan Stout發表在Gamasutra.com的這篇文章需要註冊才能閱讀。註冊是免費的而且比起這篇文章和網站的其他資源,是非常物有所值的。Bryan用Delphi寫的程式幫助我學習A*,也是我的A*程式碼的靈感之源。它還描述了A*的幾種變化。
  • 地形分析:這是一格高階,但是有趣的話題,Dave Pottinge撰寫,Ensemble Studios的專家。這傢伙參與了帝國時代和君王時代的開發。別指望看懂這裡所有的東西,但是這是篇有趣的文章也許會讓你產生自己的想法。它包含一些對mip-mapping,influence mapping以及其他一些高階AI/尋路觀點。對"flood filling"的討論使我有了我自己的“死端”和“孤島”的程式碼的靈感,這些包含在我Blitz版本的程式碼中。

其它一些值得一看的網站:

其它參考文章:

好了,這就是全部。如果你剛好寫一個運用這些觀點的程式,我想見識見識。你可以這樣聯絡我:

現在,好運!
 

相關推薦

我見容易a*演算法(A*初探)

http://blog.vckbase.com/panic/archive/2005/03/20/3778.html A*尋路初探原文:Patrick Lester翻譯:Panic 2005年3月18日

轉自知乎-我見通俗易懂的KMP演算法詳解

有些演算法,適合從它產生的動機,如何設計與解決問題這樣正向地去介紹。但KMP演算法真的不適合這樣去學。最好的辦法是先搞清楚它所用的資料結構是什麼,再搞清楚怎麼用,最後為什麼的問題就會有恍然大悟的感覺。我試著從這個思路再介

很好很詳細的A*演算法教程

http://blog.csdn.net/amdk_7/archive/2006/07/31/1005480.aspx http://www.policyalmanac.org/games/twoTiered.htm 雖然A*(讀作A星)演算法對初學者來說是比較深奧難懂,

這是我見完整的Python語法和實戰清單!是個人都能看學會!

基礎語法 Python 是一門高階、動態型別的多正規化程式語言;定義 Python 檔案的時候我們往往會先宣告檔案編碼方式: # 指定指令碼呼叫方式 #!/usr/bin/env python # 配置 utf-8 編碼 # -*- coding: utf-8 -*- # 配置其他編碼 #

我見通俗易懂的快速排序過程講解,轉自《坐在馬桶上看演算法:快速排序》

如果以上C程式碼看不懂,請看下面java程式碼: public static int Partition(int[] a,int p,int r){   int x=a[r-1];   int i=p-1;   int temp;   for(int j=p;j<=

這是我見詳細的十大排序演算法介紹了,沒有之一(十大排序演算法詳解)

> **作者:** C you again,從事軟體開發 努力在IT搬磚路上的技術小白 > **公眾號:** 【**[C you again](https://cyouagain.cn/)**】,分享計算機類畢業設計原始碼、IT技術文章、遊戲原始碼、網頁模板、程式人生等等。公眾號回覆 【**粉絲

攝像機標定--我見懶的方法

1.百度上找半天各種標定的方法,然而複製程式碼下來各種報錯,或者就是沒講清楚到底怎麼操作。 後來我突然想到opencv自帶的例程說不到也有標定的程式碼,很幸運在sample/python/下找到了calibrate.py這個程式。 開啟后里面有如下程式碼: '''camera calibration f

這是我見牛X的Java架構進階學習路線圖,沒有之一!

大部分職業都是需要有成長體系,才能讓人有奮發向上的追求。架構師就是程式設計師這個群體成長道路上往往會出現的一個重要節點。 每個好架構師都是一位出色的程式設計師。然而好的程式設計師進階為架構師,並不容易。 在規劃和設計系統架構時,涉及到方方面面的技術知識點,可選的方案也很多,如何在各種各樣,紛

一文 KMP 演算法

來源:阮一峰http://www.ruanyifeng.com/blog/2013/05/Knuth–Morris–Pratt_algorithm.html 字串匹配是計算機的基本任務之一。舉例來說,有一個字串"BBC ABCDAB ABCDABCDABDE",我想知道,裡面是否包

Python高階程式設計——裝飾器Decorator詳解(上篇)(絕對是我見詳細的的教程,沒有之一哦)

一、先從一種情況開始看起 1、裝飾器decorator的由來 裝飾器的定義很是抽象,我們來看一個小例子。 先定義一個簡單的函式: def myfunc:     print('我是函式myfunc') myfunc() #呼叫函式 然後呢,我想看看

關於深度學習,可能是容易進去的科普

(一) 一 2016 年一月底,人工智慧的研究領域,發生了兩件大事。 先是一月二十四號,MIT 的教授,人工智慧研究的先驅者,Marvin Minsky 去世,享年89 歲。 三天之後,谷歌在自然雜誌上正式公開發表論文,宣佈其以深度學習技術為基礎的電腦程式 AlphaGo, 在 2015年 十月,連續

ES6 Promise 用法(我見簡潔優秀的文章)

ES6 Promise 先拉出來遛遛 複雜的概念先不講,我們先簡單粗暴地把Promise用一下,有個直觀感受。那麼第一個問題來了,Promise是什麼玩意呢?是一個類?物件?陣列?函式?   別猜了,直接打印出來看看吧,console.dir(Promise),就這麼簡單粗

ES6 Promise詳細用法(我見簡潔優秀的文章)

ES6 Promise 先拉出來遛遛 複雜的概念先不講,我們先簡單粗暴地把Promise用一下,有個直觀感受。那麼第一個問題來了,Promise是什麼玩意呢?是一個類?物件?陣列?函式?   別猜了,直接打印出來看看吧,console.dir(Promise),就這麼簡

這是我見通俗易懂的Python入門之函式!

  函式是指將一個可重複使用的實現某一功能的程式碼塊封裝起來,其關鍵字是def,函式定義的幾個要素分別是函式名、入參和出參,入參出參皆可為空,出參一般不為空,下面是虛擬碼:     def 函式名(XX): # 入參可為空 功能程式碼

Handler詳解(容易容易理解)

好吧好吧,今天去面試的時候,一個熊二般的面試官問我handler的機制,以及和Looper的關係,以及更新UI有哪些方式。可是我是小白啊,所以不知道,所以就不可能通過面試,不過今年工作可真不好找。回來後又是把書籍,又是看視訊,最終還是打算寫成自己的部落格。希望幫助到其他的小

一文FM演算法優勢,並用python實現

介紹 我仍然記得第一次遇到點選率預測問題時的情形,在那之前,我一直在學習資料科學,對自己取得的進展很滿意,在機器學習黑客馬拉松活動中也開始建立了自信,並決定好好迎接不同的挑戰。 為了做得更好,我購買了一臺記憶體16GB,i7處理器的機器,但是當我看到資料集的時候卻感到非常不安,解壓縮之後的資

三分鐘摘要演算法

摘要演算法又稱雜湊演算法,它表示輸入任意長度的資料,輸出固定長度的資料,相同的輸入資料始終得到相同的輸出,不同的輸入資料儘量得到不同的輸出。 Java中的Object.hashCode()方法就是一個摘要演算法,它可以輸入任意資料,它的輸出是一個int型別,即

這是我見有用的java面試題,面試了無數公司總結的

【宣告】來源:動力節點Java學院,轉載源:指令碼之家(一小部分題的答案被我略作改動)1、什麼是執行緒區域性變數?執行緒區域性變數是侷限於執行緒內部的變數,屬於執行緒自身所有,不在多個執行緒間共享。Java 提供 ThreadLocal 類來支援執行緒區域性變數,是一種實現執

吊炸天的CNNs,這是我見詳盡的圖解!(下)

【摘要】本文詳細介紹了卷積神經網路的執行原理,特別是池化、全連線等過程。為了使大家更快、更輕鬆的入門,文章沒有晦澀難懂的術語和公式,全部採用“圖形”的方式來描述。文末的延展閱讀部分,更加入了彩色圖片卷積原理的手工演算過程,由淺入深,層層推進,希望你讀得暢快!

大神一步步教你ORB演算法,贊!!

工作就沒有在學校時間上有那麼自由了,最近出差了快一個月,部落格也就落下了。現在開始一點點的來學習orb-slam2,將自己的學習過程寫出,望大家指正批評。 至於為什麼學習orb-slam2,主要這比較完整的實現了slam的整個過程,論文發表在IEEE Transaction