[Golang] 學習Google Code Search 使用的Trigram Indexing

前言

一直以來,我有在追蹤許多優秀工程師的github(RD的臉書).其中尤其是dgryski. 因為他有許多有趣使用Golang來開發的演算法小專案,所以我也會一起學習一些演算法與特別的資料結構.

本週的課程是Trigram Indexing.一邊學習,一邊寫成小專案.

什麼是Trigram Indexing

直接打Trigram會找到一堆關於卦的資料.不過Trigram主要是由兩個字組合而成 Tri-gram 也就是三個字元(N-gram中的tri-gram).

其實Trigram很簡單,主要就是把一串文字透過三個三個來分組:

  • 將所有字元改成小寫
  • 把每個空白處理.這裡有些不太相同,不少人將空白作為分隔符號.而Google Code Search把空白當作其中字元放進去.
  • 把字元做成trigram

如何做把一個單字做 Trigram 拆解

舉例: Search

  • 變小寫 “search”
  • 開始拆解,三個三個為一組
    • sea
    • ear
    • arc
    • rch

如何在程式裡實作拆解trigram

其實在程式裡面實作拆解很簡單,只是重要的是要如何比對.因為如果你真的把"sea""ear" …. 存成字串,比對又是相當的消耗時間.所以不論是Google還是一般人在做Trigram的時候都會這樣拆解.

講一個個字元換成ascii的uint32並且透過位移方式存放.舉例而言s := "abc"就變成 uint32(s[0]), uint32(s[1]), uint32(s[2])也就是 97, 98, 99.並且透過位移存放.97<<16 + 98<<8 + 99 = 6382179. 這樣比較的速度就會快的更多,也很適合儲存.

	var trigrams []uint32 //存放所有拆解好的trigram
	s := "abc" //要拆解的字
	
	for i := 0; i <= len(s)-3; i++ {
		//透過將每個字元轉換成uint的方式,並且透過位移方式存放
		t := uint(uint32(s[i])<<16 | uint32(s[i+1])<<8 | uint32(s[i+2]))
		trigrams = append(trigrams, t)
	}

	//最後結果 6382179

如何在Code Search中使用

這邊開始很建議打開Russ Cox關於Google Code Search的介紹文章,雖然主軸是Regex 不過是透過Trigram Indexing的方式.

如同前面提到的,由於這個是”Code Search” 所以空白本身相當的重要.也就必須要將空白當作一個字元來作為Trigram Indexing的來源.

請注意: 本文重點在於討論Trigram Indexing,所以原先在Google Code Search針對Regex處理的部分就不討論.

透過簡單例子來了解

比如說,我們現在要輸入搜尋的是以下三段文字:

  1. Google Code Search
  2. Google Code Project Hosting
  3. Google Web Search

處理空白與加上Trigram Indexing

接下來,讓我們真的來處理一些字串,這裡結果就會有點複雜,所以我們只拿第一段文字舉例:

“Google Code Search”來做Trigram Indexing:

"Goo", "oog", "ogl", "gle", "le_", "e_c", "_Co", "Cod", "ode", "e_S", "_Se", "Sea", "ear", "arc", "rch"

在每一個Trigram上加上Document ID

Document ID就是你原本是第幾段文字(以這裡為例子),當然隨著文件變大,有可能是檔案或是磁碟代號. 這裡將以上的每個資料都加上 {1}

將Trigram 文字轉成數字方便比對

就像之前提到,要一個個比對文字 “Goo”比對 “Goo” 其實在CPU上面是比較慢的.而且存放成文字也是比較佔空間.所以比較好的方式就是都轉成Ascii的方式:

	"Goo" = uint("G") uint("o") uint("o") = 71 111 111

並且透過位移轉換,將他放成同一段數字uint32

	71 << 16 + 111<< 8 + 111 = 4681583

如此一來,之後再也不用比對文字"Goo"而是比對事不是數字4681583

如果一份文件中出現重複的trigram

前面沒有提到,不過我們還是要處理可能會重複出現的trigram.比如說"Gooood",就會拆解成"Goo", "ooo", "ooo", "ood".其中就有出現兩個"ooo". 所以針對這樣的時候,我們還需要記錄這個trigram在某個Document中出現的次數. 以這個例子而言:

	"Gooood"  = "Goo", "ooo", "ooo", "ood"
				=  4681583, 7303023, 7303023, 7303012
				= 4681583 -> {1, 1} //第一個doc 出現一次
				  7303023 -> {1, 2} //第一個doc 出現一次
				  7303012 -> {1, 1} //第一個doc 出現一次

