計算機科學基礎_4 - 算法,數據結構

算法入門

  • 選擇排序,Selection sort
  • 大O表示法,Big O notation
  • 歸併排序 - Merge sort
  • Dijkstra 算法

寫指數函數,只是無數解決方案的一種,還有其它方案。
用不一樣順序寫不一樣語句,也能獲得同樣的結果,不一樣的是「算法」,意思是:解決問題的具體步驟。
即便結果一致,有些算法會更好。
通常來講,所需步驟越少越好。不過有時候也會關心其餘因素,好比佔多少內存。node

「算法」一詞來自 阿爾●花拉子密,1000多年前的代數之父之一。
如何想出高效算法,是早在計算機出現以前就有的問題,誕生了專門研究計算的領域,而後發展成一門現代學科,計算機科學。程序員

選擇排序

記載最多的算法之一是「排序」。好比給名字,數字排序。
排序處處都是,找最便宜的機票,按最新的時間排郵件,按姓氏排聯繫人等等,這些都要排序。
計算機科學家花了數十年發明了各類排序算法,還起了酷酷的名字,「冒泡排序」, 「意麪排序」,「選擇排序」算法

好比:
一堆機票價格,都飛往目的地。把價格一組數據存放到一個數組結構。
先找到最小數,從最上面開始,而後和第一個比較,因此看最小的數是否變化,移動位置。重複循環比較,持續移動位置。
意味着,若是要排N個東西,要循環N次,每次循環中再循環N次,共N*N。編程

clipboard.png

算法的輸入規模運行步數之間的關係,叫算法的複雜度
表示運行速度的量級。
計算機科學家們把算法複雜度叫:大O表示法(big O notation)。數組

選擇排序的算法複雜度O(n^2)效率不高。數據結構

歸併排序

第一件事是檢查數組大小是否>1,若是是,就把數組分紅兩半。再檢查數組大小是否>1,若是是,繼續分組,若是不是開始「歸併」。
從前兩個數組開始,讀第一個(也是惟一一個)值,而後開始比較,若是更小,排在以前。重複這個過程,按序排列。而後數組大小增長,再次歸併。
一樣,取前二個數組,比較第一個數,而後再比較第一個數組的第一個數,第二個數組的第二個數。而後合併成更大有序的數組。
直到排序完整。編程語言

歸併排序的算法複雜度O(n * logn),n是須要比較+合併的次數和數組大小成正比,logn是合併步驟的次數。函數

圖搜索

clipboard.png

「圖」是用線鏈接起來的一堆「節點」。能夠想成地圖,每一個節點是一個城市,線是公路。
一個城市到另一個城市,花的時間不一樣,能夠用成本(cost)或權重(weight)來代稱,表明要幾個星期。
假設想找「高庭」到「凜冬城」的最快路線。
最簡單的方法是嘗試每一條路,計算總成本。這是「蠻力辦法」。測試

假設用蠻力方法,來排序數組,嘗試每一種組合,看是否排好序。
這樣的時間複雜度是: O(n!),n是節點數,n!是階乘。比O(n^2)還糟糕。人工智能

從「高庭」開始,此時成本爲0,把0標記在節點裏,其它城市標記成問號,由於不知道成本多少。Dijkstra 算法老是從成本最低的節點開始,目前只知道一個節點「高庭」,因此從這裏開始,跑到全部相鄰節點,記錄成本,完成一輪算法。
可是還未到「凜冬城」,因此再跑一次Dijkstra 算法
下一個成本最低的節點,是「君臨城」,記錄相鄰節點的成本,到「三叉戟河」,然而想記錄的是,從「高庭」到這裏的成本,因此「三叉戟河」的總成本8+5=13。如今走另一條路到「奔流城」,成本高達25,總成本33,但「奔流城」中最低成本是10,因此無視新數字,保留以前的成本10。如今看了「君臨城」的每一條路還沒到「凜冬城」因此繼續。
下一個成本最低的節點,是「奔流城」,要10周。先看到「三叉戟河」成本,10+2=12,比以前的13好一點,因此更新「三叉戟河」爲12,「奔流城」到「派克城」成本是3。10+3=13,以前是14,因此更新「派克城」爲13。
「奔流城」出發的全部路徑都走了遍,還沒到終點,因此繼續。
下個成本最低的節點,是「三叉戟河」。從「三叉戟河」出發,惟一沒看過的路,通往「凜冬城」。成本是10加上「三叉戟河」的成本12,總成本是22。再看最後一條路,「派克城」到「凜冬城」,總成本是31。
因此最佳線路是:「高庭」 -> 「奔流城」 -> 「三叉戟河」 -> 「凜冬城」。

