Saturday, June 1, 2013

bitcoin.org 翻譯說明:Transifex 簡介

歡迎各位朋友加入 bitcoin.org 中文化的活動,請先選擇以下其中之一
簡體
繁體

假設選擇的是繁體中文,會看到以下畫面:



選擇中間的聯結後,會進入另外一個畫面:



此時你可以點"Translate Now"按鈕進入編輯頁面:


以上是編輯頁面,建議先遵循建議1,選擇 "Unreviewed" 只列出還未 review 的句子。然後到左側的清單點選你想要 review 的句子,選擇之後中間上方會出現英文原文,下方則是中文翻譯。如果有覺得不通順或是錯誤的地方,可以按右邊 panel 的 Suggest 提出你的建議。如果覺得翻譯已經完成的話,可以點中間上方的翹起大拇指的按鈕。

以上是一點簡單的介紹,感謝大家的付出與努力。

Tuesday, May 7, 2013

Bitcoin 採礦機開發 1 - 簡介 getwork

本文目標
從 2009 年以來,Bitcoin 採礦技術已經從 CPU 到 GPU 再到 FPGA,目前 (2013) 最先進的是 ASIC。本文從基礎開始分析 Bitcoin 採礦技術,歡迎有志開發自己的採礦機的讀者指教。

啟動測試網路
由於目前 Bitcoin 的難度相當高,要自己挖掘一個 block 的機率極低。為了減低計算難度,我們將使用測試網路(testnet),而非一般的 Bitcoin 網路。testnet 是你測試 Bitcoin 的好夥伴。
首先準備好官方的 bitcoind。假設讀者使用 Linux,可以下列參數啟動:

$ bitcoind -testnet -daemon

text 參數告訴 bitcoind 要連接到 testnet。
daemon 參數告訴 bitcoind 要在背景作為服務執行。

取得work
確認 bitcoind 在背景執行之後,就可以透過 JSON-RPC 從它取得一個 work:

$ bitcoind -testnet getwork

執行結果如下:(數字會有不同)

{
    "midstate" : "f412665e9eaf2aef805289d0e8779ea5ce42beee7d76aec5cb036870e250db01",
    "data" : "0000000246a19088cb6e4d779bc8b6f223e748ec6da48aafb4d53ffe00cdcf99000000001b081cdb63293b7b117cb2d53a98a1ecb3e1e2dfbb322045159743673dae9de7517c91841c06305100000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000",
    "hash1" : "00000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000010000",
    "target" : "0000000000000000000000000000000000000000000000000051300600000000"
}

取得 work 後,miner 的工作就是去找出一個稱之為 nonce 的32-bit 正整數,放入 data 之中,以滿足特定條件。此條件在下面會說明。而這整個過程就稱之為採礦。

Proof-of-work
那這個條件是甚麼呢?簡單來說就是對 data 取兩次 hash 值,使得 hash(hash(data)) < target
如果條件不成立的話,就把 nonce 加一,然後更新 data 重新計算,直到條件成立為止。Bitcoin 使用的 hash 函數是 SHA-256。

work 資料結構
從上面我們得知 bitcoind 會傳回四個數值,都是以 hexadecimal 編碼,little-endian 順序存放。其中 data 和 target 是必要欄位,midstate 和 hash1 是選擇性的。data 的長度是128 bytes,不過目前只用到 80 bytes。data 實際上是一個 structure,由 6 個欄位組成。我們現在不必深究所有的欄位,只要知道這 80 bytes 的最後面 4 個 bytes 是存放 nonce。而 nonce 欄位會是零,因為這是要 miner 去計算的。

0000000246a19088cb6e4d779bc8b6f223e748ec6da48aafb4d53ffe00cdcf99
000000001b081cdb63293b7b117cb2d53a98a1ecb3e1e2dfbb32204515974367
3dae9de7517c91841c0630510000000000000080000000000000000000000000
0000000000000000000000000000000000000000000000000000000080020000

由於 SHA-256 採用 big-endian,而 getwork 傳回的資料是以 4-byte 為一個 word,所以我們需要以四個 bytes 為單位先轉回 big-endian。以下的 python 程式可以容易的作到:

b = data.decode('hex')
h = ''.join([b[x:x+4][::-1] for x in range(0, len(b), 4)]).encode('hex')


結果為:

020000008890a146774d6ecbf2b6c89bec48e723af8aa46dfe3fd5b499cfcd00
00000000db1c081b7b3b2963d5b27c11eca1983adfe2e1b3452032bb67439715
e79dae3d84917c515130061c0000000080000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000280

注意到紅色的部份 4 bytes 全為零,這就是 miner 需要計算的 nonce,nonce 從零開始不斷加一後重新計算 hash,直到滿足條件為止。而最後面的橘色 4 bytes 的數值為 0x00000280,換成 10 進位就是 640,640 bits 即 80 bytes,這就是先前提到的實際資料長度 80 bytes,除非資料結構有更動,不然 miner 可以直接忽略這些資料。

下期預告
最簡單的 miner : pyminer

參考資料
Bitcoin wiki - getwork
Bitcoin wiki - block hashing algorithm

Saturday, March 9, 2013

Bitcoin 資源整理

這裡有關於比特幣的重要資源,給入門者參考 (2013/04/04更新)

2009年的論文,一切的起點:
Bitcoin:A Peer-to-Peer Electronic Cash System
簡體中文翻譯 - 比特币:一种点对点的电子现金系统

官方網站
Bitcoin.org - 可以下載錢包和獲得基本知識
BitcoinWiki - 可以找到很詳細的資料,包含軟體實作細節

新聞來源
Bitcoin Forum - 最大的比特幣論壇
比特人 - 中文論壇
Bitcoin Magazine
Reddit

實用服務
Bitpay - 接受bitcoin付款(賣家用)
Bitspend - 用bitcoin購物
Bitcoinstore - 用bitcoin購物,特別是3C產品

