- Steve Jobs' 2005 Stanford Commencement Address
Steve Jobs於2005年給史丹福大學畢業生發表的演講辭,很有深度的演講。
Steve Jobs於2005年給史丹福大學畢業生發表的演講辭,很有深度的演講。
由IEEE舉辦的World Congress on Computational Intelligence 2008,結合了IJCNN, FUZZ-IEEE,CEC的WCCI今年在香港舉辦,能夠參加這個議會已經超出我的預期的範圍,我也是整個議會裡少數幾個Undergraduate Student,對此我真的很幸運有機會去見識一下目前的計算機智慧的走向。
這次會議從6/1~6/6為期六天(6/1為Tutorial),而我和教授兩人則早在5/31抵達香港,因為教授是信仰天主教,也因為如此我去參加了我生平第一次的教會,一間落座九龍尖沙咀的聖安德烈教堂做禮拜。
(由於門口寫著嚴禁拍攝,我只好從wiki上取得照片,實際的情景會比照片上顯得綠意盎然許多。)
6/1日的下午我們就先去香港會議展覽中心報到,也在當晚的餐敘盡可能認識一些朋友
離開Hong Kong Convention and Exhibition Centre之後,我馬上就坐著MTR到中環然後達著Star Ferry從中環坐到尖沙咀,為得就是要一睹維多利亞港灣的夜景。
到了尖沙咀之後,二話不說就衝到觀景台上拍下了尖沙咀大鐘樓的外貌。
於是就抱著流浪的心情四處亂逛,忽然看見了這一個status,我實在不知道她是誰,但我還是把他給拍下來了。
逛著逛著眼見時間也不早了,遊客也紛紛離去,我就答著MTR回到旺角住處。
會議的第一天,一早就飄起細雨,聽了一整個早上的Evolutionary Computation Presentation,疲憊的我坐在會議中心的休息區對外面的風景拍了這麼一張照片
今天起了個大早(6:00 a.m),因為從行程表得知Davie Fogel要來做今天Plenary Session的Speaker,於是就趕快盥洗換裝好,迅速前往會展中心佔個好位置。

