數學與計算學

2009/1/2

以我的觀點,數學是以基本元件來構築事物、表達概念。而數學家藉由數,嘗試構築每一件事物、表達每一個概念,拼湊出世界的全貌。比如位置、形狀、關係、轉換、遞迴、極限、比較、排列、正反、假設,這些東西都是數。又比如有、無、聚、散、疏、密、盈、虧、彎、直、交、錯、動、靜,這些東西也都是數。與物體的行為有關的數,就是物理;與物質性質變化有關的數,就是化學;與生命運作有關的數,就是生物學。繼續細分下去的話,我們所知的各種東西,其實皆可說是數。

在數當中,可以用數量來表示的,便可以計量。像是情緒、風格、謀略,難以用數量來表示,也就難以計量,甚至不可計量。像是動作、旋律、次序,可以部分地或完全地用數量表示,便得以計量。計算學是以數量來構築事物、表達概念。而計算學家藉由數量,嘗試量度各種事物、各種概念,掌握其確切的程度與層次。

程式解題論

2010/6/19

程式解題是很雜亂的一門知識,茲分類為以下五項:智力思考、數學、計算學(資料結構、演算法)、計算機操作基礎(程式語言、計算機概論)、計算機操作實務(程式設計技巧)。

不同領域的人對於程式解題會有不同期待。參與演算法競賽的學生,期望加強智力思考、數學、計算學等能力。業界的程式設計師,期望加強計算機操作理論、計算機操作實務等能力。

這五項環環相扣,但以個人經驗,這五項可以分別學習。

一、智力思考:藉由智力測驗問題、益智遊戲(如數獨、孔明棋、倉庫番)、數學科普書等等,可以活絡大腦思路,培養觀察問題與分析問題的能力。

二、數學:從學校教科書可以學到很多數學概念、數學方法、甚至是數學公式,套用在問題上面來解決問題。

三、計算學:從學校教科書和網路上的資源,可以學到很多計算方法,套用在問題上面來解決問題。

四、計算機操作基礎:從程式語言的書籍(C++ Primer、Effective C++、程式設計師的自我修養)、計算機概論等書中學習。

五、計算機操作實務:從open source與其他人寫的程式碼中學習一些寫程式的原則以及漂亮的寫法。

給想嘗試程式解題的人作為參考。

我為什麼WA?

2012/3/26

常常有人在討論區問這種問題。通常也不太有人回答。

一個從事程式設計的人,必須保證自己寫的每一行程式碼都是安全正確的。這跟如何解題沒有關係,只跟這個人對程式語言的熟悉程度、跟這個人寫程式的態度有關係。如果沒有能力看出自己的程式為什麼會錯,還需要別人幫忙找錯,那麼這種人就不太適合開發程式了──自己開發出來的程式,竟然自己都不知道為什麼會錯,那誰還會期望這個人能夠寫出正確的程式呢?

大多數的時候,自己的程式被判定成Wrong Answer、Runtime Error,是因為誤用了程式語言而造成的。初學者容易犯錯,當然也需要有經驗的人從旁指導,但是千萬不可把「我得找個人從旁指導我」視為理所當然。訓練程式設計,其實就是訓練自己去了解程式語言、訓練自己有能力搞定一支錯誤的程式、訓練自己有能力寫出正確的程式。每當遇到Wrong Answer、Runtime Error的時候,首先要看看自己的程式有沒有語法上的誤用、邏輯上的謬誤,看看程式碼是否如自己所想般運作,然後推測錯誤、修正錯誤。這才是一個開發程式的人應有的能力,或說是態度。

要找出程式的錯誤,方法很簡單:確認每一行程式碼的計算結果都是正確的。如果是迴圈和遞迴,那就要多加費心,儘管程式碼只有短短一行兩行,還是要確定迴圈和遞迴的每個步驟都是正確的。另外,內建函式庫要多加注意,因為這是自己最不能掌控的東西,無法預期它們會跑出自己想要的結果;在使用內建函式庫之前,最好花時間翻書查網頁熟悉一下,並且實際寫幾行測試用的程式碼來試試它們的功能。