金融
BitcoinWATCH - Bitcoin 匯率、市值(目前約14億美金)和其他統計數據
MtGox - 最大的Bitcoin貨幣交易所,以美金為主
BTCChina - Bitcoin對人民幣交易所
BTCjam - 個人對個人的 Bitcoin 借貸與投資
Bitfinex - 次級市場,可以借貸、做多、做空

程式
BFGMiner - 挖礦程式
Abe - python程式,用來分析bitcoin交易紀錄

Saturday, March 2, 2013

Bitfinex 與 Bets of Bitcoin

之前跟朋友聊到bitcoin,他問我有沒有bitcoin的銀行,我當時不知道,昨天正巧就看到Bitfinex,一個bitcoin的銀行,可以辦理借貸款,融資操作。以借貸為例,Bitfinex公開出來的平均放款利率高的嚇人:190%(美元)和12%(比特幣)。



這種高利率同時也反應了高風險,如果跟你借錢的人還不出錢怎麼辦?這下可沒有國家幫你討錢,所以這時Bitfinex說:沒關係,我來幫你承擔風險。
You don't have the exchange risk when you lend with bitfinex. The exchange risk is taken on by the trader, and, in case his position loses money, he will cover the loss with funds in his trading wallet. If by any chances losses are greater than what the trader can afford, the loss is on Bitfinex, we will cover for the missing amount. 
當然如果太多人還不起錢,Bitfinex也撐不下去,打賭網站Bets of Bitcoin就開了一個賭盤,賭Bitfinex能不能活過2013上半年。有興趣的朋友可以到這邊看看關於Bitfinex的討論。

Friday, March 1, 2013

Bitcoin 交易經驗

比特幣2013年來快速升值,兌美元從14升到33,匯率資料可見 bitcoinity。

目前要獲得比特幣,最主要的方法還是透過交易所,如果是要買賣比特幣,還是在交易量大的單位買賣比較迅速。關於交易量的資訊,可見 bitconchars。以下是比特幣兌換各種貨幣的主要的交易所。

美金 MtGox  交易手續費: 0.6% (大量有優惠)
歐元 Bitcoin-24  交易手續費: 0%
人民幣 Bitcoin China  交易手續費: 0.3%

以 Bitcoin-24 為例,先註冊賬號後,再從台灣的銀行匯入歐元,約兩個工作天。匯款手續費看各家銀行而定,一般是數百元到一千多元。

Thursday, February 21, 2013

Bitcoin / 比特幣 / 位元幣交易所


Bitcoin從2013年1月中以來最近不斷升值,判斷該是入手的時間。但是從台灣要買bitcoin不容易,比較可靠的管道多為交易所。那麼有哪些交易所呢? Bitcoin wiki 上面有給出一個清單,就我逛逛之後列出幾個交易量比較大的交易所。

2013/02/21
MtGox 交易量 35389 BTC
BTC-E 交易量 1738 BTC
Bitcoin-Central 交易量 741 BTC


這些交易所其實是在交換bitcoin和各種貨幣(美金,歐元,或其它電子貨幣)。在交易前需要先存款(Deposit),好比說要用美金交易的話,要先從台灣進行國際匯款美元到交易所的銀行帳戶,然後才能買bitcoin。目前用來交易的貨幣幾乎都是美金和歐元。

就自己的經驗,國際匯款不是個最好的選擇,第一是要被收取300~900圓的手續費,第二是要兩三天對方才能確認收到款項。當然有人已經著手解決這個問題,如果是在美國的話,可以使用BitInstant服務,在7-11直接存款,非常方便。應該讓台灣的7-11也能進行這種服務。

Saturday, November 17, 2012

初嘗Node.js開發管理package.json

發佈自己的程式時,如何解決相依套件的問題是很麻煩的。套件的相依性,簡單來說就是我的程式需要哪些套件,要用什麼版本。最近看到node的npm管理方法,令人驚艷。

開發npm的套件時,需要package.json,這個檔案包含package的各種資訊,如作者和依賴的套件。顧名思義,package.json就是個json,結構也很簡潔。

這裡有官方的文件,我節錄和套件有關的重要的package.json片段,

{ "dependencies" :
  { "foo" : "1.0.0 - 2.9999.9999"
  , "bar" : ">=1.0.2 <2.1.2"
  , "baz" : ">1.0.2 <=2.3.4"
  , "boo" : "2.0.1"
  , "qux" : "<1.0.0 || >=2.3.1 <2.4.5 || >=2.5.2 <3.0.0"
  , "asd" : "http://asdf.com/asdf.tar.gz"
  , "til" : "~1.2"
  , "elf" : "~1.2.3"
  , "two" : "2.x"
  , "thr" : "3.3.x"
  }
}

從上表我們可以發現,指定相依套件的方式很有彈性。可以指定特定版本,也可以指定某個區間。

設定完成後,只需要用以下指令,npm就會幫忙把套件裝到package所在的目錄下,對於開發者來說實在是很方便:)

npm i

Sunday, August 5, 2012

增進程式設計功力的好書推薦

身為一個不太勤快的工程師,從學校畢業後看程式設計相關的書的時間減少很多,在這些書中有三本(其實有認真看的也沒幾本:-D )對我的影響最大。

Pragmatic Programmer / Andrew Hunt and David Thomas



Code Complete / Steve McConnell
Programming Pearls / Jon Bentley

這三本書中我第一名推薦的是Pragmatic Programmer,有兩個原因,其一是夠薄,三百多頁而已,在不算長的篇幅中把單兵作戰守則解說得很清楚。第二個原因是他有一個很棒的小抄。




推薦的第二名是Code Complete,此本為聖經不需多說,應該是所有推薦書單的平均第一名,我沒有放在第一名的原因是因為太厚了我還沒看完。