clipboard.png
clipboard.png
clipboard.png
clipboard.png

Dijkstra的語錄:
「有效的程序員不該該浪費不少時間用於程序調試,他們應該一開始就不要把故障引入。」
「程序測試是代表存在故障的很是有效的方法,但對於證實沒有故障,調試是很無能爲力的。」

Dijkstra 算法算法複雜度是:O(n^2)通過改造的Dijkstra 算法複雜度減低爲:O(n * logn + l) n是節點數,l是多少條線

數據結構

  • 數組,Array
  • 字符串,String
  • 矩陣,Matrix
  • 結構體,Struct
  • 指針,Pointer
  • 節點,Node
  • 鏈表,Linked List
  • 隊列,Queue
  • 棧,Stack
  • 樹,Tree
  • 二叉樹,Binary Tree
  • 圖,Graph

算法處理的數據,存在內存裏的格式是什麼。
但願數據是結構化,方便讀取,所以計算機科學家發明了「數據結構」。

數組

數組Array,也叫列表(List),或向量(Vector)。有一些區別,在不一樣語言中基本相似。

數組的值一個個連續存在內存裏。能夠把多個值存在數據變量裏,爲了拿出數組中的某個值,要指定一個下標(index)
大多數編程語言中,數組下標都從0開始。
用方括號[]表明訪問數組。

j = [5, 3, 7, 21, 82, 4, 19]
a = j[0] + j[5]

數組存在內存裏的方式,十分易懂。
爲了簡單,假設編譯器從內存地址1000開始存數組,數組有7個數字,像上圖同樣按順序存。
j[0],會去內存地址1000,加0個偏移,獲得地址1000,拿值: 5。
j[5],會去內存地址1000,加5個偏移,獲得地址1005,拿值: 4。

clipboard.png

數組的用途普遍,因此幾乎全部的編程語言,都自帶了不少函數來處理數組。

字符串

數組的親戚是字符串String,其實就是字母,數字,標點符號等,組成的數組。

計算機怎麼存儲字符?
經過字符對應的ASCII表

寫代碼時,用引號括起來就好了: j = "STAN ROCKS"

雖然長的不像數組,但的確是數組。

注意:字符串在內存裏以0結尾,不是「字符0」,是二進制「0」,這叫字符「null」,表示字符串結尾。
這個字符很是重要,若是調用print()函數,會從開始位置,逐個顯示到屏幕,可是得知道何時中止下來。不然會把內存裏全部東西都顯示出來。0告知函數什麼時候停下。

clipboard.png

由於計算機常常處理字符串,因此有不少函數專門處理字符串。

矩陣

能夠用數組作一維列表,但有時想操做二維數據,好比電子表格,或屏幕上的像素。那麼須要 矩陣
Matrix

能夠把矩陣當作數組的數組,內存中的表示:

j = [[10, 15, 12], [8, 7, 42], [18, 12, 7]]

clipboard.png

clipboard.png

爲了拿一個值,須要兩個下標,好比j[2][1],告知計算機在數組2裏,位置是1的元素。

clipboard.png

結構體

把幾個有關係的變量存在一塊兒,會頗有用。多個變量打包在一塊兒叫 結構體(Struct)
多個不一樣類型數據,能夠放在一塊兒。

這些數據在內存裏,會自動打包在一塊兒,若是寫j[0],能拿到j[0]裏的結構體,而後拿具體數據。

clipboard.png

存結構體的數組,和其它數組同樣,建立時就有固定大小,不能動態增長大小。還有,數組在內存中,按順序存儲。在中間插入一個值很困難。但結構體能夠創造更復雜的數據結構,消除這些限制。

節點和鏈表