The Burden of Proof,光是看這個標題其實很難猜出他想要表達的意思,當時我也是這麼想的,直到Fogel的Presentation接近尾聲時我才了解他想要傳達的意境,我們除了花很多心力在人工智慧上之外,也別忘了這個世界還有更多值得我們關心的議題,像是環境與生態的問題。
聽完早上的Plenary Session之後喝個咖啡當作早餐就直接前往今天最有趣的一個行程-Competition Session -Game,星期二的這一場主要是以othello以及Pac-Man這兩個經典遊戲為主,othello部份則是以3-truple的evaluate function為主題,但因為othello本身並不是那麼有趣,所以重點還是放在Pac-Man的人工智慧比賽上。不禁覺得很可惜沒有趕上這班競賽列車。
當天晚上望角的街上飄著雨,正猶豫著是否要趕去中環搭山頂火車上去太平山上,但想到既然難得來了,小小的雨算什麼,於是就搭著MTR去中環,一下地鐵從匯豐銀行走到中銀大廈,眼見雨勢越來越大,心想是不是該打堂退鼓,因為褲子已經濕一半了,但我還是沒有放棄繼續沿著花園道往上走,只是走著走著(不知走多久了)前方走來一對上海來的夫婦問我說山頂纜車在哪?真是有夠巧我也在找這個地方,於是我們三個遊客就沿路問人,原來三頂纜車的搭乘點我們早就錯過了(看樣子我近視不淺),正當我們一行人很高興的準備走到山頂纜車搭車處時,忽然間一道閃電閃過,接著而來的就是雷聲巨響,這一幕可真是怵目驚心,因為我看到的不只是閃電,還有火光。頓時心想,我會不會去山上被雷劈死,但我還是去搭乘了。
雖然外面是下著大雨,但卻無法動搖我們這一群遊客使命必達的堅定意志,一上火車大家除了拍照之外就是關窗,因為雨勢越來越大,且雨水都噴進來了(說實在那個窗有夠難關的,應該要重新設計過)
因為雨勢實在太大,所以我沒有前往關景台上拍攝照片,只有在這些應該是紀念品商店閒晃,忽然看見久違的李小龍蠟像,也在這邊買了一些紀念品。
眼看雨勢沒有要變小的跡象,我只好又搭著火車結束今天的行程。
今天中午的invited Session去聽了關於Objective的演講,說實在聽完之後還真的不知道在說什麼,光是Multi-Objective就已經很奇特了,那未來又會是甚麼?
當天晚上我就為自己規劃一個亂走路線,望角->中環->紅磡->九龍塘->望角,很不幸的計畫跟現實總是有落差,我到了中環的渡輪碼頭之後才發現往紅磡沒有開了,我只好在附近隨處逛逛
鑑於上次來時沒有好好拍他一下,我又拍了一張中環碼頭的照,忽然間右邊來了一堆日本團,於是我就很賤的走在他們前面(嘿嘿!你們不能到處跑),雖然自由行沒有專業的導遊來安排行程,常常會不知該往何處,但是那種自由自在的感覺,不是那種被指定說什麼時候該走,什麼時候該停的人可以了解的(嘿嘿~)。
我在尖沙咀的碼頭向外拍了這麼一張美麗的照片。接著就搭船到灣仔。步行到銅鑼灣,不知道是哪個廣場裡在集會遊行,為了不要湊熱鬧我就趕緊離開是非之地,走著走著走到天后站了,時間不早也該回住處了。
明天就要離開香港了,所以今天就是在香港的最後一夜,有甚麼遺願要趕快在這一晚完成(奇怪,我是來參加研討會的是來觀光),於是我今天決定要再闖一次太平山。
來到山頂的觀景台上當然就是要拍照啦!縱使外面在下著雨也要使命的拍
我買了一個甜筒站在廣場前面看著對面的電視牆邊吃我的冰,怎麼有種旅人的滄桑感...
在山頂廣場待了很久,待到店家都紛紛要打烊我再搭著山頂火車離開。
回到中環後我獨自一個人(一直都一個人)待在皇后像廣場逗留,正當我讚嘆這裡不止景美且可以上網的同時,我的銳利的視覺感應告訴我旁邊有黑色不明物體移動,那就是我們熟悉的小強蟑螂,偏偏在這個時候跑出來給我掃興。
有趣的是旁邊有一個大型電子廣告看板,畫面上播放著美麗的花朵綻放的動畫(其實只是幾何圖形的漸變),原來他是...CHANEL的廣告看板
時間真的不早了,而且走了那麼久也累了,再拍幾張照片也該結束在香港的最後一個晚上。
背後那黃色發光的建築是香港立法會大樓。
今天是會議的最後一天了,而我今天彷彿是來到日本的機器人發表會上一樣,因為我今天所聽的主題都跟人機互動有關,而這個領域的專家多半是日本人,所以常常會有整個Session裡都是日本的講者的情況發生,只是部份的講者英文不是很好,所以常有出糗的情況發生,為了不讓他們緊張,所以我就沒有拍照了。
中午趁空檔我在會議室的走道上拍了這麼一張照片
下午因為去聽Advanced Intelligent Interaction聽到快4:30pm. 所以當我一到大廳時他們已經閉幕完畢在吃東西了(囧...)
Download:conncpn.cpp
Connected Component是圖學理論裡一個很重要且很基本的定理,他主要的用意就是將一張圖畫分成數個區塊,一般分為四方向鄰邊偵測(上下左右),以及八方向鄰邊偵測(上下左右,右上,右下,左上,左下)。下圖是一個10x10的矩陣,乍看之下所有有效數值都是1,如果以八方向來劃分共可分成六堆,分堆原則很簡單,如果某一個元素他的八方向鄰邊皆為0,那他就自成一堆,反之則與鄰邊的數值成為一堆。

以八方向為例,只需要檢測左,左上,上,這三個方向數值即可:

檢測的程式碼片段如下:
void findCpn(int **map, int row, int col)
{
int i,j,currn=0,max;
for(i=1;i<row+1;i++){
for(j=1;j<col+1;j++){
if(max = (findMax(map[i][j-1], map[i-1][j], map[i-1][j-1]))){
if(map[i][j-1] && map[i][j-1] != max) checkAndReplace(map,i,j-1,map[i][j-1],max);
if(map[i-1][j-1] && map[i-1][j-1] != max) checkAndReplace(map,i-1,j-1,map[i-1][j-1],max);
if(map[i-1][j] && map[i-1][j] != max) checkAndReplace(map,i-1,j,map[i-1][j],max);
if(map[i][j]) map[i][j] = max;
}else if(map[i][j]) map[i][j]=++currn;
}
}
}
比較有效率的撰寫方式會使用到Tree的結構來做分堆,由於筆者比較懶所以就直接使用類似老鼠走迷宮的遞迴模式來做鄰邊的分類,如下程式碼:void checkAndReplace(int **map,int i,int j,int ori,int modi)
{
if(map[i][j]==ori){
map[i][j]=modi;
checkAndReplace(map,i-1,j,ori,modi);
checkAndReplace(map,i,j-1,ori,modi);
checkAndReplace(map,i,j+1,ori,modi);
checkAndReplace(map,i+1,j,ori,modi);
checkAndReplace(map,i-1,j-1,ori,modi);
checkAndReplace(map,i+1,j-1,ori,modi);
checkAndReplace(map,i-1,j+1,ori,modi);
checkAndReplace(map,i+1,j+1,ori,modi);
}
}
遞迴的部份觀念非常簡單,就以某個指定的map[i][j]為中心,向八個方向做檢測。最後的輸出結果如下:
所以你可以很清楚的看到,原始的地圖經過conncpn的程式運算之後就會劃分成如上的區塊。(這裡為了demo方便使用了recursive的形式,正常來講不應該這樣實做,因為會損失很大的效率)
word separating是筆者我在兩年前製作程式語言講義裡的其中一個範例,其中的演算規則不難,主要是善用字串處理,只是整個程式使用了linked-list的結構來撰寫,所以程式碼會長一點。
Download:sepWord.c
雖然這只是一個不到兩百行的小程式,但是其中還有一些重要的環節要介紹一下。
函式的原型如下:
delDuplicate():刪除重複以及長度不及的單字。
initNode():初始化linked-list的node。
sort():使用改良後的bubble sort來排序linked-list。
separate():整個程式的核心函式,裡面實做如何分離單字。
freeList():清除linked-list。
定義了一個叫做word的structue,裡面包含了兩個變數,keyword是用來儲存單字用的,而next則是結構體指標,指向word這個結構體型態。