第三名推薦的是Programming Pearls,很老的書,很薄。最推薦的是"Back of the Envelope"這一章。此書在台灣甚難入手,Amazon上也沒有電子書,如果有機會得到的話一定要買下。

本日學習:
翻到Code Complete的第三十章:程式設計工具,其中一個自我檢查是:你的編輯器是否有自動編譯和錯誤檢查?
我赫然發覺平時用Vim寫Perl程式居然沒有這個功能,我必須用 perl -c 自行檢查。沒關係亡羊補牢猶未遲,趕緊找一個plugin perl_synwrite來幫忙,搭配這個外掛只需在存檔的時候用:W就可以先編譯perl程式,如果錯誤的話就會顯示錯誤訊息。

最後的總結就用 Jeff Atwood 的推薦書單吧


Monday, July 16, 2012

Vim基本心法 - 移動系

你聽說Vim的學習曲線很高而卻步嗎?

你正在學習Vim,但卻因指令難以記憶而苦惱嗎?

Vim和Emacs是電腦高手的唯二選擇。其實Vim的指令設計是很合理的。只要了解一個大觀念:
Vim是上古神器,那時會用電腦的人都會寫程式。
另外一個小觀念是:懶惰是程序員的美德,若可以的話,手盡量不移動位置,用右手去拿滑鼠這件事更是萬萬不可。

第一問: 為什麼Vim預設會進入命令模式而非編輯模式呢? 為什麼我不能夠一開始就打字呢?
試想你打開程式時,大多的時間在閱讀,編程的時間相對的少,所以一開始進入移動模式會比較有效率。

復述一遍:懶惰是程序員的美德,因此在命令模式下通常只需要一個按鍵就可以自在的移動。

用Vim打開你的一個檔案吧,如果沒有可供練習的檔案,可以打開Vim的求助檔案
:help<enter>

移動系四招共21個指令
左下上右 h j k l
移動游標是基本,而上下左右則是中的基本,所以就用右手最容易按的四個鍵hjkl。那為什麼j是下一行呢? 想想我們最常按的鍵就是"下一行",所以跟最容易按的右手食指j配對正好。決定j代表下一行之後,右邊的k就當作"上一行",再來就用左邊的h表示向左和右邊的l表示向右。

螢幕頂中底 H M L
除了上下移動,你有時會想移動游標到螢幕的頂部,中間,或是底部。這時就可以用按鍵H (High)移到頂部,M(middle)到中間,以及L(low)到底部。至於為什麼是大寫呢? 因為h和l已經被當成左右了呀。

到指定行 G

xG
x是行號。這指令很簡單,G就是go。

那要如何回到第一行呢? 可以用1G,但是可以用gg更為簡短。

在句子中遊走 web 0^$
w 是移動到下移個word的起始
e 是移動到此word的結束
b 是移動到前移個word的起始
0 是移動到此行的開頭。為什麼是0而不是1呢?我認為這是因為C語言中陣列的開頭是第0個字元而非第1個字元。
^是移動到此行的第一個非空白字元。選用^的原因是它可以被稱為hat,而帽子就是戴在頭上的囉。
$ 是移動到此行的最末端。選用$的原因我還不清楚:p

搜尋和跳躍 /? nN *#
/ 後面加上要搜尋的字串,就會跳到該字串。使用 / 作為指令的原因我猜是因為 slash 和 search 聽起來很像
? 和 / 相似,唯一個差別在於 ? 是用於向前搜尋。使用?的理由就是因為它和/是同一個按鍵。
n和N要配合/?使用。n就是搜尋下一個符合的字串。N是搜尋前一個符合的字串。如果一開始用 / 搜尋的話,n會往文章的後面搜尋,N則會往前面搜尋。如果是用 ? 搜尋的話,方向會相反過來。

學會這21個指令後,有沒有發覺Vim比其他的編輯器更強大呢?

Friday, April 6, 2012

連署網站設計

最近幫人做了一個簡易的連署網站,作為Google App Engine的練習。
第一次看到有這麼多人使用自己的作品,實在是很感動,同時也覺得應該要改進設計。

網頁簡介
此網站非常簡單,只有兩個使用者網頁
  • 訪客用: 顯示連署書,加上連署者的姓名、生日、地址和電話。填好資料後按下送出即可。
  • 管理者用: 可以下載連署資料,格式為csv。不需任何輸入。

可改進之處
  • 採用 https 連線。
  • 驗證輸入資料的合法性。拒絕空白的使用者姓名。
  • 平衡流量。GAE的免費版本大約只能支援每天1500人。可以採用多重網站,每個的服務都相同,並從入口處隨機分配要連到哪個網站。

Bug
英文地址裡有逗號,會讓csv的欄位判斷錯誤。解決方法為加上雙引號。

Friday, March 9, 2012

自制vimgolf競賽排行榜

緣起
因為朋友ypcatw的介紹開始玩vimgolf,現在一共有104個題目,解決了32個題目之後,晉升到第92名囉,也終於可以在Leaderboard看到自己的名字。不過這個排行榜有兩個特別的地方。
  1. 只有前一百名的排名
  2. 沒有真正的"竿數",也就是所有題目的排名總和。
排名規則
補充說明一下,vimgolf官方規定排名是用Global rank來計算,一個玩家的Global rank是他在所有比賽(題目)得到的rank總和。好比說fakecolor在"Get rid of html tags"這個比賽拿到第47名,rank就是47。如果沒有參與某場比賽,那rank就等同於參與該場比賽的最後一名玩家的rank。在計算排名時,Global rank越小的人名次越高。由此規則可知,每參與一場比賽都可以使你的Global rank下降。

自製排行榜
回到一開始的問題,官方沒有公布完整的排名,以及每個人的global rank。沒關係,身為programmer最棒的一件事就是可以自己動手做。為了知道自己和其他人的Global rank差距,我和Y決定自行計算所有人的Global rank,並製作成完整的排行榜。要怎麼自行計算Global rank呢? 要訣在於每場比賽的Leaderboard是公開的,就位於題目的右方。我們只須抓取每個比賽的Leaderboard,然後就能計算每個玩家的排名啦。
結果如下表格:

RankingIDGlobal Rank
1 clvv42 2147
2 gumnos 2491
3 wondible 2867
4 federicogalassi 2885
5 h_east 2929
6 yslinnctu 3014
7 Korvin79 3800
8 _Balkoth_ 3885
9 johnsyweb 4100
10 hujunfeng 4163
...
目前(2012 Feb)一共有約2000名玩家


製作方法
我們採用Google App Engine(GAE)做為平台,每天自動抓取所有比賽(Challenge)的Leaderboard,擷取出每場比賽的參賽者後,放入資料庫,並計算所有玩家的Global rank。使用GAE的好處是Google去別人家撈東西很勤快XD

尚未解決的問題
計算好玩家的成績後,我們利用web.py的template功能來產生網頁。目前為止都很順利,但是我們卻被頻寬的問題打敗。由於玩家約有2000名,所以每次產生網頁都需要從資料庫讀取2000資料。然而GAE是以量計價,免費帳號的資料庫存取量每天只夠我們讀取約60,000筆資料,也就是至多能產生30次網頁。為了減少流量,我們決定每天指更新一次資料庫,並利用GAE本身的記憶體快取(memcache)來減少需要讀取資料庫的情況。不過很可惜的是memcache常常會失敗。我們的第一個常識是把結果轉成RSS格式,並利用RSSPECT網站幫我們發佈,但是很可惜的是Google reader沒有辦法讀取發佈的文章,可能是因為內容太長了。
因此我們改採用另外一招:把動態產生的網頁轉成靜態網頁,放在其他伺服器上,做為權宜之計: http://www.csie.ntu.edu.tw/~b89107/vimgolf/ 。不過還是希望能夠在GAE內部就解決這個問題。

Saturday, February 4, 2012

vim競技大賽 -- vimgolf

世界上有各種奇奇怪怪的比賽,在電腦界中常出現的一種叫做某某高爾夫,如同真正的高爾夫以杆數決勝負,電腦界的高爾夫往往是利用程式的長短。最近出現的vimgolf,是以vim編輯器作為競技場,參賽者相互比較誰能用最少的按鍵數目來修改一篇文章。

來看一個實際題目:以下是一份code,但是作者把大括號,中括號,以及小括號弄錯了,想請你把錯誤修正過來。


錯誤的程式
var some_function = function {arg1, arg2} [
   var some_array = (1, 2, 3, 4, 'foo');
   for {var i in some_array} (
         console.log(some_array, [{1 + (8 / 2)}, 'hello (world)');
   )
];

正確的程式
var some_function = function (arg1, arg2) {
   var some_array = [1, 2, 3, 4, 'foo'];
   for (var i in some_array) {
         console.log(some_array, [(1 + (8 / 2)), 'hello (world)');
   }
}; 

可以試試看用你最愛用的編輯器,要按幾次按鍵才能完成編輯呢?用vim的話,最少只需用34個鍵就能搞定!如何,是否有興趣學學vim呢?

Tuesday, January 24, 2012

奮鬥吧!系統工程師(4)誰都能辦到?專案管理 閱讀心得

這兩天看了小說"奮鬥吧!系統工程師"第四集,這次的主題是專案管理。
拜過去的成功經驗所賜,主角工兵接下PM(Project Manager)的工作,為了制定計畫,以及協調來自不同公司的專案成員而奔走。由於是沒有經驗的菜鳥,主角不知如何對應不受指揮的專案成員,就在焦頭爛額時獲得了正妹高人指點:如何駕馭對立的專案成員。

小說中提到專案成員可分成三類
Server (服務者):此類型成員將PM是為客戶,可以接受內容粗略的委託,並自行做出適當判斷以進行作業。
Tool (工具):此類型成員以作業員自居,會順從PM的指示,但並不會背負專案成敗的責任。PM需要追蹤此類成員的進度。
Client (客戶):此類型成員將PM是為服務提供者,必須替自己排除影響工作的障礙,並且同時提供專案進度和支援。

看到這一段令我十分驚奇,因為這管理的道理和<僕人:修道院的領導啟示錄>所說的不謀而合。僕人中說:領導始於服務,領導就是服務。PM做為專案的領導人,也就要"服務"專案成員,為成員整頓環境(比如說解決失戀問題XD),以及發生問題時的支援。

將成員分類對應到我現在的團隊中也很有趣。我的前輩是Server型的成員,不需多做指示變會自動完成工作,甚至還會反過來指示PM接下來的計畫:) 而我則偏向Tool,只專心在被交付的工作。

最後引用書中角色藤崎的話:專案管理是甚麼呢?就是可以讓大家愉快進行作業的工作。

Saturday, January 14, 2012

[推薦] 奮鬥吧! 系統工程師

[此文適合:資訊系學生,想進入資訊業的朋友,已經在資訊業工作的朋友]

有次同事聚餐的時候,我的大長官語重心長得跟我說了:「雖然我們是軟體業,但是最難的地方不在於技術,而是在於人啊。」我才剛踏入業界一年,很難體會他的感想,只覺得工作就是看能力,努力一定會有結果。最近看到一部輕小說 "奮鬥吧! 系統工程師",把軟體業的黑暗真實面描寫得相當清楚。
為了不讓氣氛太沉悶,先放張美美的封面 (圖片來自台灣角川)。





「千萬不要當上SE。」
研究室的教授這麽對我說。
「我站在這裏授課,不是爲了要讓你們過勞死。」
    -- 摘自第一集附錄

還沒被嚇跑的朋友請往下看:)