一個結構體,叫節點(Node)。它存一個變量,一個指針(pointer)
「指針」是一種特殊的變量,指向一個內存地址,所以得名。

用節點能夠作鏈表(linked list),鏈表是一種靈活數據結構,能存不少個節點(node), 靈活性是經過每一個節點指向,下一個節點實現的。

假設有三個節點,在內存地址1000,1002,1008。
內存地址隔開,多是建立時間不一樣,它們之間有其它數據,能夠看到第一個節點,值是7,指向地址1008,表明下一個節點,位於內存地址1008,來到下一個節點,值是112,指向地址1002,往下一個節點,地址1002的值是14的節點,這個節點,指回地址1000,也就是第一個節點,這個叫循環鏈表。
但鏈表也能夠是非循環的,最後一個指針是0null,表明鏈表的盡頭。

clipboard.png

clipboard.png

clipboard.png

當程序員用鏈表時,不多看指針具體指向哪裏,而是用鏈表的抽象模型。

clipboard.png

數組大小須要預先定好,鏈表大小能夠動態增減,能夠建立一個新節點,經過改變指針值,把新節點插入鏈表中。

clipboard.png

鏈表也很容易從新排序,兩端縮減,分割,倒序等。

由於靈活,不少複雜的數據結構都用鏈表。

最出名的是 隊列(queue)和棧(stack)。

隊列和棧

「隊列」就像郵局排隊,誰先來就排在前面。先進先出(FIFO)。
有個指針,指向鏈表的第一個節點,第一個節點是Hank,服務完Hank以後,讀取Hank的指針,把指針指向下一我的,這樣就把Hank「出隊」(dequeue)了。

clipboard.png

clipboard.png

若是想加到隊列裏,「入隊」(enqueue)。
要遍歷整個鏈表到結尾,而後把結尾的指針,指向新人(Nick)。

clipboard.png

只要稍做修改,就能用鏈表作棧,棧是後進先出(LIFO)。能夠把「棧」想成一堆鬆餅,作好一個新鬆餅,就堆在以前的上面,吃的時候,是從最上面開始吃的。棧出入叫「入棧(push)」,「出棧(pop)」。

若是節點改一下,改爲2個指針,就能作成樹(Tree)
不少算法用了樹,這種數據結構,一樣程序員不多看指針的具體值,而是把樹抽象成:

  • 最高的節點叫作「根節點(root)」
  • 根節點下的全部節點,都叫「子節點(children)」
  • 任何子節點的直屬上層節點,叫「父節點(parent node)」
  • 沒有任何「子節點」的節點,也就是「樹」結束的地方,叫「葉節點(leaf)」。

clipboard.png

其中有一個特例,節點就2個,叫「二叉樹(Binary Tree)」。

節點能夠用鏈表存全部的子節點。
「樹」的一個重要性質是(無論是現實仍是數據結構中):「根」到「葉」都是單向的。

若是數據隨意鏈接,包括循環,能夠用「圖」表示。這種結構,能夠用有多個指針的節點表示,所以沒有根,葉,子節點,父節點這些概念。能夠隨意指向節點。

不一樣數據結構使用於不一樣場景,選擇正確數據結構會讓工做簡單。

圖靈

  • 可斷定性問題
  • Lambda算子
  • 圖靈機
  • 停機問題
  • 圖靈測試

計算機科學之父,阿蘭●圖靈。
於1912年出生倫敦,從小就表現出驚人的數學和科學能力。
對計算機科學的建樹始於1935年,他開始解決德國數學家提出的問題:可斷定性問題,是否存在一種算法,輸入正式的邏輯語句,輸出準確的「是」或「否」答案?

美國數學家阿隆佐●丘奇於1935年首先提出解決方法,開發了一個叫「Lambda 算子」的數學表達系統,證實了這樣算法不存在。
雖然「Lambda 算子」能表示任何計算,但它使用的數學技巧難以理解和使用。

圖靈想出了本身辦法來解決「可斷定性問題」,提出了一種假想的計算機,如今叫「圖靈機」。
圖靈機提供了簡單又強大的數學計算模型,雖然用的數學不同,但圖靈機的計算能力和Lambda 算子同樣。同時由於圖靈機更簡單,因此在新興的計算機領域更受歡迎。

