一、計算機科學修鍊之路——概述篇
前言
之前自己私下便已在與多位好友的談話中得知他(她)們想涉足計算機領域的想法,但又不知如何入門。儘管自己功力尚淺,也希望能通過一些具有深度的文章,來為大家打開進入計算機世界的大門。
從專欄申請到開始在鍵盤上敲下這些字,過去了將近半月,遲遲不敢動手。一是自己能力不足,二是自己確實真切希望能寫些對大家有幫助的文章,而不是百度上所充斥的各種博客里Ctrl + C而來的東西。
這半個月中,自己思考過要再這個專欄里寫些什麼內容,結合自己的專業與當前熱點,目前的粗略計劃是從本文的概述篇開始,接著是程序語言篇、數據結構與演算法篇、人工智慧篇。其中,程序語言篇將介紹三種應用場景不同但又廣泛使用的語言——C++、JavaScript、Python。C++在操作系統、遊戲開發、計算機視覺等對性能要求很高的環境中使用較多,儘管很多初學者抱怨C++太難不易入門,但掌握C++之後能打通學習編程語言的任督二脈;JavaScript在網站開發中的起著很重要的作用——前端工程師使用其來實現各種交互效果,而隨著Google發布V8引擎,以JavaScript為核心的node.js開始廣泛用於網站後端的實現,不止如此,「所有能用JavaScript實現的技術最後都將用JavaScript實現」;Python也廣泛用於網站後端的實現,除此,在人工智慧領域,比如機器學習,大量代碼都使用Python實現,儘管在強調性能的模塊仍將使用C++。在計算機軟體領域,程序設計語言可以看作工具,用於實現我們的各種想法,但要想進行有效率的實現,光有工具不夠,還得有方法。數據結構和演算法便是實現的方法。數據結構能夠讓我們掌握如何使用程序設計語言對數據進行有效的組織利用(空間方面);而演算法則是一種問題的解決方案,能夠實現我們的想法,一般來說,演算法效率(時間方面,可以理解為解決問題的方法所花的時間)越高越好。人工智慧領域涉及非常多的方向,自然語言處理(Nature Language Processing,簡稱NLP),計算機視覺(Computer Vision),機器學習(Machine Learning),自動駕駛等目前非常火熱的領域都是人工智慧的一個分支。
專欄名字之所以為「計算機科學修鍊之路」,是因為本人也是一位學生,希望能和大家一起修鍊成長。文中因為自己能力和視野所造成語言不當或有失偏頗之處,以及有錯誤見解的地方,歡迎大家留言並積極提出。
除此之外,自己還將邀請同學一起撰寫專欄的相關部分。到現在為止,我已經邀請了我的大學本科同學、目前保送至清華大學深圳研究生院的戶建坤同學撰寫數據結構與演算法部分。他在本科期間曾獲得「北京航空航天大學程序設計競賽」冠軍並多次獲得ACM區域賽獎牌。
1. 漫談計算機科學
1.1 計算機的發展
很多人可能在小學時期便已了解到關於世界上第一台電子計算機的描述——「龐然大物」,「佔滿一整個房間」等等——目前較為普遍的說法認為,世界上第一台電子計算機是1946年2月14日誕生於美國賓夕法尼亞大學的ENIAC。而在這之後的幾十年時間裡,從第一代的電子管計算機,第二代的晶體管計算機,第三代的中小規模集成電路計算機,第四代的大規模和超大規模集成電路計算機,計算機硬體開始了高速發展,性能也較過去有了大幅提升。有一條著名的摩爾定律在一定程度上描述了此變化:當價格不變時,集成電路上可容納的元器件的數目,約每隔18-24個月便會增加一倍,性能也將提升一倍。之所以說一定程度上,在於摩爾定律已經不適用於目前的計算機發展狀況。考慮到CPU頻率越高發熱越大,再一味提升CPU的運算速度已經不適合了。計算機硬體並非我的專業方向,因此這裡無法向各位做更為詳細的介紹。
1.2 二進位的計算機世界
也許你在看這篇文章之前就已經聽說過各種編程語言,Visual Basic,C,JAVA等等。這些語言都能夠構造在計算機上運行的軟體系統。但是,計算機並不能直接理解這些語言以直接運行,計算機實際上運行的是以0和1表示的二進位可執行文件。
在數字和其它計數法產生之前,遠古時期的人類祖先用手指計數,所以現在我們學習的數學採用十進位,也就是「逢十進一」。而計算機的世界則採用二進位,也就是「逢二進一」。而關於計算機之所以採用二進位的原因也非常多,這裡不一一講述。
可執行文件里的可執行文件並非一些無序的0和1的組合,它們是一些有規律的指令組合而成。比如8位的指令01011010,前四位0101代表將操作數送入內存的指令,後四位1010表示對操作數進行加法運算的指令。這裡只是舉個例子,真實的指令(比如32位指令和64位指令)的解釋執行實際上要複雜許多。
計算機硬體上的飛速發展同時也帶動了軟體的發展。而計算機軟體的開發方式及開發語言也有著相當大的改變。
最初的計算機編程是在卡片或紙帶上穿孔,如下圖所示。
有孔表示1,沒孔表示0。看到這裡你肯定會想,那這樣編寫程序得多麻煩,指令那麼多,光記住0和1的組合就很難了,更別說編寫程序了。為了更高效地編寫計算機程序,程序設計語言便誕生了。
2. 程序設計語言概述
由於程序設計語言眾多(來來來,我們簡單列列:Pascal,Visual Basic,DELPHI,Perl,Ruby,C,C++,C#,Java,JavaScript,Python,Objective-C,LISP……),要想學完這些語言幾乎是不可能的,而且之後也會出現新一代的程序設計語言,因此我選取了C++、Python、JavaScript這三種語言作為大家入門計算機學科的程序設計語言。
總的來說,程序設計語言可以分為機器語言、彙編語言、高級語言三大類。機器語言也就是由0和1組合而成的指令集。彙編語言更像是一種助記符。我們可以先嘗試理解以下執行簡單運算的MIPS彙編指令:
subt$t2,$t3,$t4n addit$t2,$t3, 5n addut$t1,$t6,$t7n
上面指令中的$t2、$t3、$t4表示寄存器,計算機在進行數值運算時,需要取寄存器中的操作數,再由前面的指令(sub,addi,addu)得到結果(這裡大大簡化了MIPS的指令運行原理)。我們假設操作數a在寄存器$t2中,操作數b在寄存器$t3中,操作數c在寄存器$t4中,則第一行表示的是a = b - c。addi和addu表示加法(更準確的說,addi表示寄存器中的數和立即數相加,addu則表示兩個無符號數的加法),和第一行的指令同理,我們能很快知道所表達的意思。
而高級語言對彙編語言來說,則更為直觀。以C++為例執行簡單的操作數運算:
int a = 1;nint b = 2;nint c, d;nc = a - b;nd = a + b;n
忽略前三行的變數聲明,後兩行的操作數運算是不是比彙編要直觀上許多?
實際上,C++代碼和彙編指令集之間存在某種聯繫。如果本節的內容讓你有所疑惑,不妨在掌握一門高級程序設計語言之後閱讀《計算機組成與設計 硬體/軟體介面 》第三版與《深入理解計算機系統》。前者從計算機的硬體設計方面講述,同時涉及MIPS指令集和一些操作系統設計方面的知識;後者從程序員的角度出發,深入淺出地講解計算機更底層的一些運行細節,其中會涉及X86指令集。
當然了,程序設計語言應該只把它作為一種工具。不同場景下要使用的工具也不同,挖土要用鐵鍬;鑽孔要用鑽頭;即使是殺豬和宰牛,用的刀具也不一樣。當前編譯器為了講究效率,有的部分甚至使用了彙編;構建跨平台項目大都會使用Java;構建Windows桌面應用通常使用C#;Web開發由於應用場景與需求的不同,一般使用的技術架構也會有很大差異……
「工欲善其事,必先利其器」,掌握好了一門語言對於開發工作有時可以起到事半功倍的效果。但是,僅僅掌握一門或幾門語言,但是沒有數據結構和演算法能力的話,要想學習更頂層的知識(比如機器學習、數據挖掘)肯定會遭遇瓶頸的。
3. 什麼是數據結構與演算法
3.1 概念
數據結構是放數據的,便於使用(操作數據或者存儲調用方面),平時就用簡單的int,double變數啥的就好,但是多了就用數組方便 ,再多了數組不好記用類封裝起來有了含義,但是添加刪除多了數組太慢,就用到set,map等等,又想描述數據之間複雜的關係就有了樹和圖。這是計算機領域的智慧之一,解決問題首先想想描述數據怎麼描述。
演算法就是解決一些問題的過程,其實就是很多經典問題的經典解決辦法,並且可以拼湊起來解決一切問題( ̄▽ ̄)吧。並且這些演算法中會用到數據結構(比如圖,樹,好的樹),所以熟練掌握演算法就跟背完了高考的例題一樣,非常重要。
3.2 用處
本質的用處在概念中已經說了,實際上,這兩個的用處主要還真是思維上的。在平時工作時候使用的(1)是封裝好的數據結構,不用實現(2)經常會在網上參考別人,不用自己琢磨演算法。但是這樣說就明白它的用處了:(1)要分析透封裝好的數據結構,知道特點,分析空間,時間複雜度,以及常數時間複雜度好不好啥的(2)大致知道一個問題可能有幾個解決的辦法,這樣才能在網上搜(3)明確明白什麼問題可解,什麼問題解不出來,或者我把條件限制成什麼樣就可以簡單的求解。這都是需要熟練掌握基本的演算法的前提下才可以的。當然還有個用處,就是筆試,面試演算法題,這個是我寫這部分的主要目的和方式。當然演算法題=基礎演算法+數據結構+題意理解+腦筋急轉彎。
4. 如何入門計算機科學
雖然是計算機科班出身,但自己本科四年也是走了不少彎路。這裡僅以個人經歷為經驗總結。
最開始自然是要掌握一門程序設計語言。掌握的意思是能熟練使用該語言解決一些問題。不過入門容易,精通難。很多語言,如果沒有多年的使用與積累,妄談精通是一種愚昧的表現。這個過程中,我們不僅需要大量的代碼練習,還要不斷總結思考。僅僅寫出自己的代碼是不夠的,我們要多學習別人的代碼,使之更為精鍊和優雅(儘管有時「魚與熊掌不可兼得」,但這應該稱為一個目標和要求)。
其次是項目經驗的積累。可以從一個小型項目開始,比如從1000行代碼量的項目開始,然後3000行,5000行,10000行……這裡的一個悲哀之處是,如果不是從事計算機行業的業餘學習者,要獲得這麼多代碼量的項目積累是很難的。網上的開源項目可以學習,比如GitHub上有大量的開源項目,可以找到適合自己的項目並加以學習。其中,也許你會需要掌握git的使用(網上有大量的教程,但是如果有需要的話我也會在專欄里涉及這部分)。
當然還有文檔的閱讀能力。工作中會有很多問題需要解決,這其中有第三方的代碼庫可能會提供解決方案,而代碼庫的使用便需要我們閱讀別人寫的文檔。文檔的閱讀能力也需要在項目中加以鍛煉。
還有一個很重要的能力——問題搜索能力。自己遇到的很多問題前人也有很大可能已經遇到並且已經解決過,對於自己無法解決的,要善於利用搜索引擎和專業的計算機問答網站。百度和谷歌不去評論好壞,自己的經驗是中文找百度,英文找谷歌(也許使用谷歌需要翻牆)。專業的問答網站有stackoverflow和segmentfault等。StackOverflow是一家專業的英文為主的計算機問答網站,segmentfault則以中文為主。在這些網站上面,你可以隨時提出自己的問題,會有專業人士來解答。
5. 數學之美
首先請原諒我在這裡盜用吳軍博士的《數學之美》一書的書名。
剛開始進入大學之時,周圍的同學就說,學好計算機數學一定要好。於是即使自己誤了其他課程的學習,數學卻是沒有落下過,儘管和我說這句話的同學數學成績比較糟糕。
學了高等數學有什麼用?我不懂微積分,不懂傅里葉級數不是照樣可以寫代碼、擼項目?學了線性代數又有什麼用?我不會算行列式、不懂矩陣運算、不知道秩是什麼東西不是照樣可以看懂各種文獻?概率統計又算什麼?就算我條件概率算的溜、大數定律的公式做夢也能寫出來,我也不能抱著計算機在拉斯維加斯的賭場里發家致富吧?
先讓我們從概率統計中的馬爾可夫鏈說起。假設我們要根據前t-1個時刻預測t時刻的氣溫,馬爾可夫為簡化問題,提出一種簡化的假設,即隨機過程中各個狀態的概率分布,只與它的前一個狀態有關,即 ,這個假設的隨機過程就稱為馬爾可夫鏈。接著再說說隱含馬爾可夫模型。隱含馬爾可夫模型是馬爾可夫鏈的一個擴展:任一時刻t的狀態 是不可見的。所以觀察者沒發通過觀察到一個狀態序列來推測轉移概率等參數。但是,隱含馬爾可夫模型會在每個時刻t輸出一個符號 ,而且 跟相關且僅跟 相關。看起來是不是很簡單?但就是這樣一個簡單的模型,已經「陸續成功地應用於機器翻譯、拼寫糾錯、手寫體識別、圖像處理、基因序列分析……」。
馬爾可夫鏈的例子來源於吳軍博士《數學之美》一書。有興趣的讀者可以在閑暇之餘閱讀該書,並發現自己的興趣點。
最後
本篇的內容僅作為引導性文章,目的是激發讀者從事計算機科學學習的興趣。「師傅領進門,修行靠個人」,更多更深的內容,可通過閱讀文中提及的書籍獲取。
為了保持內容的嚴謹性和專業性,撰寫過程中也查閱了不少資料;同時,為了讓文章不是那麼晦澀難懂,我將自己理解之後的內容加以陳述。這是我個人第一次寫專欄,經驗不足。如果各位讀者有什麼意見或者建議,歡迎進行評論或者私信討論。當然,如果你覺得本專欄對你的學習有幫助,歡迎推薦給周圍對計算機學習感興趣的非計算機專業人員(計算機專業人員也是可以的,~_~)。
下一篇將介紹高級程序設計語言,敬請期待!
推薦閱讀:
※如何解釋多色球通過高爾頓釘板後不同顏色的球分離開的動圖?
※有人認為人的大腦類似計算機的內存而非 CPU,大家如何看待?
※未來已來:摩爾定律盡頭的計算科學
※摸爬滾打學設計模式