故事主角櫻坂工兵非科班出身,只是因為畢業找不到工作,被拐騙到駿河系統公司,開始了SE(system engineer)的生活。故事的架構是主角一邊學習,一邊把妹,然後順利完成工作...的樣子。雖然是照著輕小說的歡樂公式,但是本書對於工作的描寫卻是寫實到靠北邊走。好比說主角的主修是行銷,但是卻搖身一變成為工程師,這個設定其實反映了軟體業的入門門檻低,像dot com泡沫時期,阿貓阿狗都可以進入科技公司工作。
不過也不是說非科班出身就不能打出一片天,但隨著故事進行,主角利用美色協調能力讓團隊順利運作,並隨著經驗增加,不斷完成各種管理工作。我個人的感覺是,除了軟體公司裡面幾乎沒有正妹工程師以外,其他的部分都很寫實,好比說顧客任性的改動程序,老闆總是接下超過負荷的工作等等。如果主角換成女生,其他的角色保持男性的話,應該就毫無違和感了!事實上我大學時有個美人學姊,畢業後就到某知名外商當PM,也過得相當不錯。在一片缺乏社交能力的阿宅中,有交際手腕就容易顯得突出。想到這裡,我才發覺當年因為自己不擅和別人相處,還以為資訊工作可以只和電腦打交道,實在是太天真了。想要在這個業界混,還是不能不加強人際管理的能力啊。


發覺寫到這裡還沒有介紹多少內容,但是我懶得打書摘:p 所以放上前五集的標題,看得懂的人想會會心一笑吧

1. 兩週內即可上手?SE入門
2. 從基礎學習?運用建構
3. 絕不失敗?提案活動
4. 誰都能辦到?專案管理
5. Step by Step? Customer Engineer (中文譯名未定)

Sunday, January 1, 2012

親愛的軟體工程師,你平常到底在做什麼?

又到了歲末年初的時候,三五好友聚會的時候總是會談到:最近的工作如何呀?誰誰誰受不了要跳槽了之類的。剛開始到業界工作時,我常跟人說我的工作不有趣,好像只是為了賺錢而已,所以萌生的辭職的想法。但是聽聽朋友的意見之後,發覺我其實還蠻幸福的,因為:
  • 休假多,而且想放假就能放假,不會有專案緊急被叫回去加班的情況。這點很重要,我聽說不少公司的假很多,但是看得到吃不到。
  • 每天平均開會時間約一小時,相當有效率。
  • 薪水有外商的水準。
  • 主管的技術底子強,不會有外行領導內行的情況。

以上是從外部來評量工作的好壞,但是我實際上在做的事究竟是怎樣呢?有那麼無趣嗎?我試著回想看看自己的工作情況,並看看自己有什麼成長。

Who:和誰一起工作
工作一年後,發覺關鍵果然是人,如果人對了,可以聊天打屁之外,還能夠相互刺激學習。如同一般軟體團隊,我們有
產品經理:收集客戶反應,然後決定產品的功能。
專案經理:scrum master。不曉得scrum是什麼?請收看Teddy Chen的部落格。此人並不需深入瞭解技術,他的工作追蹤專案進度。
架構師:一人,負責決定技術決策。
開發者:我和另外五人。主要工作是依據需求設計程式。
支援工程師:平時不會看到他們,不過當產品上市後,站在第一線面對顧客的意見抱怨的就是他們 :) 所以我們需要寫FAQ給他們用。最近從支援工程師那邊來的抱怨是:FAQ寫太多啦,特別是安裝系統的步驟太繁瑣。

What:做了哪些事
我嘗試用簡短幾句話來說我做了什麼。我們團隊的產品是開發針對VMware虛擬化環境的分析工具。為什麼要做這個產品呢?因為VMware是個包山包海的大怪物,裡面什麼東西都有,即便我接觸快一年了,對裡面各種虛擬硬體仍是不完全瞭解,更別說新近的VMware管理員了。
1. 收集事實:用Perl來擷取VMware環境的所有資料,像記憶體用量,CPU用量,錯誤紀錄,使用者的操作歷史等等等。這些東西都會送到我們後端的專用資料庫中。
2. 說故事:有了事實,我們要找出合適的呈現方法。好比說用折線圖呈現各種資源的用量,讓使用者能快速的瞭解他的虛擬環境。由於我們的前端是Web,所以我們常用到Javascript,將來更會多採用HTML5。
3. 回答問題:雖說預防勝於治療,但是一般管理者平時不一定會去用我們的分析工具,只有在火燒屁股時才會趕緊問:到底什麼東西壞掉了?所以我們事先收集各種錯誤的發生原因,並找出偵測方法,好在問題發生時能快速鎖定。

學到什麼
Script language:我們工作用到Perl和Python。特別是Perl,因為這是VMware官方的SDK之一。在這之前我怎麼都搞不懂Perl裡面的%&$@奇怪符號。說真的script langulage開發起來比C快很多,程式也簡短很多。唯一的遺憾是你沒辦法吹噓說自己寫了上萬行程式碼:)
VMware:這是個龐大的怪物!我好像進了大觀園一樣,改天要整理自己的心得分享一下。
Splunk:強大的搜尋引擎,有著類似SQL但又獨特的指令。

回顧完這一年的工作經驗後,發覺好像也沒哪麼無趣嘛。新的一年如果還在這家公司的話,我想要學習HTML5和jQuery,一方面可以強化我們的使用者介面,一方面可以玩一些資料視覺化。

各位看官,你們的工作是否也能說成一個有趣的故事呢?

Monday, December 26, 2011

以 Python 做測試驅動開發 (Test-driven development) 練習 -- 測試一個多邊形是否近似於四角形 (二)

回顧一下前篇文章中提到測試驅動開發(TDD)的三步驟:

1. 寫好程式的介面,只要能夠通過編譯即可
2. 增加測試
3. 執行測試,如果有失敗的測試的話就修改程式,然後回到步驟2