圖靈機是一臺理論計算設備,有無限長的紙帶,紙帶能夠存儲符號,能夠讀取和寫入紙帶上的符號,還有一個狀態變量,保存當前狀態,還有一組規則,描述機器作什麼。規則是根據:當前狀態+讀寫頭看到的符號,決定機器作什麼。結果多是在紙帶寫入一個符號,或改變狀態,或把讀寫頭移動一格,或執行這些動做的組合。

圖片描述

簡單例子:
讓圖靈機讀一個以0結尾的字符串,並計算1個出現次數,是否是偶數。若是是,在紙帶上寫一個1,若是不是,在紙帶上寫一個0。

首先要定義「圖靈機」的規則:

  • 若是當前狀態是偶數,當前符號是1,那麼把狀態更新爲「奇數」,把讀寫頭向右移動。
  • 若是當前狀態爲偶數,當前符號是0,意味着到了字符串結尾。那麼在紙帶上寫一個1,而且把狀態改爲停機(halt),狀態改成「停機」,是由於圖靈機已完成計算。
  • 還須要2條規則,來處理狀態爲奇數的狀況:
    一條處理 奇數 + 紙帶是0的狀況
    一條處理 奇數 + 紙帶是1的狀況
  • 最後,要決定機器的初始狀態,定成「偶數」。

圖片描述

圖片描述

圖片描述

定義好了 起始狀態+規則,就像寫好了程序,如今能夠輸入了,假設把「1 1 0」放在紙帶上,有兩個1,是偶數。
規則只讓讀寫頭向右移動,其它部分可有可無,爲了簡單留空。

圖靈機開始運行:

  1. 機器起始狀態爲「偶數」,看到的第一個數是1,符合第一條規則,因此執行對應的步驟,把狀態更新到「奇數」,讀寫頭向右移動一格。
  2. 而後又看到1,可是機器狀態是「奇數」,因此執行第三條規則,使機器狀態變回「偶數」,讀寫頭向右移動一格。
  3. 如今看到0,而且機器狀態是偶數,因此執行第二條規則。在紙帶上寫1,表示「真」的確有偶數個1。
  4. 而後機器停機。

圖片描述

若是有足夠時間和內存,能夠執行任何計算,它是一臺通用計算機。
只要有足夠的規則,狀態和紙帶,能夠創造任何東西。

因此,圖靈機是很強大的計算模型。
事實上,就可計算和不可計算而言,沒有計算機比圖靈機更強大,和圖靈機同樣強大的,叫「圖靈完備」。

每一個現代計算系統,好比筆記本電腦,智能手機,甚至微波爐和恆溫機內部的小電腦,都是「圖靈完備」。

停機問題

爲了回答可斷定性問題,把圖靈機用於一個有趣計算的問題:停機問題。
簡單說就是:給定圖靈機描述和輸入紙帶,是否有算法能夠肯定,機器會永遠算下去仍是會到某一點會停機?

有沒有辦法在不執行的狀況,弄清會不會停機呢?一些程序可能要運行好幾年,因此在運行前知道,會不會出結果頗有用。不然就要一直等啊等,憂慮到底會不會出結果。圖靈經過一個巧妙的邏輯矛盾,證實了停機問題是沒法解決的。

想象有一個假想圖靈機,輸入: 問題的描述 + 紙帶的數據,輸出「yes」表明會停機,輸出「no」表明不會停機。

clipboard.png

推理:若是有個程序,H(halt的第一個字母)沒法判斷是否會「停機」,意味着「停機問題」沒法解決。
爲了找到這樣的程序,圖靈用H設計了另外一個圖靈機,若是H說程序會「停機」,那麼新機器會永遠運行(即不會停機),若是H的結果爲No,表明不會停機,那麼讓新機器輸出No,而後「停機」。

圖靈機1(H): 輸出yes->停機, 輸出no->不會停機
圖靈機2:H(yes) -> 不會停機,永遠運行, H(no)-> 機器停機,輸出no

實質上是一臺和H輸出相反的機器,若是程序不停機,就機器停機。若是程序停機,就機器永遠運行下去。