可以透過這個方式來表示一個trigram indexing 的文件資料.

出現的次數可以作為trigram 刪除的時候參考,輸入的字串也必須要有複數個才能把該文件裡的trigram整個刪除.

如何在儲存這樣的資料

接下來要解釋,如何將Trigram Indexing結果做儲存.主要的方式是透過Golang 的map

	
	var trigramIndex map[uint32][]int
	
	// 這裡指的是透過 trigram -> document ID slice
	// 舉例而言: "Goo" 出現在 Document 1,2,3
	// "Goo"-> {1, 2, 3},假設 "Goo" 在第一個文件出現兩次,第三個文件出現三次
	//      -> { {1,2}, {2,1}, {3,3} }
	// 而 "Goo" 會變成 uint32 4681583
	// 就存放成 trigramIndex[4681583]-> []int= { {1,2}, {2,1}, {3,3} }

查詢 (Query)

透過以上的方式,將所有的文件(以這裡為範例就是三段文字). 做好Trigram Indexing之後,並且全部存成一連串的Doc ID之後. 我們就要來開始進行一個簡單的查詢:

假設我們要查詢"Code"這個字出現在哪些文件中,處理的流程如下:

  • "Code"拆解成 "Cod""ode"
  • 透過轉換變成44194287300197
  • 透過我們儲存好的mapping table,可以找到 4419428 -> {1,2}還有 7300197 -> {1, 2}
  • 講兩個結果做"AND",也就是 {1, 2} AND {1, 2} = {1,2}
  • 查詢結果該文字出現在文件1跟文件2

範例程式

以下寫好我自己的範例程式,大家有興趣可以進去參考看看.https://github.com/kkdai/trigram

相關鏈結

[Android][JNI]如何由JNI thread/callback去呼叫Java Method

image

前言

在執行Android JNI的時候或多或少都需要在JNI中去呼叫Java的函式.(不論是只有Android才能拿到的資料,或是需要做callback)

這裡就介紹如何在 JNI thread (或是 callback)中去呼叫 Java Method.

基礎觀念

先了解基礎概念,大部分而言會有兩種狀況需要由JNI呼叫Java.

  1. Java -> Jni -> 更新另外一個Java
  2. Jni的Callback or Thread -> 更新 Java