回到我們目前的IsQuadrilateral函數,目前的實作非常簡單,只是檢查傳入的多邊形是否有四個頂點,若是的話就傳回真。現在要來處理其他狀況:長的像四角形的五角形(或更多角)。解法如前篇所說,我們先取多邊形的第一個點做為p0,然後找出最遠的點p1,接著找離p1最遠的點p0。我們把這件工作交給FindFarthestPoint,此函數接受一個點p和一個集合S,傳回在集合S中離p最遠的點。這時我們依照TDD的精神,加上個測試先。testFindFarthestPoint利用我們之前定義好的square,測試離左下角最遠的點是否為右上角。

# 要放到 class TestSequenceFunctions 裡面
def testFindFarthestPoint(self):
    self.assertTrue( numpy.array_equal(FindFarthestPoint(numpy.array([0,0]), self.square)[0], numpy.array([1,1])) )
    self.assertTrue( numpy.array_equal(FindFarthestPoint(numpy.array([0,2]), self.square)[0], numpy.array([1,0])) )

def FindFarthestPoint(p, polygon):
    x = polygon[0,]-p[0]
    y = polygon[1,]-p[1]
    idx = numpy.argmax(x**2 + y**2)
    return polygon[:,idx], idx

好了,雖然繞了些路,終於萬事俱備,可以來寫判斷三角形的函數了。由於我們已經把多邊形切成兩半,各自是兩群連續的點的集合。我們可以想像從p1p2的連線,如果途中有一個凸出的點,p1到p2就會是一個三角形(或者更多角)。我使用CountMiddleVertex來計算一群連續的點之中有幾個凸出的點。這會是一個遞迴函數,先找到最突出的點p3,然後用p3切割線段,然後分別呼叫CountMiddleVertex繼續計算。

# 要放到 class TestSequenceFunctions 裡面
def testCountVertex(self):
    self.assertEqual(CountMiddleVertex(self.square2),   1)
    self.assertEqual(CountMiddleVertex(self.triangle),  1)
    self.assertEqual(CountMiddleVertex(self.square),    2)
    self.assertEqual(CountMiddleVertex(self.pentagon1), 3)
    self.assertEqual(CountMiddleVertex(self.pentagon2), 3)
    self.assertEqual(CountMiddleVertex(self.hexagon),   4)

def CountMiddleVertex(pointset, threshold=0.03):
    pointnum = pointset.shape[1]
    if pointnum <=2:
        return 0
    p1 = pointset[:, 0]
    p2 = pointset[:, -1]
    dist2 = (p1[0]-p2[0])*(p1[0]-p2[0]) + (p1[1]-p2[1])*(p1[1]-p2[1]) 
    triarea = []
    for p in range(pointset.shape[1]):
        triarea.append(PolygonArea(numpy.column_stack((p1,p2,pointset[:,p]))))
    # 如果最突出的點超過門檻值
    if max(triarea)*1.0/dist2 > threshold:
        midpoint = triarea.index(max(triarea))
        vertexnum = 1 + CountMiddleVertex(pointset[:, 0:midpoint+1])
            + CountMiddleVertex(pointset[:, midpoint:], maxnum-vertexnum);
    return 0

CountMiddleVertex測試完成後,我們就利用它來判定三角形吧。更新後的IsQuadrilateral以及完整程式如下。為了讓CountMiddleVertex執行快一些,我加了一個參數maxnum,讓它可以早點結束。

import numpy
import unittest

def PolygonArea(polygon):
    x,y = numpy.column_stack((polygon, numpy.array([polygon[0,0],polygon[1,0]])))
    return abs((numpy.dot(x[0:-1],y[1:]) - numpy.dot(x[1:],y[0:-1])))/2.0 
    
def FindFarthestPoint(p, polygon):
    x = polygon[0,]-p[0]
    y = polygon[1,]-p[1]
    idx = numpy.argmax(x**2 + y**2)
    return polygon[:,idx], idx

def CountMiddleVertex(pointset, maxnum=10, threshold=0.03):
    pointnum = pointset.shape[1]
    if pointnum <=2:
        return 0
    p1 = pointset[:, 0]
    p2 = pointset[:, -1]
    dist2 = (p1[0]-p2[0])*(p1[0]-p2[0]) + (p1[1]-p2[1])*(p1[1]-p2[1]) 
    triarea = []
    for p in range(pointset.shape[1]):
        triarea.append(PolygonArea(numpy.column_stack((p1,p2,pointset[:,p]))))
    if max(triarea)*1.0/dist2 > threshold:
        midpoint = triarea.index(max(triarea))
        vertexnum = 1 + CountMiddleVertex(pointset[:, 0:midpoint+1]);
        if vertexnum >= maxnum:
            return maxnum
        return vertexnum + CountMiddleVertex(pointset[:, midpoint:], maxnum-vertexnum);
    return 0

def IsQuadrilateral(polygon):
    x,y = polygon
    
    if len(x) < 4 or len(x)!=len(y):
        return False
   
    p1, p1idx = FindFarthestPoint(polygon[:,0], polygon)
    p2, p2idx = FindFarthestPoint(p1, polygon)
    
    if p1idx > p2idx:
        p1idx,p2idx = p2idx,p1idx
        p1,p2 = p2,p1
    
    if CountMiddleVertex(polygon[:, p1idx:p2idx+1], 2)==1 and \
        CountMiddleVertex(numpy.column_stack((polygon[:, p2idx:], polygon[:, :p1idx+1])), 2)==1:
        return True
    else:
        return False
    