現下的IDE提供了強大的debug功能,可以逐行觀看程式碼的執行結果,讓使用者能夠輕鬆的判斷每一行程式碼是不是正確的。如果覺得IDE的debug工具太難用,也可以直接採用最原始的printf/cout方式(記得flush),適當地註解程式碼,並且把每一行的執行結果印出來看看,就知道哪裡有錯了。

等這些都確定沒有錯之後,再來想想看是不是有一些特殊的例子沒有考慮到,演算法正不正確。通常這個時候,跟自己志同道合的人,就會有興趣一起來討論「如何解題」了。

最大流演算法整理

2010/7/23

關於最大流問題的演算法,網路上資料很多,名稱天花亂墜,內容眾說紛紜,看著看著都糊塗了。

我手邊有兩本談到網路流的書:

Introduction to Algorithms [CLRS]

Network Flows: Theory, Algorithms and Applications [AMO]

就拿這兩本書進行整理。

首先是CLRS的部份,讀者可以隱約發現最大流問題的演算法,在這本書中被分類成兩種主要的類型,一種是以Ford-Fulkerson演算法為基礎的擴充路徑類型,另一種是以Push-Relabel演算法為基礎的預流推進+高度標號類型。

1. Ford-Fulkerson演算法:不斷找擴充路徑。時間複雜度O(EF),這是假設找一次擴充路徑需時O(E)的情況下。F是最大流流量。

2. Edmonds-Karp演算法:不斷找最短擴充路徑。注意到實作的時候,是用O(VE)次BFS找擴充路徑,資料結構必須是adjaency lists,BFS才會是O(E),總時間複雜度才會是O(VEE)。如果資料結構是adjacency matrix的時候,BFS其實是O(VV),做不出理論上的時間複雜度。

3. Push-Relabel演算法:每個點都有高度標號,push和relabel的順序隨意。時間複雜度O(VVE),受限於push的總次數,至於push的次數受限於relabel的次數。

4. Relabel-to-Front演算法:加上discharge的概念,然後以插隊的方式進行discharge。時間複雜度O(VVV),是用詭異的均攤分析算出來的。

(註記:其實只要是用了discharge的演算法,只要照著admissible network之拓樸順序discharge,基本上都會是O(VVV)。)

接著是AMO所提到的多項式時間演算法。這本書的觀點與CLRS不同,他先介紹最短距離標號的概念,然後任何一個演算法套上最短距離標號就會變快。

(註記:最短距離標號其實就是一種高度標號。)

1. Capacity Scaling 演算法:對管線容量使用scaling method,分成logC個階段,每階段最多可以擴充E大小的流量,擴充流量時套用最慢的Ford-Fulkerson演算法,就可以達到 O(EElogC)的時間複雜度了。如果套上最短距離標號,可以加速到O(VElogC)。

2. Shortest Augmenting Path演算法:其實就是Edmonds-Karp演算法,時間複雜度O(VEE)。如果套上最短距離標號,可以加速到O(VVE)。套上最短距離標號後的結果,其實可以看做是Push-Relabel演算法的一種實作方式,實作的方式是在admissible network上面以DFS順序進行push,找不到路徑而要回溯時,就做relabel。時間複雜度等於Push-Relabel演算法的時間複雜度 O(VVE)。

(註記:中文網路上把這演算法說得有夠厲害。真是奇怪。)

3. Distance Labels and Layered Networks章節:其實就是網路上流傳的Dinic演算法。分層圖其實就是最短距離標號。這個演算法跟Edmonds-Karp演算法的思路一樣,只不過它是一口氣找完所有的最短擴充路徑(即是blocking flow),然後一口氣擴充,如此反覆做O(V)次即得最大流。