會需要這種狀況,大部分都是(2).當然也可能是(1).由於在(2)的狀況下限制比較多而且比較麻煩,所以只講解(2)的部分. (當然,一樣的方式也可以適合給(1)使用.

先講解可能的限制

由於我們是從jni裡面的thread 或是 jni 去呼叫C/C++ module的callback. 所以.. 你拿不到JNI Environment JNIEnv 跟原先呼叫或是你想要呼叫的Activity的jobject

所以這裡要先透過某個 JNI main thread的function.舉例而言,最基本的Hello World JNI 都會有個 stringFromJni() 透過那個main thread的JNI function.

相關流程

  1. 需要紀錄JVM (一般而言 JVM生命週期是每個App launch啟動)
  2. 也必須要記錄你要呼叫的 Activity 的 jobject
  3. 透過JVM取得目前的目前 JNIEnv (透過AttachCurrentThread)
  4. 依照一般流程呼叫CallVoidMethod ->GetMethodID -> CallVoidMethod(如果是呼叫void)
  5. 透過DetachCurrentThread回收相關資源

程式碼:

容易出錯的部分:

錯誤的jobject使用

這邊主要是提醒,由於JNI function 函式裡面的參數 jobject thiz的處理:

JNIEXPORT jstring Java_com_example_hellojni_stringFromJNI( JNIEnv *env, jobject thiz )

裡面的 JNIEnv *envjobject thiz主要解釋如下:

  • JNIEnv *env : 負責處理JNI的環境,千萬注意在thread裡面的JNIEnv會不同.所以要使用上面的AttachCurrentThread
  • jobject thiz: 主要是處理該JNI library owner (Activity) 的Java Object.
    • 這裡要主義的是這個jobject 主要是System.loadLibrary("hello-jni"); 的Java Activity,而不是呼叫JNI function 的Java Object.

所以如果你有一個Java Class (ex:JNIJava)專門處理JNI,另外一個Java Class (ex: MainActivity)去建立 JNIJava然後去呼叫他,如果你需要呼叫回去MainActivity的函式,記得要把該Activity 傳進來. (不然就是要用FindClass找到正確的)

沒有使用AttachCurrentThread,造成CALL_TYPE crash

如果在JNI native debugger 發現有CALL_TYPE error 在 jni.h.代表你再不是JNI main thread去使用main thread的JNIEnv.所以要注意.

相關鏈結

[iOS]安裝XCode的Plugin來操作CocoaPods

前言

其實一直以來有寫iOS的code,不過由於習慣上的因素.一直沒有使用Cocoapods. 不過最近想說弄個小iPhone App搭著Golang的專案. 所以開始玩,不過Cocoapods其實一開始沒有那麼容易入手,所以又看了些plugin希望能讓使用上更簡單.

原本CocoaPods使用方式:

原本使用方式,需要如下表:

  • 安裝CocoaPods sudo gem install cocoapods
  • 撰寫 Podfile (相關細節)
  • 打上 pod install來建立相關必要檔案
  • 打開產生的workspace

這其實還挺繁瑣的,所以去找了plugin

安裝XCode Plugin

###先安裝 Plugin Package Manager - Alcatraz

這個Package Manager在安裝的時候,會跳出一個plugin 沒有經過授權的警告,不過就跳過去吧.

安裝Alcatraz:

curl -fsSL https://raw.githubusercontent.com/supermarin/Alcatraz/deploy/Scripts/install.sh | sh

重新開啟XCode就可以.

###安裝CocoaPods Plugin for Xcode

安裝好Alcatraz重開之後,打開XCode

	[Windows] -> [Package Manager]

裡面選取,CocoaPods Plugin 就可以.

安裝好之後,要設定一下GEM_PATH,先確認一下你安裝Pod的位置,在Console打上:

dirname `which pod`

把結果填入

XCode -> [Product] -> [CocoaPods] -> [GEM_PATH]

就可以直接在XCode上面更新與修改Podfile

如果還有出現相關問題,可以看這裡The command path could not be resolved

相關鏈結:

[Mac OSX] 一些在console的快速鍵設定

前言:

主要是紀錄一下一些在命令列(console mode)下的快捷鍵,希望以後有需要用到可以給自己紀錄一下.

Console的快捷鍵列表

參考這裡來的.

以下兩個主要是一次移動一個字

  • alt ⌥+F to jump Forward by a word
  • alt ⌥+B to jump Backward by a word

這邊有其他的部分,移動字元,或是移動到一開始.

  • ctrl+A to jump to start of the line
  • ctrl+E to jump to end of the line
  • ctrl+K to kill the line starting from the cursor position
  • ctrl+Y to paste text from the kill buffer
  • ctrl+R to reverse search for commands you typed in the past from your history
  • ctrl+S to forward search (works in zsh for me but not bash)
  • ctrl+F to move forward by a char
  • ctrl+B to move backward by a char

在iTerm2 裡面編輯快捷鍵來使用一次跳一個字

參考這裡

  • 按下 cmd + ,
  • 選取 Profile -> Keys
  • 加入以下: 使用 Alt+ <-來向左跳一個字
    • Keyboard Shortcut: ⌥←
    • Action: Send Escape Sequence
    • Esc+: b
  • 加入以下: 使用 Alt+ ->來向右跳一個字 - Keyboard Shortcut: ⌥→
    • Action: Send Escape Sequence
    • Esc+: f

[Golang] 學習關於Twitter的Three-legged Authentication

前言

主要是因為Twitter上面有人提出來想寫一個小遊戲

當然,這時候就給了正在愁不知道要怎麼讓自己Project52新的專案的我有了想法來寫.

所以我的目標是寫一個Golang的Package可以去做server-side oauth,並且讀取一些Twitter上面的資訊.

關於Twitter的Three-legged Authentication

How To Implement Sign in With Twitter - Web Solutions Blog

上面這張圖清楚的顯示該如何做Twitter的OAuth Login,這裡講的主要都是Server-side的部分.簡單來說步驟主要分為以下數個:

  • 透過你在Twittter Dev App申請的App Consumer Key 跟 Consumer Secret連線到oauth/request_token 開始認證
  • Twitter Server會給你另外一個 token URL 會連線到另外一個網址(該網址為Twitter擁有)去輸入帳號密碼或是同意該App使用你的帳戶資料.
  • Twitter確認完畢後,會自動轉移到當初App設定好的Callback URL
  • 你所架設Callback URL會收到Twitter Server呼叫,並且給你以下三個資料確認你登入的狀況.
    • oauth_token
    • oauth_token_secret
    • oauth_callback_confirmed
  • 只要oauth_callback_confirmed是正確的,你就可以透過oauth_tokenoauth_token_secret去連接到該使用者的一些資訊.

設定正確的App資訊

只要到Twitter的Dev App 申請頁面,就會看到以下的幾個欄位.

不過最重要的欄位還是

  • Callback URL
  • Callback URL
  • Callback URL

很重要要講三次,因為很容易讓你卡很久就是這裡.

如果你要寫server-side的oauth的話,你必須要填入可以被接受的網址. 這裡定義可以被接受網址如下:

  • 必須不是 localhost
  • 必須有 https://

如果你沒有填入資料到Callback URL的話,你就會被當成是Desktop App而無法進行Three-legged Authentication

填好這一切的資訊,就可以拿到consumer keyconsumer secret來繼續以下的部分.

開始架設本地端測試流程

等等! 你剛剛不是說不能把Callback URL寫成 localhost嗎? 這邊就是要教導各位,如何在本地端測試與撰寫關於Server-side OAuth的流程.

  • 修改你的hosts,並且將localhost改成比較有意義的. (ex: 個人改成 testgoserver.com)
  • 記得testgoserver.com 填入Callback URL

這邊建議各位,直接參考這一段Twitter Server Go

跑起來後,記得在瀏覽器打上 http://testgoserver.com就可以了.

常見的錯誤

Desktop applications only support the oauth_callback value ‘oob’

詳細討論看這裡

表示App被認為是Desktop App,這時候需要去 https://apps.twitter.com/app/YOUAPPID/settings 設定Callback URL

此外,這邊的網址還不能使用localhost.所以你可能得寫成某個真正的網址.

不過建議不要lock,不然會出現以下錯誤

	This client application's callback url has been locked

小專案

最後,我還是整理整個架構與將幾個我馬上會用到的API成一個packagey.放在https://github.com/kkdai/twitter

參考鏈結

[Coursera][Automata] 自動機理論-Automata筆記-第六週(下): Intractable Problems and NP-completeness

image

前言與心得整理

我把第六週的部分拆成上跟下,上半部主要解釋P與NP之間的定義與關係.下半部就是要介紹一些證明P=NP的一些理論與方法.

課堂到了最後,其實課堂裡面有許多有用的理論與推導想法,尤其是NP Complete推導的思路,真的能改變解決問題的思考脈絡與方法.相信也可以在以後解決問題的時候,更快可以判別問題的難度.

相關文章

第六週後半部分的課程內容:

問題難度的排序

根據前一個章節的整理,我們可以把問題依照困難度排列(難->簡單)如下: (假設 P不等於NP)

  • 無解的問題
  • 需要Exponatial Time才能解決的問題
  • 多項式時間,還不能決定的問題 (所謂的 NP類問題)
    • 裡面可以再分成 NP Complete(較難) -> NP (較簡單)
  • 多項式時間,可以決定的問題 (所謂的 P類問題)

先回過頭看 Polynomial Reduction

在繼續看Cook’s Theorem定義之前,根據NPTEL的影片,其實有很多詳細的介紹.不過針對許多名詞有不同的定義:

首先先回過頭來看polynomial time reducibility定義如下:

假設一個可判定圖靈機(DTM),從輸入為x處理並且輸出y的處理時間為Polynomial time-bond.如果x屬於L2,我們可以寫L2從L1polynomial time reducible過來的如果滿足以下條件:

  • L2 屬於NP類問題,如果L原本是NP類問題
  • L2 屬於P類問題,如果原本L是屬於P類問題

這邊也就是解釋,我們可以透過reduction把不同的問題經過歸約(reduction)後到可以處理(或是判斷)為哪一類問題. 就可以回過頭來判斷轉換錢的問題是屬於那一類的問題.

再來,根據原有的plynomial time reducibility,我們會引入另外一個名詞定義polynomially transformable如下:

	A language L1 is polynomially transformable to L2, if there is a deterministic polynomial-time-bounded Turing machine M which will convert each string w1 in the alphabet of L1 into a string w2 in the alphabet of L2, such that w1 is in L1 if and only if w2 is in L2 
  • L1 is NP-Complete and L1 polynomially transformable L2. To prove NP L2 is NP-Complete
  • Two condition must be satisfied:
    • L2 belong to NP (It is given)
    • Try to find L’ which belong to NP, and let L’ polynomially transformable to L2
      • How?:
        • Because L1 is NP, So L’ must could polynomially transformable L2
        • According to transition theroem:
          • L’ polynomially transformable L1 (because L’ is NP)
          • (given) L1 polynomially transformable L2
          • So L’ must polynomially transformable L2

繼續講解Cook-Levin理論 (或稱為Cook’s Theorem)

回過頭來試圖去證明 SAT(SATisfiability problem)是NP-Complete,首先我們已經知道SAT是NP類的問題.

(這張圖就是我們的計劃)

想要證明SAT是NP Complete

  • 先證明SAT是NP Hard (被Reduction後依舊是NP類問題)
  • SAT Reduction 成 3SAT 而 3SAT又被Reduction 成其他 NP類問題
  • 可以證明SAT是NP Complete

將所有NP類問題,透過歸約都能轉換成SAT

不過這個時候,為了要證明P=NP我們需要證明任何NP類問題L都能被歸約(Polynomial Reduction)成SAT.以下是我們的目標與方向:

  • 已知 L為一個NP類的問題
  • 針對L建立一個圖靈機MM為一個NTM(Nondeterministic Turing Machine)
  • 針對這樣的問題L的圖靈機M,我們會有輸入w
  • 針對w我們能夠建立出一個布林表示式(Boolean Expression) 並且讓 w 可以透過Boolean Expression來表示.

接下來,就是要建立一個方式可以將所有的問題.將他們的輸入全部轉換成布林表示式.

透過表列式來轉換所有的輸入為布林表示式

概念: 透過查表 K=1,2,…. k 代表第K個輸入

範例:

  • 假設輸入的個數有K個(ex: a, b, c, …. k).
  • 那麼如何透過輸入來表示呢. 假設第一個輸入是b.原先的表示會是X_ij=”b”.
  • 透過轉換,我們會將所有的輸入多了一個維度K (K= 1…k)於是要表達X_ij 就會改成 X_ij1, X_ij2 … X_ijk .
  • 由於X_ij=”b” ==> 所以 X_ij1= false, X_ij2=true, X_ij3=false …… X_ijk=false
  • 另外一個例子: 假設是X_38= “c” –> X_381= false, X_382=false, X_384=true.

透過這樣的方式,我們可以把所有的NP類問題轉換成SAT的問題.接下來我們可以繼續往下做.

更多關於 NP類的問題

接下來的部分會提到更多關於NP類的問題,會提到NP-Hard,Co-NP.主軸會圍繞在透過將3-SAT轉換成其他的問題來解釋更多關於NP的定義.

Tautology Problem

Tautology Problem(也就是恆真式): 指的就是一個表示式一定為真(true). 比如說:

  • X or !X (不論X是什麼一定為真)
  • ! (A or B) = !A and !B

Co-NP(反NP)

所謂的Co-NP也就是該語言L的補集L’(Completement)為NP,如此一來就稱L為Co-NP類問題.

這邊也有一個開放的問題(open problem:只尚未解決的問題),就是if P=Co-NP then P=NP=Co-NP

NP-Hard (NP困難)

NP困難指的是原先是NP類的問題,但是透過Reduction之後才會到目前的問題.看了許多的文章都直接稱它為”就是有NP這麼難的問題”.

透過NP-Hard可以再來回頭看,所謂的NP-Complete

NP Complete(NP完全)

NP完全被稱為是最困難的問題,主要是因為如果一個問題被稱為是NP Complete,它會滿足以下:

  • 本身是NP類問題
  • 就算被歸約(reduction)過,依舊是屬於NP類問題 (NP困難)

套句簡單的話來說,NP完全的問題相當的困難.因為就算被歸約後,他依舊屬於NP類的問題.所以是相當困難的象徵.

證明其為NP-Complete問題的變換流程圖

接下來會提到,如果遇到一個問題的時候,要如何證明它是NP-Complete?

流程概念會是:

  • 先證明該問題為NP類問題
  • 透過下圖的轉換方式,可以轉換成任何其中一個.(ex: 歸約成SAT or 3-CNF SAT…)
  • 由於SAT與3-CNF SAT都是NP-Complete問題,於是就可以證明它也是NP-Complete

NP-Complete問題及證明其為NP-Complete問題的變換流程圖

回過頭來看,為何會說SAT問題是NP完全呢? 因為SAT問題可以透過歸約(Reduction)成3SAT(等等會提到).由於3SAT問題依舊是NP問題,所以我們就可以知道SAT是NP完全問題.

SAT -> 3SAT 轉換

首先回顧一下SAT(SATisfiability problem),指的是一連串的布林表示式,像下者:

	X1 or X2 

如果透過轉換:

	(X1 or X2 or Y) and (X1 or X2 or notY)

這樣的轉換主要是加入Y並且讓原本的邏輯沒有改變.而類似以上的布林表示式(三個為一組的)就稱為 3SAT. 而上面簡單的轉換方式就是Polynomial Reduction.

順便附帶一下,如果x為以下各種個數的時候,由SAT->3SAT的轉換方式:

  • SAT: X:
    • 3SAT:
      • (X or Y1 or Y2) and (X or (not Y1) or Y2) and (X or Y1 ir (not Y2)) and (X or (not Y1) or (not Y2))
  • SAT: X1 or X2:
    • 3SAT:
      • (X1 or X2 or Y) and (X1 or X2 or notY)
  • SAT: X1 or X2 or X3 -> 3SAT一樣不用轉換
  • SAT: X1 or X2 .... Xk
    • 3SAT:
      • (X1 or X2 or Y1) and ((not Y1) or X3 or Y2) and ( (not Y2) or X4 or Y3) ….. ( (not Yk-4) or Xk-2 or Yk-3) and ( (not)Yk-3 or Xk-1 or Xk)

經過轉換,由於3SAT本身被證明依舊是NP類問題.所以可以知道SAT本身是NP困難.也可以說SAT是NP完全問題.

學Automata 對我們的好處

接下來的課程,主要都是不斷的轉換不論是從3SAT歸約成背包問題.或是其他的轉換. 這部分,我就不再去提了…

由於要找轉換,我找了不少其他的學校的投影片與影片.這一篇聯合大學資管系:演算法講義我相當的推薦. 裡面也有提到學P與NP究竟對工作上有什麼樣的影響.(slide p 18)

(引用如下):

假設你在一個公司上班。有一天,上司叫你去為某個對公司很重要的問題找出有效率的演算法。‹結果,你研究了很長的一段時間,沒有任何進展.就在快被解雇的時候,忽然想到世界上很多的電腦科學家正在為旅行推銷員問題(TSP)找尋一個較有效率的演算法。但是,到目前為止卻沒有人能發展出一個在最差情況下,時間複雜度比指數複雜度要來得好的TSP演算法。不過,也沒有人証明出找到這種演算法是不可能的…

你找到了一線生機!! 因為,你只要能証明找出公司問題之有效率演算法的難度和找出旅行推銷員問題的有效率演算法是一樣難的 (亦即:兩者是同一 類的問題),代表著上司要求你解決的問題也曾難倒很多電腦科學家。

你終於証明出來公司的問題和旅行推銷員問題是同一等級的… (這就是Polynomial Reduction,並且也代表原本的問題是NP完全問題)

程式作業/Homework

homework比較多是考關於Polytime reduction的觀念,還算可以.不過11/02~11/09有Final Exam倒是真的讓我需要K書了.

Final Exam: (update on 11/08)

記錄一些值得再去看看的部分,不要因為考完就忘記:

  • DFA_minimization
    • 透過簡化的方式,可以找出相同輸入與輸出下能夠將一些DFA的State做合併.
    • 基本上計算方式透過是否透過相同Input可以到相同的state,來作為合併的挑選.
      • ex: 這張圖 中,c跟d 輸入 1都會到 f,而輸入0都會到e.所以可以合併.

相關程式

這星期主要是實作TM(Turing Machine),其實要實作圖靈機並不難,只是要找到比較好的圖靈機範例可以測試倒是比較麻煩的.

可以到這裡看看https://github.com/kkdai/tm

參考網址