2012年9月30日 星期日

[演算法] 希爾排序法 Shell Sort

  希爾排序法 (Shell Sort),由Donald Shell 於1959年發明最初版本,這也是此排序法的名稱由來,另外,由於操作方式,又被稱作增量遞減排序排序法 (diminishing increment sort),是插入排序法的一種改進版本。

  原始的插入排序法在插隊的時候需要一個一個比較和交換(或賦值)才能移動到正確位置,希爾排序認為如果一次移動長一點的距離,可以更快完成,例如某序列的最大值在其中一端點,插入排序法可能需要經過 n - 1次的比較和交換才能到另外一點,而若一次檢查1/4 序列長度,只要比較3次就可以了。所以希爾排序法利用不同的增量(步進)進行排序,在逐漸遞減增量,值到增量為1時回復到插入排序法,此時整個序列已經接近排序完成,所以可以以接近O(n)的速度排序完成。

  希爾排序法最重要的就是決定增量遞減的方式,不同的增量方式可以決定最糟狀況下的複雜度,從O(n^2)、O(nlog^2n) 到 O(n^1.5),詳細可以參考英文版的 Wiki,內有不同增量的時間複雜度。在序列不大時甚至有可能比快速排序法快一些。

複雜度:
  • 時間: 最佳 O(n) 最差 O(n^1.5) [增量遞減方式為 3*n+1]
  • 空間: O(n), O(1) 輔助

步驟:
  1. 依照不同的方式先決定最大的增量(步進)值 A。
  2. 以 A 為步進做插入排序法。
  3. 依不同方式遞減 A 的值。
  4. 重複步驟2. 3. 直到 A 小於 1 (A為1時是標準差入排序法)。
  5. 完成排序。

Java 簡單實現:

 
/** 增量遞減方式為 3*n+1 */
public ShellSort {
     public void sort(int[] array) {
          int step = 1;

          // 決定最大增量
          while(step <= array.length/3) {
              step = 3 * step + 1;
          }
          
          while(step > 0) {
               // 進行插入排序法
               for(int i = step; i < array.length; i += step) {
                    int target = array[i];
                    int j = i;
                    while((j > 0) && (array[j - 1] > target)) {
                         array[j - 1] = array[j];
                         j--;
                    }
                    array[j] = target;
               }

               //遞減增量 
               step = (step - 1) / 3;
          }
     }
}


範例:
  以 Wiki 的例子來舉例,有一序列為 [ 13 14 94 33 82 25 59 94 65 23 45 27 73 25 39 10 ],若決定增量為 5, 3, 1,則排序方式會如下:

原始數列,先用5個一列排好比較好理解。
13 14 94 33 82
25 59 94 65 23
45 27 73 25 39
10

以5為增量做插入排序法,可以看到每行都排列好了。
10 14 73 25 23
13 27 94 33 39
25 59 94 65 82
45

 換成3個排一列
10 14 73
25 23 13
27 94 33
39 25 59
94 65 82
45

以3為增量作為排序後
10 14 13
25 23 33
27 25 59
39 65 73
45 94 82
94

最後再以1為增量,也就是進行一般的插入排序法做最後一次排列 (應該是直的,但省空間就橫放囉)
10 14 13 25 23 33 27 25 59 39 65 73 45 94 82 94

排序完成
10 13 14 23 25 25 27 33 39 45 59 65 73 82 94 94
 
範例影片:

參考資料:
  1.  Wiki 希爾排序法
  2. 英文 Wiki Shellsort