還須要在機器前面加一個分離器,讓機器只接收一個輸入,這個輸入既是程序,也是輸入。把這臺新機器叫「Bizzaro(DC漫畫的一名反派角色,他的能力和超人相反)」

clipboard.png

若是把「Bizzaro」的描述,做爲自己的輸入會怎樣,意味着在問H,當「Bizzaro」的輸入是本身時,會怎樣。
但若是H說「Bizzaro」會停機,那麼「Bizzaro」會進入無限循環,所以不會停機。
若是H說「Bizzaro」不會停機,那麼「Bizzaro」會輸出No而後停機。
因此H不能正確斷定,停機問題,由於沒有答案,這是一個悖論,意味着「停機問題」不能用圖靈機解決。

圖靈證實了圖靈機能夠實現任何計算。
可是,「停機問題」證實了,不是全部問題都能用計算解決。

丘奇和圖靈證實了計算機的能力有極限
不管多少時間或內存,有些問題,是計算機沒法解決的,計算是有極限的,起步了可計算性理論,稱之爲:「丘奇-圖靈論題」。

圖靈測試

當時是1936年,圖靈只有24歲,在1939年,二次世界大戰,圖靈的才能很快被投入戰爭,事實上,在戰爭開始前一年,已經在英國政府的密碼破譯研究。工做之一,是破解德國的通訊加密,特別是「英格瑪機(Enigma)」加密信息。
簡單說,英格瑪機會加密明文,若是輸入字母H-E-L-L-O,機器輸出X-W-D-B-J,這個過程叫「加密」。
文字不是隨便打亂的,加密由「英格瑪機」頂部的齒輪組合決定。每一個齒輪有26個可能位置,機器前面還有插板,能夠將兩個字母互換。總共有上十億種可能。
知道正確的齒輪和插頭的設置,輸入 X-W-D-B-J就會輸出hello,解密了這條信息。

有數十億的組合,根本無法手工嘗試全部組合。英格瑪機和操做員不是完美的,一個大缺陷是:字母加密後毫不會是本身。H加密後絕對不是H。
圖靈在前人的基礎上,設計了一個機電計算機,叫: Bombe。利用了這個缺陷,它對加密消息嘗試多種組合,若是發現字母解密後和原先同樣,就知道英格瑪機不會這樣子作,這個組合會被跳過,接着試另外一個組合,Bombe大幅度減小了搜索量,讓破譯人員把精力花在更有可能的組合,好比在解碼文本中找到經常使用的德語單詞。
德國人時不時會懷疑有人在破解,而後升級英格瑪機,好比加一個齒輪,創造更多可能組合,甚至還作了全新的加密機,整個戰爭期間,圖靈和同事都在努力破解加密,解密到德國情報,爲盟軍贏得了不少優點,有些史學家認爲他們把戰爭縮短好幾年,戰後,圖靈回到學術界,爲許多早期計算機工做做出貢獻。
好比曼切斯特1號,一個早期有影響力的存儲程序計算機。但他最有名的戰後貢獻是「人工智能」。這個領域很新,直到1956年纔有名字。

1950年,圖靈設想了將來的計算機,擁有和 人類同樣的智力,或至少難以區分。
圖靈提出若是計算機能欺騙人類相信它是人類,纔算是智能。這成了智能測試的基礎,現在叫「圖靈測試」

想象在和兩我的溝通,不用嘴或面對面,而是來回發消息,能夠問任何問題,而後會收到回答,但其中一個是計算機,若是分不出哪一個是人類,哪一個是計算機,那麼計算機就經過了圖靈測試。

這個測試的現代版叫:「公開全自動圖靈測試,用於區分計算機和人類」,簡稱,驗證碼,防止機器人發垃圾信息等。

圖靈於1954年服毒,年僅41歲。

因爲圖靈對計算機科學貢獻巨大,許多東西以他命名,其中最著名的是「圖靈獎」(計算機領域的最高獎項)。

  • 可計算性理論
  • 斷定問題
  • 電子計算機
  • 人工智能
  • 數理生物學
  • 圖靈試驗
  • 破解德國的著名密碼系統英格瑪機(Enigma)
相關文章
相關標籤/搜索