class TestSequenceFunctions(unittest.TestCase):
    def setUp(self):
        self.triangle   = numpy.array( [ (0,10,0),(0,0,10)  ] )
        self.square     = numpy.array( [ (0,1,1,0),(0,0,1,1)  ] )
        self.square2    = numpy.array( [ (0,0.5,1,0),(0,-0.01,0,1)  ] )
        self.pentagon1  = numpy.array( [ (0,1,1,0,-1),(0,0,1,1,0.5)  ] )
        self.pentagon2  = numpy.array( [ (0,1,1,0,-0.01),(0,0,1,1,0.5)  ] )
        self.pentagon3  = numpy.array( [ (0,1,1,0,-0.21),(0,0,1,1,0.5)  ] )
        self.hexagon    = numpy.array( [ (1,1,0,-1,-1,0),(1,-1,-2,-1,1,2)  ] )
        
    def testFindFarthestPoint(self):
        self.assertTrue( numpy.array_equal(FindFarthestPoint(numpy.array([0,0]), self.square)[0], numpy.array([1,1])) )
        self.assertTrue( numpy.array_equal(FindFarthestPoint(numpy.array([0,2]), self.square)[0], numpy.array([1,0])) )
        
    def testCountVertex(self):
        self.assertEqual(CountMiddleVertex(self.square2),   1)
        self.assertEqual(CountMiddleVertex(self.triangle),  1)
        self.assertEqual(CountMiddleVertex(self.square),    2)
        self.assertEqual(CountMiddleVertex(self.pentagon1), 3)
        self.assertEqual(CountMiddleVertex(self.pentagon2), 3)
        self.assertEqual(CountMiddleVertex(self.hexagon),   4)
    
    def testIsQuadrilateral(self):
        self.assertEqual(IsQuadrilateral(self.triangle),   False)
        self.assertEqual(IsQuadrilateral(self.square),     True)
        self.assertEqual(IsQuadrilateral(self.pentagon1),  False)
        self.assertEqual(IsQuadrilateral(self.pentagon2),  True)
        self.assertEqual(IsQuadrilateral(self.pentagon3),  False)
        self.assertEqual(IsQuadrilateral(self.hexagon),    False)

if __name__ == '__main__':
    unittest.main()

Sunday, December 18, 2011

以 Python 做測試驅動開發 (Test-driven development) 練習 -- 測試一個多邊形是否近似於四角形 (一)

這次用Python來練習驅動程式開發。
TDD簡單的說只有三步
1. 寫好程式的介面,只要能夠通過編譯即可
2. 增加測試
3. 執行測試,如果有失敗的測試的話就修改程式,然後回到步驟2

我要寫的程式是輸入一個多邊形的頂點,檢查是否為近似四邊形。
這個問題可以很簡單,我們只需要檢查多邊形的頂點數目,
如果是四個點的話就是四邊形。
當然我希望這個程式的能力不只如此,不然就沒有練習的效果啦。
其實這個問題是從實際的問題演變過來的,實際上是應用在擴增實境中,
我們從攝影機抓取影像,然後找出影像中的正方形,做為一個參考點。
一個平片上的正方形從不同角度去看,會變成一個任意的四邊形,
我們從影像分割出許多多邊形,然後判斷像不像四邊形。然而由於影像處理必定會有誤差,
所以一個四邊形可能有一些凸出的部分,而會變成五角形或六角形。我們的程式須要能處理這種情況。
這也是採用TDD的理由,我們可以一步一步改進我們的程式。

開始囉
首先定義函數IsQuadrilateral,輸入為一個多邊形,輸出真假值。由於現在還沒有測試,
不假思索就可以得到一個最簡單的實作方法,一律傳回假就好。
def IsQuadrilateral(polygon):
  return False

接著是增加測試,利用Python的unittest模組,我們將測試程式包在TestSequenceFunctions物件中。

import unittest
import numpy
class TestSequenceFunctions(unittest.TestCase):
  def setUp(self):
    self.triangle   = numpy.array( [ (0,1,0),(0,0,1)  ] )
    self.square     = numpy.array( [ (0,1,1,0),(0,0,1,1)  ] )
  def testIsQuadrilateral(self):
    self.assertEqual(IsQuadrilateral(self.triangle),    False)
    self.assertEqual(IsQuadrilateral(self.square),      True)
if __name__ == '__main__':
    unittest.main()

setUp函數是用來建立測試用的資料,這邊我們建立了兩個二維陣列來表示三角形和正方形的頂點。
numpy是常用的數學函式庫,程式中的 [ (0,1,0),(0,0,1)  ] 表示一個二維陣列,第一列是三個頂點的x座標 0,1,0,
第二列三個頂點的y座標 (0,0,1)。square物件存放的是正方形,有四個頂點。

完成以後就是執行測試。結果在第二個測試失敗了,所以我們要回頭修改程式。第二版的程式如下:檢查傳入的多邊形是否含有四個頂點,
有的話就為真,否則為假。

def IsQuadrilateral(polygon):
    x,y = polygon
    if len(x) != 4 or len(x)!=len(y):
        return False
    return True

現在回頭再跑測試,就可以過關啦。當然我們不能停在這裡,要繼續加測試才行。

五角形1:
self.pentagon1 = numpy.array( [ (0,1,1,0,-1),(0,0,1,1,0.5)  ] )
五角形2:
self.pentagon2 = numpy.array( [ (0,1,1,0,-0.01),(0,0,1,1,0.5)  ] )
五角形3:
self.pentagon3 = numpy.array( [ (0,1,1,0,-0.21),(0,0,1,1,0.5)  ] )
此處我們用了三個五角形,長相如下:

其中第二個五角形其實是四角形,只是左邊有一點小突出而已,我們的新測試如下:

def testIsQuadrilateral(self):
    self.assertEqual(IsQuadrilateral(self.triangle),   False)
    self.assertEqual(IsQuadrilateral(self.square),     True)
    self.assertEqual(IsQuadrilateral(self.pentagon1),  False)
    self.assertEqual(IsQuadrilateral(self.pentagon2),  True)
    self.assertEqual(IsQuadrilateral(self.pentagon3),  False)