[演算法] 氣泡排序法 Bubble Sort

  氣泡排序法是經典的三大 O(n^2) 排序演算法之一,同時他也是三者中最簡單的,大陸翻作「冒泡排序法」,因為他的概念是最大或最小的物件會慢慢地浮到飄到該去的位置。

  氣泡排序法的在不做任何最佳化的狀況下,因為無法判斷數列是否已經排好了,也會傻傻的作O(n^2)次的交換和比較來確保數列排序完成,故最佳狀況也需要 O(n^2)。因此,大多數的氣泡排序都會加上一個旗標(flag)用來判段當一輪的交換完成時是否有任何物件被交換,如果沒有其實就是完成了,可以將最佳狀況變為 O(n)。

  特別要注意的是,氣泡排序法所需要的最壞狀況交換操作次數遠超過選擇排序法跟插入排序法,雖然他們都是 O(n^2),但那是指比較操作加上交換操作的結果。氣泡排序法最糟狀況需要做  n*(n-1)/2 次交換,選擇排序法只要 n - 1 次,插入排序法要(((n-1)^2)/2)/3 次,都會比氣泡排序法德有效率。因此,氣泡排序法只適用在非常少量的數據,一般不太會用到 (但他就是簡單)。

複雜度:
  • 時間:最佳 O(n)  [使用 flag] 最差 O(n^2)
  • 空間:O(n), O(1) 輔助用

步驟:
  1. 將序列式為兩段,已排序和未排序,一開始整個序列都屬於未排序。
  2. 從未排序頭比較相鄰兩物件,若前者較大(小)就交換他們。
  3. 不斷向後比較、交換直到未排序序列的最後一個物件。此時未排序序列中最大或最小的物件會被成為已排序序列的頭。
  4. 重複 n*(n -1)/2 次,直到所有物件都進入已排序序列。

Java 簡單實現:

public class BubbleSort {
     public void sort(int[] array) {
          // 未排序序列的尾
          for(int i = array.length - 1; i > 0; --i) {
               // 交換整個未排列序列
               for(int j = 0; j < i ; ++j) {
                    if(array[j] > array[j+1]) {
                         int tmp = array[j];
                         array[j] = array[j + 1];
                         array[j + 1] = tmp;
                    }
               }
          }
     }
}
Flag 最佳化版本:
public class BubbleSort {
     public void sort(int[] array) {
          // 未排序序列的尾
          for(int i = array.length - 1; i > 0; --i) {
               boolean isChange = false;
               // 交換整個未排列序列
               for(int j = 0; j < i ; ++j) {
                    if(array[j] > array[j+1]) {
                         int tmp = array[j];
                         array[j] = array[j + 1];
                         array[j + 1] = tmp;
                         isChange = true;
                    }
               }
               // 都沒有交換到東西就排序完成
               if( ! isChange) {
                   break;
               }
          }
     }
}

範例影片:
參考資料:

2012年9月29日 星期六

[演算法] 選擇排序法 Selection Sort

  選擇排序法是經典的三大 O(n^2) 排序演算法之一。所謂的「選擇」是要在未排列序列中最小(大)的物件,將其放置到已排列序列的末端。(一個序列可分為兩段,已排序和未排序),重複此動作最後就能完成排序。

  雖然會說選擇排序法有一個優點是最壞狀況的「交換」次數是 n - 1 次,在交換位置的時間比比較大得多的情況下,故若數列接近反序,選擇排序法會比冒泡排序法跟選擇排序法來得有效率,但實際上在實作時是錯的。由於尋找最小值時就會不斷需要記住最小值的索引,會一直進行賦值操作,故在實際使用上(測試過整數完全反序數列排序),插入排序法效率遠比選擇排序法來得高。

複雜度:
  • 時間:最佳 O(n^2) 最差 O(n^2)
  • 空間:O(n), O(1) 輔助

步驟:
  1. 將序列視為兩段,已排序和未排序,一開始整個數列都算未排序。
  2. 從未排序序列的頭開始向後尋找最大(小)的物件。
  3. 將其與未排序數列的頭交換 (也就是將這個物件放到已排序數列的尾端)。
  4. 重複此步驟2~3,直到整個數列都變成已排序數列。
Java 簡單實現:

public class SelectionSort {
     public void sort(int[] array) {
          // 未排序數列的頭
          for(int i = 0; i < array.length; ++i) { 
               int min = i;
               // 向後找最大(小)
               for(int j = i + 1; j < array.length; ++j) {  
                    if(array[j] < array[min]) {
                         min = j;
                    }
               }
               // 和頭交換
               int tmp = array[i];
               array[i] = array[min];
               array[min] = tmp;
          }
     }
}

範例影片:

參考資料:

[演算法] 插入排序法 Insertion Sort

  插入排序法是經典的三大 O(n^2) 排序演算法之一。與其叫他「插入演算法」,不如叫他「插隊演算法」更能表達其運作原理,可以想像做一個未排序的物件(人?)隊伍,從頭開始,每個物件都有一次機會向前插隊到它該去的位置。

  插入排序法最佳狀況下(已排序好數列),只需要掃描一遍就可以結束,故 O(n) ,但最差狀況需要做 n*(n-1)/2 - (n-1) 次的賦值,。但是,插入排序法在處理少量數據的能力平均來說是三大O(n^2)排序演算法中最佳的,部分快速排序法到最後也是用插入排序法排少量的數據,是一定要當常識記得的演算法之一 (XD)。

複雜度:
  • 時間:最佳 O(n) 最差 O(n^2)
  • 空間:O(n), O(1) 輔助用
步驟:
  1. 第一個物件當作已經排序好。
  2. 向後逐一取出物件,並從該物件開始向前掃描其他物件。
  3. 若前一個物件大於(小於)此物件,兩者對調位置。 (在實作上使用賦值而不對調)
  4. 重複步驟3. 一直交換到前一個物件不再大於(小於)此物件。
  5. 重複步驟2. 從原位置繼續看下一個物件。 
Java 簡單實現:
public class InsertionSort {
    public void sort(int[] array) {
        // 一個一個看
        for(int i=0; i < array.length; ++i) {
            int target = array[i];
            int j = i;
            // 向前檢查 (插隊)
            while((j > 0) && (array[j - 1] > target)) { 
                array[j] = array[j - 1];
                j--;
            }
            array[j] = target;
        }
    }
}


範例影片:

 參考資料:
  1. Wiki 選擇排序法

2012年9月23日 星期日

安裝 Gliffy Atlassian Confluence plugin 失敗

  Gliffy (http://www.gliffy.com/) 是一個相當令人驚豔的線上製圖應用程式,功能和 Microsoft Visio 差不多,但能畫出遠比 Visio 好看的圖形。Gliffy 同時也有針對 Atlassian Confluensse / JIRA (http://www.atlassian.com) 推出 Plugin。

  為了要可以離線使用這項強大的工具,最近正在安裝 Confluence + Gliffy plugin 看看是不是符合需求,但首次安裝就不太順利,出現以下錯誤訊息。


Caused by: com.mysql.jdbc.PacketTooBigException: Packet for query is too large (20833751 > 1048576). You can change this value on the server by setting the max_allowed_packet' variable.
at com.mysql.jdbc.MysqlIO.send(MysqlIO.java:3250)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1940)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2113)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2693)

  提示很清楚是 MySQL 所允許的 Query 封包大小不夠產生的,但到底要把這個值調多大呢? 在試過很多次之後發現答案其實還滿誇張的,那就是: 比插件的 .jar 檔大。推測 Confluence 是將整個 plugin 放到資料庫內,但為什麼要這麼做就不得而知了。

在簡單寫一下解法:
修改 my.ini 將max_allowed_packet 值設為20MB以上,如:
max_allowed_packet = 32MB

2012年9月9日 星期日