實作時要注意到,每階段會有O(E)條最短擴充路徑,必須用O(V)時間找到一條擴充路徑,如此一來找blocking flow才是O(VE),整個演算法才是O(VVE)。DFS和BFS再怎麼厲害都是O(VV)或是O(E),要達到O(V)就不能使用DFS和BFS來找擴充路徑,必須妥善利用分層圖的特性,建立適當的資料結構來找擴充路徑。

4. Generic Preflow-Push演算法:就是CLRS的Push-Relabel演算法。

5. FIFO Preflow-Push 演算法:其實就是網路上流傳的Goldberg-Tarjan演算法。實作時用一個queue記錄overflowing vertex,照順序一直做discharge就可以了。時間複雜度O(VVV)。這個演算法在CLRS的節末證明題中有出現,只是沒給出名稱。

6. Highest-Label Preflow-Push演算法:最高點做discharge,利用特別的方式記錄最高點,以及特別的discharge方式,時間複雜度可以達到O(V*V*sqrtE),在前面幾個演算法當中,理論上速度最快的一個。

(註記:使用一般的priority queue來排序高度,不照著書上寫的方式做,時間複雜度是O(VVV)。)

7. Excess Scaling演算法:有興趣的自己看吧。

視網膜保健

2010/7/16

之前上網收集了一些關於視網膜保健的資料,在這裡跟大家分享一下心得。

視網膜是眼球內壁上的一層膜,上面有許多視覺神經。進入眼睛的光線,會照到視網膜上面,經由神經將光線訊號傳至大腦中,形成視覺。如果視網膜受損,受損的地方無法傳遞光線訊號,就失去了對應的視覺。視網膜一旦損壞,一般認為是不會恢復再生的,目前也沒有手術能夠填補或移植視網膜。這意味著視網膜一旦損壞,終生都將失去相對應的視覺。

經常以強光照射視網膜,容易造成視網膜損傷,例如直接看太陽、長時間看電腦螢幕等等。眼球變形(如高度近視者)或者眼壓太高(如用眼過度者),視網膜也很容易因為受到壓迫而損傷。像我們電腦工程師,長期暴露在螢幕光線下,又加上用眼過度,視網膜就會比一般人更容易受損。

要保護視網膜,可以攝取葉黃素。視網膜囤積著大量的葉黃素,會吸收藍光(日光、電腦螢幕光都有),降低光線對視網膜的傷害。另外葉黃素還可以減緩視網膜退化,加強視覺效果,是保護視網膜不可或缺的營養素。

葉黃素無法由人體合成,必須由食物當中攝取(例如甘藍菜、菠菜)。如果覺得自己攝取量不足,市面上有賣葉黃素膠囊,價格便宜,每天吞一顆,可以保障視網膜的健康。葉黃素膠囊可以說是電腦工程師不得不知道的好東西。

要保護視網膜,另一個選擇是吃枸杞,枸杞含大量玉米黃素,是葉黃素的異構體,功能與葉黃素差不多。這裡所說的枸杞,指的是枸杞的果實「枸杞子」,紅色一顆,如小指指甲般大,曬乾後外形如同葡萄乾,中藥店或菜市場就可以買到,價格便宜可以一次買一大包。枸杞受潮濕容易變質發霉,買來後最好放入冰箱冷藏,再不然則以密封袋罐裝好,保存得當可以放上一年半載。簡便的枸杞食用方式,是一日取30顆左右的枸杞,以熱水沖泡後當一杯茶喝,茶水會有微微紅黃色,喝起來有微微甜味,至於枸杞果粒可以不吃,有人說吃過量容易上火。由於枸杞在種植過程中都有添加農藥,所以在沖泡前最好用水浸泡沖洗一下,切勿久泡,以免營養流失。

網友迴響

我個人的經驗是:山桑子和黑醋栗市面上都有賣膠囊,據說可以保護眼睛。

而決明子可以泡成茶,據說也有保健視力的功效,而且比較便宜,只是不能喝太多就是了。