演算法

演算法（粵音：jin2 syun3 faat3；英文：algorithm），又可以簡稱算法，係數學同電腦科學上嘅概念，指一串能夠冇歧義或者含糊噉教一個人或者一部電腦解決某啲特定問題嘅命令。演算法有分好多唔同種，唔同種嘅演算法可以用嚟解決唔同嘅問題，由簡單嘅算術以至自動化嘅認知等等都有演算法可以做得到。
演算法可以喺有限嘅時間同記憶空間之內，透過形式語言（簡單講就係個個字詞都有精確定義嘅語言，相對於自然語言）嚟表達，用嚟計某一啲函數：噉講嘅意思係話，一串演算法會要求某啲特定嘅輸入，跟住啲命令會描述一柞運算；當呢柞運算由人或者電腦執行嗰陣，會經過一連串數量有限嘅中介狀態，最後產生一個輸出，並且喺呢個最終狀態嗰度終止執行。順帶一提，由一個中介狀態去到下一個嘅過程唔一定係決定嘅，有好多演算法都涉及帶有隨機性嘅運算。
演算法呢個概念歷史悠久，有得一路追溯到去公元前嘅古希臘：由古希臘數學家諗出嚟嘅愛氏篩同埋係歐幾里得演算法等都可以算係早期嘅演算法；而演算法個英文名係由九世紀嘅波斯人數學家花剌子密，花剌子密做咗啲相關研究，局部噉確立演算法嘅概念；現代嘅演算法概念係喺一九二八年由德國數學家打域囂拔嘗試解決可判定性問題嗰陣奠定嘅。自從嗰時開始，演算法同相關嘅研究就喺數學同電腦科學領域度受到廣泛採用。
籠統噉講，演算法呢個詞嘅定義可以指「一串能夠精確噉定義一系列作業嘅規則」。呢個定義包含晒所有電腦程式入便嘅演算法－就連啲唔曉做數字上嘅運算嘅程式都包含喺呢個定義之內，而歐幾里得演算法同埋文頭嗰個流程圖所描述嘅都係符合呢個定義嘅演算法。演算法對於電腦處理數據嚟講係不可或缺嘅：電腦程式會包含一大柞嘅演算法，仔細噉教部電腦要做啲乜嘢運算同埋「以乜嘢次序做呢啲運算」，用演算法解決用戶的特定問題－呢啲問題可以包括咗計啲簡單嘅算術以至複雜嘅統計學作業等等都得。
精確啲噉講，一段演算法包含一串作業，而串嘢要有以下呢啲特質：
演算法可以用集（可作粵拼set1）嘅概念嚟想像。例如美國邏輯學家佐治·布勞斯同佢嘅同事係噉樣描述演算法呢個概念嘅：
上面呢段嘢當中所講嘅可數嘅無限集係數學上嘅一個概念，指一個包含咗多樣嘢嘅集，而呢個集包含嘅物件有無限咁多件，但啲件數可以用由1開始嘅整數嚟數。所以貝勞斯同佢啲同事講緊就係話：一串演算法係一連串指令，例如係一柞好似
噉嘅算式；而呢柞指令能夠由是但一啲輸入嗰度計同整一啲輸出出嚟俾人睇，而且呢啲輸入－至少喺理論上－係幾大都得嘅。
想像有一部機械，識得跟事先指定咗嘅規則由輸入計輸出，呢部機嘅記憶體係無限咁多嘅－即係一部理想化嘅圖靈機。呢部機計嘅運算可以用多種唔同嘅圖表同語言表達，包括自然語言、虛擬碼、流程圖、狀態轉移表同埋係多種嘅程式語言呀噉。用自然語言－好似粵語等日常講嘢用嘅語言－寫嘅符號（虛擬碼）對一般人嚟講易明，但係好多時都充滿歧義，而且電腦唔會識睇。程式語言係專門為咗畀人用嚟同電腦溝通而設嘅特殊語言，電腦睇得明，演算法好多時都會用程式語言定義。
舉個例說明，好似係以下呢段用粵文寫嘅演算法噉，就做咗一連串運算：
呢串演算法用Python（一九九一年出嘅一隻程式語言）寫出嚟嘅話會係：
諗演算法嘅過程，涉及將一件作業揼散做組成佢嘅細部份，每個細部份都要係一啲電腦普遍識做嘅簡單工作，例如「比較兩個數，睇吓邊個大啲」噉－呢啲細部份可以話係組成演算法嘅「元素」，有咗佢哋就可以砌出任何「人類會想電腦做嘅工作」。
演算法嘅表達方法，大致上可以分做三大圖靈機描述層次：
Python 源碼屬於高層描述－就算睇咗佢，一個人仲係唔會知部電腦嗰啲邏輯門行咗乜嘢運算，計咗乜嘢數。有關廿一世紀初嘅電子電腦係點樣用物理方式計數嘅，可以睇吓邏輯門同訊號等嘅概念。
搜尋演算法泛指用嚟由數據結構當中搵出想要嗰件數據嘅演算法－輸入係「數據結構」同「想要嗰件數據嘅樣」，而輸出係「想要嗰件數據喺數據結構當中嘅邊個位」。廿一世紀嘅電腦科學界有多種搜尋演算法可以用，而每種都有優缺點。
窮舉搜尋係最原始嗰種搜尋演算法，指將手上嘅數據（事先以某啲方式排好咗）逐個逐個耖，一路耖到搵到想要嗰個為止。例如手上有一列以隨機次序排嘅數字，用家想要由呢個數列當中最大嗰個出嚟，用窮舉搜尋嘅話，睇嗰個人就要睇嗮個數列當中嘅每個數字，再俾出最大嗰個出嚟；呢段簡單嘅演算法用粵文寫出嚟嘅話會係：
上述呢串演算法用比較形式些少嘅虛擬碼寫出嚟嘅話會係噉：
廿一世紀嘅研究者一般都會嫌呢串演算法唔夠好，原因係呢串演算法喺最壞情況嗰陣要睇嗮成個列先至搞得掂，所以又有人諗咗第啲演算法出嚟做呢樣工作－而呢啲新演算法好多時都曉淨係睇個數字列嘅一小部份就經已搵得出想要嗰個數字。
對分檢索係用嚟由一個預先排好咗次序嘅數列嗰度搵某一個特定數字出嚟嘅演算法。對分檢索嘅做法係首先將行資料拆做兩橛，再睇吓中間嗰個數係咪等如要搵嗰個數（目標），假如個數列係預先由細到大排好咗嘅話，噉就意味一樣嘢：如果數列中間嗰個數大過個目標，噉個目標實係喺中間個數前面，而如果數列中間嗰個數細過個目標，噉個目標實係喺中間個數後面，跟手段演算法就可以再重複呢個程序，將個搜索範圍縮細，最後搵到個目標嘅數字（假如個目標真係存在喺嗰行數列入面嘅話）。好似係以下呢段虛擬碼噉：
對分檢索平均會快過「吓吓都將數列入面嘅數逐個逐個睇一次」嘅窮舉搜尋：喺最壞情況下，想搵嗰個數會係個數列嘅第一個或者最尾嗰個，而喺呢個情況下，用對分檢索嚟做搜尋要做O(log2⁡n){\displaystyle O(\log _{2}n)}咁多次比較（當中n{\displaystyle n}係「個數列入面有幾多個數字」），而「吓吓將數列入面逐個逐個睇一次」喺最壞情況之下就要做n{\displaystyle n}次比較，O(log2⁡n){\displaystyle O(\log _{2}n)}數值永遠細過n{\displaystyle n}。「由一個數列當中搵一個特定數字出嚟」係電腦好常要做嘅基本作業，如果（例如）喺一個程式當中部電腦要做呢個作業 100 次嘅話，噉一個用對分檢索嘅程式基本上實會快一截，所以編程員多數會比較鍾意用對分檢索。
排序演算法泛指用嚟將一柞物件以某啲特定次序（例如由數值細到大）排好嘅演算法。呢種演算法嘅價值在於支援第啲演算法－有唔少演算法都係要啲數據事先排好咗先至會啱用，例子有頭先提到嘅對分檢索。
快速排序係指用嚟將一個數列入面啲數字由細至大排好嘅演算法－輸入係一個（亂糟糟嘅）數列，而輸出係執好咗嘅數列。步驟如下：
呢串演算法俾人好廣泛噉攞嚟將啲數列嘅次序排好。上述呢串演算法用Python寫出嚟嘅話會係噉嘅：
冒泡排序係一種用嚟將一個數列入面啲數字由細至大排好嘅演算法，做法係喺每一步攞相鄰嘅兩個數比較，將兩個數由細到大排好，再將個過程做若干次，做到成個數列都排好嗮為止。原則上，冒泡排序並唔好用－喺最壞情況下要行成O(n2){\displaystyle O(n^{2})}咁多步先行得完，所以喺現實，冒泡排序多數都淨係俾人用嚟做教育用途。
例如如果用冒泡排序同5, 1, 4, 2, 8呢個數列排序嘅話：
歐幾里得演算法，係用嚟求最大公因數嘅一串演算法，首次記載係喺大約公元前 300 年嘅古希臘數學家歐幾里得嗰本《幾何原本》入面。歐幾里得佢係噉樣講嘅：（粵文翻譯）「家吓有兩個彼此唔係對方因數嘅數，個問題係要搵出佢哋嘅最大公因數」，佢首先諗到一點－要計佢哋最大公因數嗰兩個數相減嘅話，得出嗰個數一定係個最大公因數嘅倍數。佢諗出嗰串演算法步驟簡單講如下：
要將呢串演算法用電腦嚟執行嘅話好簡單，淨係需要幾個種類嘅指令已經夠：條件性嘅GOTO、無條件性嘅 GOTO、設變數同埋基本嘅算術。
以下呢段碼係歐幾里得演算法嘅一個可能實現形式（雖然實際上係一個唔多靚嘅演算法）。佢個做法嘅基本原理係將要搵佢哋最大公因數嗰兩個數設做l{\displaystyle l}（大啲嗰個）同s{\displaystyle s}（細啲嗰個）兩個變數，再將s{\displaystyle s}由l{\displaystyle l}嗰度減出嚟，減到得出個數細過s{\displaystyle s}為止。
輸入：
E0：確保r≥s{\displaystyle r\geq s}。
E1：搵個餘數出嚟－將S{\displaystyle S}由R{\displaystyle R}減出嚟，減到得出嘅餘數r{\displaystyle r}細過S{\displaystyle S}為止。
E2：個餘數係咪零？如果係嘅話，個程式終止得，如果唔係嘅話，串演算法要再行落去，直至得出一個係零嘅餘數。
E3：將S{\displaystyle S}同r{\displaystyle r}對調，用L{\displaystyle L}嚟做啲數字嘅中轉站。
輸出：
搞掂：
以下呢個係歐幾里得演算法嘅第個實現例子。佢淨係使用咗 6 個核心指令，相對於第一個例子嘅 13 個，俾好多人覺得係一個「優雅」啲嘅演算法。佢用嗰種程式語言係以「LET [] = []」嚟設變數嘅數值嘅。
如果用緊嘅程式語言係比較物件導向嘅話，可以用Java 程式語言噉做：
以上嘅呢啲演算法由相關領域嘅研究者用好多唔同嘅數字輸入試過，證實咗係掂嘅，亦都有數學家用歸納法嚟證明佢真係行得通嘅。
搵路演算法係人工智能（AI）上嘅一個大課題，指教一個 AI 喺空間入面指定兩個點，並且搵出一條連接兩點嘅線。做搵路嘅演算法有好多用途，例如喺機械人學上教機械人探索自己周圍嘅空間，以及喺遊戲製作上教遊戲 AI喺遊戲空間入面搵出要行嘅路線。
一般嚟講，搵路演算法－無論係咪電子遊戲用嘅都好－都會
有關搵路演算法嘅具體實現，可以睇吓迪卡斯特拉演算法同埋A* 搜尋演算法。
要將演算法分類，可以用好多唔同嘅準則：
遞歸係好多演算法有嘅特性，（簡化噉講）指串演算法當中有個子程式會用到佢自己，例如以下呢段簡單嘅碼噉：
喺編程上，遞歸可以用嚟解決一啲可以揼散做一大柞細問題嘅問題，而且當中每一個細問題都屬同一個類型，例如歐幾里得嘅演算法可以用C 程式語言寫成以下嘅遞歸程式：
好出名嘅河內塔問題，就可以用遞歸演算法輕鬆解決。
決定性演算法係指本質上決定性嘅演算法，而非決定性演算法係指冇呢種特徵嘅演算法：決定性演算法喺每個步驟由輸入去輸出都好精確，冇任何隨機性喺入面，俾同一個輸入串演算法實會出同一個輸出；而非決定性演算法就相反，會有隨機性喺入面，所以就算吓吓俾同一樣嘅輸入佢，出嗰個輸出都可能會唔同咗，可以睇吓擬亂數產生。
例：RPG 遊戲當中成日會有一大柞招式俾角色用，一個招式有若干個屬性，包括咗嗰招嘅殺傷力同命中率等等，當一個角色用一個命中率係 80% 嘅招式嗰陣，一個常見做法係遊戲程式內部會用RNG產生一個 0 至 1 之間嘅數字，而如果個數字大過或者等如 0.8，噉嗰吓攻擊就會打中，否則就會唔中，實際例子有寵物小精靈系列。
決定性同非決定性演算法各有優劣－非決定性演算法嘅輸出難以預測，喺睇個輸出之前，一個非決定性演算法嘅輸出會以一個概率分佈嘅形式存在，而非決定性演算法「輸出缺乏精確性」呢一點表示好多要求高度精確嘅科學同工程學應用唔能夠用呢啲演算法，但非決定性演算法「難以預測」呢一個特性又有第啲用途，例如，例如係一個模擬啤牌嘅遊戲程式喺洗牌嗰陣，就需要用到非決定性演算法，因為啤牌遊戲一般都預咗玩家冇能力預測抽到嘅牌係乜樣嘅，而統計學同機械學習嘅應用上都會用到非決定性演算法。
近似演算法係指嘗試搵出近似想要嘅答案嘅數值嘅演算法，而精準演算法係指要求搵到嘅答案同目標完全一樣：演算法一般都會俾出（通常係以數字形式存在嘅）答案，精準演算法要求個輸出同目標答案完全一樣，而近似演算法淨係要求個輸出同目標答案相近；例如喺RPG 遊戲當中計一吓攻擊中唔中－1 代表中、0 代表唔中－就係一個精準演算法，要求個答案同目標數值完全一樣，但喺好多統計學同機械學習上嘅應用當中，可能輸出嘅數量極大－原則上如果答案嘅數值係連續（可以小數點後有幾多個位都得）嘅，答案嘅可能數值會有無限咁多個，所以喺呢種情況下，串演算法只會務求俾出一個「理應會接近真實答案」嘅數值做輸出。即係話一個近似演算法會答嘅唔係「答案係幾多」而係「答案相信係喺幾多同幾多之間」或者「真正答案應該會接近呢個數值」。
爬山演算法係機械學習上一種常用嚟做最佳化（指研究點樣喺特定情況下將一個函數或者變數最大化或者最小化）嘅近似演算法；想像一個數學模型，有兩個參數，x{\displaystyle x}同y{\displaystyle y}，而家用指標z{\displaystyle z}嚟衡量個模型有幾「好」，而z{\displaystyle z}係數值愈細代表個模型愈理想嘅。家陣x{\displaystyle x}同y{\displaystyle y}經已有咗數值，所以個模型喺上圖入面有個座標位置，而個爬山演算法可以喺每一步噉做：
如果順利，個模型嘅z{\displaystyle z}值會慢慢變到愈嚟愈細（接近理想數值），不過原則上，呢個最後嘅答案值唔保證會係理想數值－可能喺睇到嘅空間外有更低嘅可能z{\displaystyle z}值，但呢串演算法唔會能夠保證將個模型帶到去嗰個數值－所以係一個近似演算法。
窮舉演算法指嘅係一啲「試嗮所有可能答案，再揀最好嗰個出嚟」嘅演算法，例如如果要由一個數列當中搵某一個特定嘅數字出嚟，可以用一個窮舉嘅方法：將個數列入面嗰啲數字逐個逐個攞嚟睇，睇吓嗰個數字係咪想要嗰個，最後俾個輸出。呢種做法喺個數列（例如）得三個數字嗰陣唔會有乜問題，但喺現實應用上，要耖嘅數列好多時都閒閒地有成幾千個數字，而喺（例如）教人工智能捉象棋嘅情況下，一盤象棋會有成上萬個可能情況，用窮舉演算法會嘥好多時間精神，實證嘅研究亦都表明咗窮舉演算法根本行唔通。
相比之下，好似正話提到嘅對分檢索方法就唔係一個窮舉演算法，因為呢串演算法唔會將所有可能嘅答案冚唪唥都睇一次；而廿一世紀嘅電腦科學界同相關領域喺解起問題上嚟都唔會用窮舉演算法，而係好多時會集中思考點樣由成千上萬個可能性當中揀一部份出嚟處理。可以睇吓近似演算法同蒙地卡羅樹搜索。
分治演算法係指重複係噉將手上要解嗰個問題砍件做一柞細啲（而且一個板）嘅問題，直至每一份細問題都有返咁上下簡單為止，即係話：
例如合併排序就係分治演算法嘅一種－合併排序係一種（喺某啲情況下好用嘅）排序演算法，會將要排好嘅數據列砍件做細細橛，將每一橛排好咗之後就可以將呢啲細數據列砌返埋一齊，形成一個排好咗嘅大數據列；而頭先提到嘅對分檢索都係合併排序嘅一種－對分檢索涉及到將要排嘅數列分拆做幾橛，將嗰幾橛逐個逐個排好咗之後再併返埋一齊做個輸出。
回溯法係指用遞歸一步一步建立一個答案，而發現個答案唔掂（唔符合由用家指定嘅條件）嗰陣，就放棄嗰個答案（「回溯」－返轉頭做過）郁手建立下一個答案，一路重複做，做到搵到個掂嘅答案為止。大致上可以噉樣想像：
例如下圖係用回溯法解數獨嘅動畫：
運算複雜度理論係運算理論嘅一個子領域，研究用演算法解決問題「有幾撈絞」－舉個例子說明，想像有個問題，先前嘅分析顯示咗，個問題係有可能用電腦解決嘅，但打後進一步嘅分析發現，解決呢個問題要用嗰串演算法複雜到就算用最先進嘅電腦行，都要用成 100 年先行得完，噉嘅話個問題喺實際應用上根本冇得有效率噉解決。對「可以解到嘅問題要嘥幾多時間空間先解到」嘅思考就係運算複雜度理論嘅重心。
運算複雜度理論嘅分析興用大 O 符號嘅方法嚟表示演算法嘅運算複雜度，一個大 O 符號會將一段演算法嘅時間複雜度（簡單講就係指段演算法嘥幾多時間）同空間複雜度（簡單講就係指段演算法嘥幾多記憶體）表達成輸入大細嘅函數，例如一個用窮舉演算法搜尋一個數列嘅演算法喺最壞情況下要耖嗮成個數列先會搵到想要嗰個數，即係話設數列嘅長度係n{\displaystyle n}，噉呢個窮舉演算法嘅最壞情況時間複雜度用大 O 符號寫嘅話，就會係O(n)，意思即係話數值同n{\displaystyle n}成正比。
而演算法可以按行嗮要幾耐嚟分類：
... 等等。
某啲難題可能會有幾個（唔同複雜度嘅）演算法解決到，又有啲難題係冇已知能夠有效率解決佢哋嘅演算法嘅。因為噉，科學家好多時都要喺解難嗰陣諗用乜嘢演算法最好，而複雜性就係量度「一串演算法有幾好」嘅重要指標之一：假設其他因素不變，科學家會鍾意用最簡單最唔嘥時間嘅演算法。
演算法設計係數學同各門工程學上一個受關注嘅課題。數學家同工程師會係噉嘗試諗一啲新嘅演算法出嚟，目標係要做起嘢上嚟更加有效率，例如有好多呢啲領域嘅科學家都致力於諗一啲步驟少過現有演算法嘅新演算法出嚟－假設其他因素唔變，步驟少啲嘅演算法做起嘢上嚟會快啲。演算法設計主要有以下呢啲步驟：
演算法分析係電腦科學嘅一門子領域，專門對演算法嘅各種特性作出分析，尤其係對運算複雜度嘅分析：好多時，同一個問題可以用好多個唔同嘅演算法嚟去解決；因為噉，軟件工程師同編程員等嘅人員好多時都會想知唔同嘅演算法當中邊啲好用啲，而要搵出個答案，佢哋就要知道每串演算法「所嘥嘅時間」同埋「用咗幾多行指令」等嘅資訊；某啲演算法可能（例如）用咗幾十行指令就解到第啲演算法要用成幾百行指令先解到嘅問題，前者行起上嚟會快啲，而且要儲落部電腦度嗰陣又會冇咁掗碇－自然啲人會更加鍾意用呢串演算法，例如頭先提到嘅對分檢索就俾人評定為好用過「將數列入面嘅數逐個逐個睇一次」嘅窮舉搜尋。
原則上，最抽象化嘅演算法分析係純數學性嘅：喺演算法分析當中，研究者可以齋靠抽象嘅數學符號嚟表達啲演算法，好多時根本唔使用真嘅程式語言寫低串演算法先；雖然係噉，絕大部份嘅演算法去到最尾都係要用某啲硬件或者軟件平台嚟執行嘅，而啲演算法執行嗰陣嘅效率就會俾人攞嚟評估串演算法。
演算法效率係演算法分析上至關重要嘅一個概念，指緊一串演算法要用幾多資源－用咗幾多行指令、幾多款指令呀噉－嚟解決一個特定嘅問題；假設第啲因素唔變，一串演算法愈係可以用少嘅資源－用嘅指令短、唔使用啲複雜同進階嘅指令－嚟解決一個問題，串演算法就愈會俾人覺得佢有效率，而工程師同科學家一般都鍾意高效率嘅演算法。高效率嘅演算法可以有用得好交關：例如係處理圖像用嘅快速傅立葉變換噉，有研究試過發現，快速傅立葉變換演算法方面嘅改進令到醫療上嘅圖像處理快咗成 1,000 倍咁多。
一串演算法嘅專利誰屬喺法律上係一個問題。專利係指由一個國家或者地區嘅政府俾發明一樣嘢嘅人，宣佈喺一段時間內（例如廿年內）嗰樣發明專屬於嗰位發明者，而想用嗰樣發明嚟賺錢嘅人通常都要俾錢個發明者先，然後個發明者先至俾佢哋有權運用嗰樣發明嚟賺錢，軟件專利就係泛指為咗電腦軟件－電腦程式、函式庫、用家介面同演算法等－而設嘅專利。
喺廿一世紀初，「齋靠一串演算法有冇得攞去申請專利」係一個受爭議嘅課題：喺 2006 年嘅美國，一個人係唔可以齋靠玩弄抽象概念、數字或者訊號嚟攞專利嘅，所以發明一串演算法冇得攞專利；之但係演算法嘅實現成品就可能有得申請專利－例如如果有個人諗咗個新演算法出嚟，並且用某隻程式語言寫咗段源碼嚟執行呢段演算法，噉嗰個人就有可能有得為嗰段源碼申請專利，而噉做實際上就係為個演算法申請咗專利；不過另一方面，又有唔少人都批評容許人為軟件伸請專利嘅做法，例如喺 2019 年嘅印度，專利呢家嘢淨係可以為「能夠喺工業上實際使用」嘅物件申請，而電腦程式同演算法唔俾印度官方定性為噉樣嘅物件，所以冇得申請專利－即係話喺有個人喺印度發明咗串演算法，第啲人可以隨意攞嚟用。