SCIgen Paper 產生器

  SCIgen 是一個在 2005 年由 MIT 著名的人工智慧實驗室三位學生合力寫出來的程式,透過一組寫好的 CFG, Context-Free Grammar,可以自動產生一篇符合常見格式的 Paper,包含內容、圖、表格、參考文獻等等。我也自己試著產生了一篇《Deconstructing B-Trees Using Chimaera》 乍看之下整個還滿專業的,哈哈。

  這三位學生也用 SCIgen 產生的 Paper ,向許多當時知名的 Conference 投稿。最著名的例子就是 WMSCI 2005  接受了其中一篇,這也是他們的初試,馬上讓 WMSCI 隔年失去了 IEEE 的贊助。不知道他們寫這隻程式從一開始就是為了要愚弄 (提醒大眾?) 某些不管 Paper 品質的 Conference,或是一開始只是玩笑,而經過記者的美化。但總之,可以知道的是,MIT 的人工智慧實驗室真是不斷地秉持 Hacker 精神在做研究和開發程式阿,希望我們 LAB 也能培養成這樣的風氣 (正在努力)。

   SCIgen,這隻程式隨然沒辦法拿來幫忙研究生提早畢業,但從中間也能看到一些可以發展的價值,不知道 MIT 有沒有進行後續的研究。SCIgen 的 CFG 提供了一片論文相當好的框架,若有隻程式能夠透過自動的摘要、統計分析等動作,將研究者針對主題所撰寫的文章、實驗數據轉換為一篇 Paper,對於研究者來說其實是相當便利的動作。當然啦,寫作本來就是研究者應盡的義務,讓後人可以踩著你的研究成果繼續努力,就算真有這樣的程式出現,他也只能被當作一個框架、一個幫助摘要、格式化的工具,而不可能取代人腦的工作。


一個正在默默努力的研究生

Jack Yu

2012年9月8日 星期六

閱讀心得:為什麼有錢人都用長皮夾?

《為什麼有錢人都用長皮夾?年收入200倍法則!改變25萬人的錢包增值術!》

博客來連結: http://www.books.com.tw/exep/prod/booksfile.php?item=0010553142

  這次回家剛好愛買書的老媽買了這本書就拿來翻翻看,會不會只要換個錢包,錢就自己已直跑來呢? 想太多,當然不會 XD。但作者仍在書中提到一些值得去思考與實踐的事情,留下來給自己做一些紀錄。本書共分三個部分,錢包錢的分類以及態度

錢包

錢包是錢與人的橋樑,作者認為從一個人的錢包就可以知道一個人對於錢的態度,如果錢包總是亂七八糟,塞滿折價券、會員卡,裡面的紙鈔破舊、沒有排列,雖然不太表著這個人覺得錢不重要,而是沒有認真地去注意、關心錢的使用。所以並不是靠著神奇力量,換長皮夾就能夠帶來 200 倍的年收入,而是靠著 (這很有趣) 去關心錢過的舒不舒服,就能更加關注錢的使用,作者也提出了幾個能讓錢過得舒服的方式:
  1. 使用長皮夾。一來錢不會被折到,二來因為皮夾大就不容易放到口袋,而被坐在屁股下面。
  2. 每天整理錢包,不要讓錢包鼓鼓的。 
  3. 紙鈔要朝同一面排放整齊。頭朝下表示你希望錢不容易出去,頭朝上則代表你希望錢能有流動。 (後者才會促進經濟發展喔!)
  4. 結帳使用新鈔。使用新鈔除了讓收錢的人開心之外,他也給人一種你對錢是有在注意的人。
  5. 遞錢時態度要小心。好,這不是怕錢掉下去會痛。主要也是希望收錢的人能夠開心。
  6. 心裡對錢說「一路小心」、「歡迎回來」。這除了非科學的部分,主要ˋ目的是在提醒自己正在動用金錢,而近一步的多做考慮。
  7. 使用不太便宜的錢包。 作者多年觀察到許多成功的有錢企業家的年收入,大約就等於錢包的價錢 x 200,例如使用 5 萬元的錢包,年收入就會是一千萬! 其實雖然不太相信這樣的數據,但透過一個不太便宜的錢包,也能讓人注意到錢的重要性。