在main()程式碼裡有一行程式(如下圖),這個宣告是特別用在head,tail這兩個word結構體的變數初始化用的,為了要讓後面的sort程式能夠正常排序且不會調動到head,tail這兩個結構體裡的keyword(也就是不要讓任何單字跟這兩個變數內的keyword做交換,否則我們想要留下的單字可能會被放在tail的keyword裡。)這只是筆者我比較習慣的處理方式,處理這類的方法還可以在排序的演算法其他函式上動手腳。
下圖是改良後的bubble sort,但是有別於一般的bubble sort,這個版本不會有任何for loop,因為linked-list本身就是以指標的型態做串連,所以沒有index可以使用,所以這個時候要善用head,tail這兩個node。(如果看不懂這個sort怎麼寫,姑且可以先跳過。)
接下來的就是整個程式的核心,separate(),程式碼不長,佔用了30行而已,
在separate()函式一開始就宣告了一個叫做token的陣列,裡面已經預先定義好要去除哪些字元,但是其中有兩個數字可能會讓讀者困惑,那就是10跟13,這兩個數字轉換成ascii之後分別代表著new line, carriage return,其中以10最需要被過濾掉,否則分離後會有一堆不需要的空白字元。
宣告完token之後,還要做一些資訊的讀取,例如程式碼157~159是在讀取檔案的byte數,162~163是在動態宣告一個char 陣列,並一次將所有要處理的文字讀取到buffer,這樣的作法會比筆者原先以一個一個字元讀取的方法來得更有效率。
終於我們要進入最核心的演算部份,我使用了兩個迴圈,外部迴圈(程式碼171)是一直做到整個buffer的最後一個字元,而內部迴圈(程式碼173)是不斷對每個字元去比對我們設定的token,如果比對成功(程式碼174),會接著去比對是否是空白字元或者是跳行字元(程式碼175),如果很幸運的又成功了,這時候我們可以確定程式已經讀到了一個單字,我們要為這個單字做收尾的工作(程式碼176),並且再新增一個node(程式碼178)給下一個單字儲存使用,只是這一個過程式linked-list的型態,所以程式碼177~180之間我們要將現有的單字跟新的node串接起來,否則list 就斷掉了。
而程式碼183~186是一個特殊處理的環節,程式碼183是在判斷是否已經處理到最後一個字元了,如果是個化同樣也要做收尾的動作,比且跳開這個內部while loop,如果還有字元要做處理,就遞增buffIndex的值,並且將tokenIndex給歸零(程式碼187),如此一來我們就可以繼續從程式碼173繼續做判斷,而不需要離開內部迴圈在繞一大圈進來。
如果目前要比對的字元跟我們設定好的token字元不符合(例如'a'),那程式碼就會執行到190行,並將'a'這個字元儲存起來,繼續再往下一個字元做判斷,不斷地重複上述的動作直到所有buffer裡的字元都已經分離完畢程式碼就會執行到192行做最後的收尾動作,也許這個時候你會有疑問為什麼程式碼176跟184都已經做了收尾的動作,程式碼192為什麼還要再寫一遍,原因是因為我們文字的結尾很有可能不是要過濾的符號,例如來源文件檔裡就只有一句話"this is a simple program",他最後一個字元是m完全不會進入到程式碼174裡的判斷,所以這個時候我們還要為這種情況額外收尾。
依照所要分析的文字結構不同,視情況去決定碰到什麼字元要做收尾並且產生新node的動作。
所以整個文字分離程式概念雖簡單,但是要仔細去設想各種狀況,否則一有缺失就會出錯。
測試用的短文(article.txt),特別加了許多符號來將句子給複雜化
執行使用的指令:
其中5代表著小於5個字母的單字都刪除掉。
輸出的結果(sepWords.txt)
輸出結果很理想的把我們所要的單字給分離出來,並且依造大小寫與字母給排序好,重複的單字以及不要的字元也已經消除。
本程式已由筆者重新校正過,我想程式應該是不會有錯誤了,除非讀者您使用了一些違反我程式所設定的token字元結構才有可能分析出不正常的單字。
雖然Borland C/C++ Compiler很少使用,但是還是特別寫出這一篇
Compiler可以從下面的網頁來下載:
http://www.codegear.com/downloads
安裝完畢之後還要做一下簡單的設定,將bcc32.cfg 和 ilink32.cfg這兩檔案放置到"c:\Borland\Bcc55\Bin\"裡,也許你會問這兩個檔案從哪裡來,我們可以自行建立這個檔案
-I"c:\Borland\Bcc55\include"
-L"c:\Borland\Bcc55\lib"
-L"c:\Borland\Bcc55\lib"
設定完上面的步驟之後,最後我們還要設定一下系統變數,控制台->系統->進階,在系統變數的地方找一個變數名稱為"path",在他的數值增加C:\Borland\BCC55\Bin; 如此一來你在console底下就可以使用bcc32來編譯。
以下是Borland C++ Compiler的基本編譯方式,假使我們有一個檔案叫做hello.c。
簡單透過上面兩行指令就可以編譯出hello.exe的執行檔了。
一個程式運作的快慢除了取決於系統的硬體環境、演算法設計之外,程式的最佳化也是一個重要的課題,大部分的人常常會忽略掉編譯器的最佳化部份,導致整個程式一經編譯後效能極慢。一個標準的編譯流程如下:
Source Code->Preprocessor->Compiler->Assembler->Object Code/Machine Code->Linker->Executables。
所以在整個編譯過程中,一個很大的環節就發生在Source Code to Assembly Language,一個好的編譯器會幫你將你的原始碼轉成最少行數或者最少Machine Cycle的組合語言,以提昇程式的執行或者記憶空間的效能,反之,則會降低程式的執行效率。GCC/MinGW本身提供了5種等級的最佳化,如下:
At this optimization level GCC does not perform any optimization and compiles the source code in the most straightforward way possible. Each command in the source code is converted directly to the corresponding instructions in the executable file, without rearrangement. This is the best option to use when debugging a program and is the default if no optimization level option is specified.
This level turns on the most common forms of optimization that do not require any speed-space tradeoffs. With this option the resulting executables should be smaller and faster than with -O0. The more expensive optimizations, such as instruction scheduling, are not used at this level. Compiling with the option -O1 can often take less time than compiling with -O0, due to the reduced amounts of data that need to be processed after simple optimizations.
This option turns on further optimizations, in addition to those used by -O1. These additional optimizations include instruction scheduling. Only optimizations that do not require any speed-space tradeoffs are used, so the executable should not increase in size. The compiler will take longer to compile programs and require more memory than with -O1. This option is generally the best choice for deployment of a program, because it provides maximum optimization without increasing the executable size. It is the default optimization level for releases of GNU packages.
This option turns on more expensive optimizations, such as function inlining, in addition to all the optimizations of the lower levels -O2 and -O1. The -O3 optimization level may increase the speed of the resulting executable, but can also increase its size. Under some circumstances where these optimizations are not favorable, this option might actually make a program slower.
This option selects optimizations which reduce the size of an executable. The aim of this option is to produce the smallest possible executable, for systems constrained by memory or disk space. In some cases a smaller executable will also run faster, due to better cache usage.
一般來講都用使用-O2 level,得到的效能換比較好,但還是得視你的程式碼的撰寫方向而定。
Reference:
在一些大數值運算的需求中,我們可能會需要用到長整數的型態(也就是超過4byte能表達的整數),以int64為例,可以表達的signed範圍從2e63-1(9,223,372,036,854,775,807) to -2e63(-9,223,372,036,854,775,808)在各開發環境下的使用方法略顯不同。如下:
各開發環境下的寫法各有些微的差距,其中以VC的寫法最為簡便,這部份Microsoft實做的很好,使用者不需要花太多心思去思考如何運作。而在linux底下則是使用long long 的型態來宣告,上面所有的程式輸出結果皆如上圖。
Installation pip install orange3 Run orange python -m Orange.canvas