修改演算法,四邊形是兩個三角形所組成,所以我們先嘗試找到四邊形的兩個頂點。這裡我用了一個經驗法則,先任意選取polygon上的一個點p0,然後選取離p0最遠的點p1,接著選取離p1最遠的點p2,如此一來p1和p2就是近似四邊形上的兩個點。p1p2的連線把polygon分成兩半,我們接下來只需要判斷這兩半是否為三角形即可。請見下面的圖。

為了要找到離p0最遠的點,我需要再寫一個函數FindFarthestPoint,同時需要新的測試來驗證這新的函數。
今天就先寫到這裡吧。來回顧一下到目前為止我們的程式看起來如何:

import unittest
import numpy

# 主函數
def IsQuadrilateral(polygon):
    x,y = polygon
    if len(x) != 4 or len(x)!=len(y):
        return False
    return True

# 測試程式
class TestSequenceFunctions(unittest.TestCase):
    # 此函數在 testIsQuadrilateral 被執行前會被呼叫,以建立測試所需的物件
    # 想一想為何需要一個獨立的 setUp 函數呢?
    def setUp(self):
        self.triangle   = numpy.array( [ (0,10,0),(0,0,10)  ] )
        self.square     = numpy.array( [ (0,1,1,0),(0,0,1,1)  ] )
        self.pentagon1  = numpy.array( [ (0,1,1,0,-1),(0,0,1,1,0.5)  ] )
        self.pentagon2  = numpy.array( [ (0,1,1,0,-0.01),(0,0,1,1,0.5)  ] ) # 這個測試會失敗
        self.pentagon3  = numpy.array( [ (0,1,1,0,-0.21),(0,0,1,1,0.5)  ] )

    def testIsQuadrilateral(self):
      self.assertEqual(IsQuadrilateral(self.triangle),   False)
      self.assertEqual(IsQuadrilateral(self.square),     True)
      self.assertEqual(IsQuadrilateral(self.pentagon1),  False)
      self.assertEqual(IsQuadrilateral(self.pentagon2),  True)
      self.assertEqual(IsQuadrilateral(self.pentagon3),  False)

if __name__ == '__main__':
    unittest.main()  # 會呼叫TestSequenceFunctions裡所有的測試函數

Saturday, December 3, 2011

我的軟體生涯發展規劃

到業界工作進一年來,有幸遇到功力深厚又照護晚輩的前輩。由於師徒制在軟體業不盛行,一般的人不會花多餘時間指導新人,一來是沒有回報,二來是怕被超越。對我來說學到的東西中,是前輩指出我的兩個最大的缺點。

1. 應付了事。我向來是那種很會考試的人,對於大多數問題都能找到一個答案並且應付過去。但是久而久之會養成一種交差了事的態度,只求解決眼前的問題,沒有去思索整個問題的來龍去脈,並且透徹了解問題。

2. 獨善其身。所謂教學相長,我平時學到的東西很少與人分享,一方面是個性不主動親近人,另一方面是表達能力不強。


前輩給我的建議是先改進第二點,練習成為某個領域的大師,並且能幫別人速成。所謂速成就是指:比自己更快速學成。他指出唯有能幫別人速成,才表示你已經學的透徹。由於現在沒有學生,我先嘗試把自己學習的心得以教學的方式放在部落格上。

最後分享一篇文章做為結尾: 給學生與軟體業新進的十招

Sunday, November 27, 2011

家庭幫手機器人的需求

我是個很懶的人,在家裡時用過的東西常隨手就丟,完全違反"物歸原位"的整潔原則哈。所以想說如果能有個機器人幫我拿東西收東西多好。

情境:我所需要的東西放在一個儲物櫃中,我呼叫機器人幫我拿來,用完後幫我收好。
為了達成這個目標,這樣的機器人需要以下能力:

接收命令的能力。最好是用語音輸入,可以簡單的用單字即可,如"剪刀"

辨識物體的能力。視覺能力,能夠在儲物櫃中找到特定物體。
移動能力。可以來回於使用者和儲物櫃之間。
取得物體的能力。打開儲物櫃,取出物體,所以要有"手"。

以上幾項技術早已存在,語音輸入有Apple最近發表的Siri語音助理,特定物體辨識在電腦視覺領域已經有一定的成熟,移動能力早已成熟,最後是拿取物體的能力,這部分也不成問題。照這樣看來,我們在市面上應該能找到這樣的產品才對。所以離題一下,來搜尋看看有哪些公司已經發表家事機器人。

Willow Garage
主要產品: PR2, OpenCV, ROS (Robot Operating System)

iRobot
主要產品: Roomba (地上亂跑的吸塵器那隻), Scooba (這隻可以拖地!), Looj (清除房屋外沿的排水溝), Verro (清潔游泳池)

比照這兩家公司,可以看出Willow Garage走通用型機器人,而iRobot是專用型的。

結尾來對照一下現實中的機器人和想像中的機器人做家事吧~




摺衣服



摺襪子

Friday, November 11, 2011

為什麼我們還沒有萬能家務機器人?

2012將至,
為什麼我們還沒有機器人可以幫我整理衣服?
為什麼我們還沒有機器人可以幫我收拾房間?

或者更積極一些的問題
我們要克服哪些技術上的困難才能做出家務機器人?
哪個技術是我最感興趣的?

牢騷才發完,便發覺最近Honda公開了Asimo的最新情況 [1] (連結內含影片)
我們得到甚麼新東西?
1. 單腳跳,很有意思,不過還不知道有甚麼用
2. 時速 9km小跑,還不錯,當個跑腿小弟挺合適
3. 推餐車,單手轉開水壺蓋,並將水倒至紙杯中。Cool,有服務生的潛力。

唯一的遺憾是Sony向來保密,所以沒有內部的技術資料。我認為走開源路線的Willow Garage會有更好的發展。他們的PR2現在可以...幫你買三明治、做早餐,摺襪子。