錢的分類  

錢的使用可以被分為三類:消費投資浪費
  1. 消費代表著可以立即可以獲得等值的回報,如買食物。
  2. 投資代表著在未來可以得到回報,如買書。
  3. 浪費代表著不會獲得回報,如打電動等。
無論如何,應該盡可能的讓所有錢的使用成為投資的類型,例如光是吃東西就能夠試著去思考,要如何吃才能讓自己吃得健康,而讓只是為了果腹的消費轉為獲得健康的投資。

態度

這部分是最趨近於立志類型的部分,但還是有很多值得思考的事情。
  1. 不要成為「守財奴」,正面思考,不要只想著錢還剩下多少,而是思考還有多少錢,如此一來才能進一步地去思考如何充分運用這些錢。透過這樣的思考在面對突如其來的金錢問題時,也要不容易慌張。
  2. 成為控制錢的人,不要為了特價就衝動購買。 (這個好難)
  3. 不要為了省錢購買廉價品 => 購賣高價品。 這個規則在近年來自己體會非常明顯,最近的廠商 cost down 到一種境界後產品的品質和耐用度都大幅度的下降,為了不要製造不必要的麻煩、獲得好的服務以及同時讓自己有意識地注意要愛惜這個產品,在同產品中,購買中價位以上的產品能獲得不錯的效果。 (甚至一次攻頂)
  4. 少殺價。看起來也是個奇怪的觀念,但作者要說的不是讓商人多賺點,而是希望在買方、賣方、社會三者間找到一個正向的平衡。

2012年9月4日 星期二

HTC One X Bootloader 解鎖 (unlock)

  Bootloader 是開機第一個執行的程式,用來將作業系統 (在 One X 上也就是 Android) 跑起來,同時也提供一些相當底層的操作,例如燒錄 ROM 等功能。以 One X 來說你想要刷入 Recovery 模式、刷第三方 ROM、取得 Root等動作都需要解鎖 Bootloader,但HTC 和多數的大廠為了確保使用者不要不小心按一按就把手機搞壞了,所以一般來說 Bootloader 是被鎖住的,但也有進階使用者認為 Android 手機就是應該提供使用者最大的自由度才對,所以 HTC 也有順應民意提供官方的 Bootloader 解所可以使用。

  但特別要注意一件事, HTC 的保固政策,解鎖會失去原廠的保固,且目前沒有任何重新回復到原本狀態的方法(看不出來有解鎖過)。所以建議大家三思過後再決定是否要解鎖。

<<三思三思>>

OK,若你決定要繼續解鎖,就繼續往下看吧。

※ 在還沒開始走官方解鎖步驟前,你可以到最後面的參考資料第三項,有一鍵全自動解鎖程式可以使用,他將官方的步驟進行包裝,會自動下載驅動、需要的程式、而且會自動解鎖,所以幾乎什麼事都不用做了,但是要注意的是,使用它會拿不到解鎖過程中會產生的檔案,若之後有問題會難救回來,還是走官方流程會好一點喔。 (反正這個步驟應該只會做一次)

事前準備:

1. 安裝 HTC Sync (點我去下載),這樣電腦才有驅動程式能順利抓到手機。
2. 安裝 Java Runtime (點我去下載),Android 的 SDK Manager 需要 JRE 才能執行。
3. 安裝 Android SDK(點我去下載),取得和手機溝通的程式。安裝好後開啟 Android SDK Manager,除了預設設全部勾選的 "Android 4.1 (API 16)" 之外,在將第一項 "Tools" 打勾。 並點選 "Install" 進行安裝,這需要一點時間。
   
4. 備份好所有個人資料,解鎖會重設手機到出廠狀態,所有資料都會不見。

官方解鎖步驟:

1.  來到 HTC 開發者網頁 http://www.htcdev.com/,並在右上角進行登入,如果你沒有帳號就註冊一個吧,注意 email 務必要填對,不然會收不到解鎖用的檔案

2. 點選 "Unlock Bootloader" 分類,然後點選 "Get Start"。

3.右側下拉選單選擇使用的型號,One X 在這邊是選擇 "All Other Supported Models",按下 "Begin Unlock Bootloader"。

4.解鎖會喪失保固你確定嗎? "Yes" 繼續。 (還是看一下才能保護自己的權益)

5. 把兩個勾勾都打勾確定你清楚風險了,接著就點選按鈕開始看官方教學流程。
  

6. 這頁的步驟要目的是讓手機進入到 Bootloader 的操作模式,One X 屬於不能拆電池的機種,所以有兩個方法可進到 Bootloader ,擇一使用 (其實兩個是一樣的東西)。
     a. 重新開機,並在重開機過程中按住 "音量鍵↓"。
     b. 關機後,先按住 "音量鍵↓",然後按電源鍵開機。 
成功進入後會看到手機上出現白白一堆英文的介面,使用音量鍵 ↑、↓  移動光標選住 "Fastboot",並按下電源鍵進入 "Fastboot"。最後再將 USB 線插上跟電腦連線就完成了這頁的步驟,點選 " Processd To Step 5" 繼續看教學。

7. 在事前準備我們已經抓好了 Android SDK,現在需要從裡面找到我們要的程式。先在任意地方建立一個資料夾 (如 "C:\Android"),接著從 Android SDK 的安裝目錄下 (像我的在 "C:\Program Files (x86)\Android\android-sdk\"),複製 ".\platform-tools\adb.exe"、".\platform-tools\adbWinApi.dll"  到 "C:\Android"。另一個我們需要的檔案在最新版的 SDK 中已經不提供了,所以從舊版的來取得 android-sdk_r13-windows.zip (點我下載) ,解壓縮其中 ".\android-sdk-windows\tools\fastboot.exe" 到 "C:\Android",所以 "C:\Android" 會有三個檔案在內。


 8. 當檔案都準備好之後要從命令提示字元來執行這些指令,按快速鍵 Win+R 將執行視窗叫出來,並輸入 "cmd" 開啟命令提示字元。並輸入 "cd c:\Android" 切換到剛剛準備好的目錄。這是這頁的最後一個步驟,點選 "Proceed To Step 8" 繼續看教學。

9. 在命令提示字元輸入 "fastboot oem get_identifier_token" 取得 One X 上 Bootloader 的身分序列 (每一台都不一樣)。


10. 稍微等一下後就會出現,從 Bootloader 顯示的資訊。點選右鍵,選擇"標記"後用滑鼠把"<<<< Identifier Token Start >>>" 到 "<<<<< Identifier Token End >>>>>" 選起來,並按下 "Enter" 將這些內容複製到剪貼簿。 (命令提示字元不要關,等等還要用)

11. 將剛剛複製的內容貼到對話框中,並按下 "Submit",接著就可以去信箱收 HTC 寄來的解鎖檔案。

12. 檔案是寄到我的 Gmail 信箱,將這個檔案下載到剛剛的資料夾 ("C:\Android")。

13. 在命令提示字元輸入 "fastboot flash unlocktoken Unlock_code.bin" 將收到的解鎖檔案燒錄到手機上。

14. 這時手機的畫面會變成下圖,若已經三思確定要解鎖了就選擇 "Yes" 這樣手機就會重開機進行解鎖動作 (注意手機會重設到出廠狀態);若反悔了,就選擇 "No" 手機會重開機並..什麼事都沒發生。

 15. 好啦,恭喜已經解鎖 Bootloader 了,其他更進階的應用會在之後的文章繼續說。 :)


參考資料:
  1. 【教學】HTC One X Root 機教學(含解鎖Bootloader)
  2.  <<教學文>>ONE X 官方解鎖 ROOT 全教學
  3.  小白不是障礙!!HTC One X一鍵官方解鎖和上鎖工具寶典!!