2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
15 |
16 |
21 |
22 |
羅素出生於1872年的一個貴族家庭,當時大英帝國正值巔峰,逝於1970年,此時英國經歷過兩次世界大戰,其帝國已經沒落。
23 |
羅素的家族相當顯赫,祖父約翰·羅素勛爵在1840年代曾兩次出任英國首相,是「輝格黨」的核心成員。羅素的父母分別在他兩歲與四歲時過世,因此羅素是擔任兩次首相的祖父所扶養長大的。
24 |
27 |
在婚姻生活方面,羅素於 17 歲時便與美國籍的 Alys Pearsall Smith 墜入情網,22 歲時兩人結婚,但由於羅素有婚外情的關係,所以在 39 歲時離婚了。49 歲時又娶了 Dora Black 並育有兩個孩子,55 歲時夫妻兩人一起創立了「皮肯·希爾教育實驗學校」(Beacon Hill School),後來羅素因反戰活動而被劍橋大學開除。接著羅素又有了婚外情,於是與 Dora Black 又離婚了。64 歲時羅素與牛津大學學生 Patricia Spence 結婚。72 歲時羅素回到英國,並重新執教於劍橋大學三一學院。80 歲時羅素再度離婚,並與一名美國的英語教授結婚。88 歲時羅素出版了自傳,並曾參與了甘迺迪遇刺事件的調查。後來羅素活到了 98 歲高齡,於1970年去世。
28 |
在學術方面,羅素 18 歲時進入劍橋大學三一學院學習哲學、邏輯學和數學。他起初對數學感興趣,並認為數學是邏輯學的一部分,於是試圖用邏輯建構整個數學體系,1901 年他發現了「理髮師悖論」,並於 1910 年與老師懷海德一起發表了《數學原理》這部巨著。
29 |
所謂的「羅素理髮師悖論」,是有「一位理髮師,宣稱要為所有不自己理頭髮的人理髮,但是不為任何自己理頭髮的人理髮」。這句話是有問題的,因為該理髮師會在要不要為自己理頭髮這件事情上產生矛盾。
30 |
羅素發現的悖論,也讓他開始思考邏輯學的問題,後來他與「摩爾、弗雷格、維根斯坦和懷特海」等人創立了「邏輯分析哲學」,企圖將哲學問題轉化為邏輯符號,讓哲學家們就能夠更容易地推導出結果,而不會被不夠嚴謹的語言所誤導。
31 |
他們企圖建構出能夠解釋世界本質的理想邏輯語言。但是「哥德爾」所證明的「不完備定理」卻毀了這個想法,
32 |
1920年羅素訪問俄國和中國,並在北京與杜威同時期進行一年的講學,在長沙時期,青年毛澤東曾經擔任記錄員。他回到歐洲後解了一本《中國問題》,孫中山並稱羅素為「唯一真正理解中國的西方人」。
33 |
徐志摩年輕時曾經到英國劍橋想拜羅素為師,但那時羅素已經離開劍橋大學,否則徐志摩很可能會成為羅素的學生。
34 |
羅素的著作很多,列舉如下:
35 |
《幾何學基礎》(1897年)
36 | 《數學原理》(懷特海合著,1910, 1912, 1913年)
37 | 《哲學論文集》(1910年)
38 | 《哲學問題》(1912年)
39 | 《社會重建原則》(1916年)
40 | 《自由之路》(1918年)
41 | 《中國問題》(1922年)
42 | 《工業文明的前景》(合著,1923年)
43 | 《科學的未來》(1924年)
44 | 《相對論入門》(1925年)
45 | 《論兒童教育》(1926)
46 | 《物之分析》(The Analysis of Matter,1927年)
47 | 《我為什麼不是基督徒》(1927年)
48 | 《心靈分析》(1927年)
49 | 《懷疑論》(1928年)
50 | 《婚姻與道德》(1929年),(1950年,羅素因此書而獲得諾貝爾文學獎)
51 | 《幸福的贏得》(1930年)
52 | 《哲學與現代世界》(1932年)
53 | 《自由與組織》(1934年)
54 | 《宗教與科學》(1935年)
55 | 《權力:一種新的社會分析》(1938年)
56 | 《西方哲學史》(1945年)
57 | 《權威與個人》(1949年)
58 | 《名人的惡夢》(1954年)
59 | 《羅素回憶錄》(1956年)
60 | 《我的哲學發展》(1959年)
61 | 《人類有將來嗎》(1962年)
62 | 《文明之路》
63 | 《人類為什麼戰鬥》
64 |
羅素於 67 歲時搬到美國,但卻因為《婚姻與道德》這部作品而被法官「麥吉漢」認為有道德問題,取消了「紐約城市大學聘任羅素為哲學教授的任命」一案,但是到了 78 歲的時候,羅素卻又因這部作品而得到「諾貝爾文學獎」,成為第一個非文學領域作家得到諾貝爾文學獎的人。
65 |
筆者對羅素為何會以《婚姻與道德》獲得「諾貝爾文學獎」這件事較有興趣,於是上網查了一下這本書,其內容如下:
66 |
第一章 為什麼需要性道德
67 | 第二章 無父之鄉
68 | 第三章 父親的勢力範圍
69 | 第四章 生殖器崇拜,制慾主義,與罪惡
70 | 第五章 基督教的道德
71 | 第六章 浪漫的愛
72 | 第七章 婦女的解放
73 | 第八章 性知識的禁忌
74 | 第九章 愛在人生中的地位
75 | 第十章 婚姻
76 | 第十一章 娼妓
77 | 第十二章 試婚制
78 | 第十三章 現在的家庭
79 | 第十四章 家庭對個人心理的影響
80 | 第十五章 家庭與國家
81 | 第十六章 離婚
82 | 第十七章 人口
83 | 第十八章 優生學
84 | 第十九章 性與個人的福利
85 | 第二十章 性在人類價值中的地位
86 | 第二十一章 結論
87 |
從這本書的目錄中,我想大家應該也可以理解為何羅素結婚、外遇與離婚的次數那麼的頻繁了,以及為何會遭受到那麼多的爭議,我想這應該也與他的學術思想有關吧!
88 |
89 |
93 |
【本文由陳鍾誠取材並修改自 維基百科 ,採用創作共用的 姓名標示、相同方式分享 授權】
94 |
95 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/htm/info.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
15 |
16 |
26 |
27 |
28 |
程式人雜誌是一個結合「開放原始碼與公益捐款活動」的雜誌,簡稱「開放公益雜誌」。開放公益雜誌本著「讀書做善事、寫書做公益」的精神,我們非常歡迎程式人認養專欄、或者捐出您的網誌,如果您願意成為本雜誌的專欄作家,請加入 程式人雜誌社團 一同共襄盛舉。
29 |
我們透過發行這本雜誌,希望讓大家可以讀到想讀的書,學到想學的技術,同時也讓寫作的朋友的作品能產生良好價值 – 那就是讓讀者根據雜誌的價值捐款給慈善團體。 讀雜誌做公益也不需要有壓力,您不需要每讀一本就急著去捐款,您可以讀了十本再捐,或者使用固定的月捐款方式,當成是雜誌訂閱費,或者是季捐款、一年捐一次等都 OK ! 甚至是單純當個讀者我們也都很歡迎!
30 |
本雜誌每期參考價:NT 50 元,如果您喜歡本雜誌,請將書款捐贈公益團體。例如可捐贈給「羅慧夫顱顏基金會 彰化銀行(009) 帳號:5234-01-41778-800」。(若匯款要加註可用「程式人雜誌」五個字)
31 |
32 |
給專欄寫作者: 做公益不需要有壓力。如果您願意撰寫專欄,您可以輕鬆的寫,如果當月的稿件出不來,我們會安排其他稿件上場。
33 |
給網誌捐贈者: 如果您沒時間寫專欄或投稿,沒關係,只要將您的網誌以 [創作共用的「姓名標示、非商業性、相同方式分享」授權] 並通知我們,我們會自動從中選取需要的文章進行編輯,放入適當的雜誌當中出刊。
34 |
給文章投稿者: 程式人雜誌非常歡迎您加入作者的行列,如果您想撰寫任何文章或投稿,請用 markdown 或 LibreOffice 編輯好您的稿件,並於每個月 25 日前投稿到程式人雜誌社團 的檔案區,我們會盡可能將稿件編入隔月1號出版程式人雜誌當中,也歡迎您到社團中與我們一同討論。
35 |
如果您要投稿給程式人雜誌,我們最希望的格式是採用 markdown 的格式撰寫,然後將所有檔按壓縮為 zip 上傳到社團檔案區給我們, 如您想學習 markdown 的撰寫出版方式,可以參考 看影片學 markdown 編輯出版流程 一文。
36 |
如果您無法採用 markdown 的方式撰寫,也可以直接給我們您的稿件,像是 MS. Word 的 doc 檔或 LibreOffice 的 odt 檔都可以,我們 會將這些稿件改寫為 markdown 之後編入雜誌當中。
37 |
38 |
您也可以擔任程式人雜誌的編輯,甚至創造一個全新的公益雜誌,我們誠摯的邀請您加入「開放公益出版」的行列,如果您想擔任編輯或創造新雜誌,也歡迎到 程式人雜誌社團 來與我們討論相關事宜。
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
52 |
53 |
54 |
55 | 財團法人羅慧夫顱顏基金會
56 | http://www.nncf.org/ lynn at nncf dot org 02-27190408分機 232
62 | 顱顏患者 (如唇顎裂、小耳症或其他罕見顱顏缺陷)
63 | 銀行:009彰化銀行民生分行 帳號:5234-01-41778-800
64 |
65 |
66 | 社團法人台灣省兒童少年成長協會
67 | http://www.cyga.org/ cyga99 at gmail dot com 04-23058005
73 | 單親、隔代教養.弱勢及一般家庭之兒童青少年
74 | 銀行:新光銀行 戶名:台灣省兒童少年成長協會 帳號:103-0912-10-000212-0
75 |
76 |
77 |
78 |
79 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/htm/article2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
15 |
16 |
21 |
22 |
擴增實境(Augmented Reality,簡稱 AR、 維基百科 )這個技術,主要的概念就是把攝影機實際拍到的畫面,在顯示出來的同時,根據畫面的內容,加上其他的資訊、畫面,讓虛擬的物體,可以和真實的廠景融合在一起顯示。
23 |
由於把虛擬和現實融合,算是一個相當有趣、而且也相當特別的應用,所以其實在這方面的應用、研究,也都算滿多的;再加上現在許多行動手持裝置,不但配備了小型化的攝影機,也都有足夠的計算能力了,所以在近年來,也算是越來越熱門。不過實際上,這種概念並不算非常地新,在 1994 年就已經有人提出來了~到目前為止,也算是發展好一段時間了。
24 |
27 |
而雖然 AR 的理想,是可以直接辨識畫面的內容,來做物體的辨識、定位,但是礙於實際上的計算效率、準確性,現階段比較普遍的應用,應該都還是需要特殊哪片、也就是需要「mark」來做辨識、定位的 AR。像上圖,就是一套算是相當成熟的 AR 開放原始碼函式庫、ARToolkit( 官網 、 維基百科 )的示意圖;裡面的人所拿著的,就是專門的 AR 卡片,上面就是即時辨識出這張卡片,並把虛擬人物放進去的效果。
28 |
不過,雖然說 ARToolKit 是一個相當成熟的函式庫,但是由於他已經沒有在繼續維護了(News 最後是 2007 年底、提供的 Windows 環境也是到 Visual Studio .Net 2003 而已),所以以現在的觀點來看,在使用上算是有相當地限制…像是他雖然是以 OpenGL 來做 3D 繪圖的函式庫,但是如果要和新的 Shader-based OpenGL 來做整合,似乎也有不少問題。所以當 Heresy 這邊要做 AR 相關的應用的時候,馬上就覺得他並不適和 Heresy 這邊的需求。
29 |
而 Heresy 後來使用的,則是使用以 OpenCV( 官網 )這套電腦視覺函式庫所提供的功能為基礎的方法;實作上,主要則是參考 OpenCV-AR( 首頁 )這個 SourceForge 上的開放原始碼專案,來修改而實作完成的。
30 |
在這個函式庫的實作裡面,他主要概念,是去偵測畫面中的四邊形、然後把抓到的四邊形影像,轉換成為正方形的圖片,根據指定的樣板進行辨識;如果辨識的相似度夠高,則就視為是要處理的 marker,根據四邊形的四個點來計算出所應對應的矩陣,讓顯示的程式可以透過這個矩陣,來把 3D 的物體畫在對應的位置上。
31 |
而由於他是基於四邊形偵測來做處理的,而且辨識的過程又是把影像轉換成正方形,所以它基本上能接受的 marker,就變成一定是要是有明確的四邊形外框的正方形影像了~下面兩張圖,就是這個函式庫所給的兩個範例的 marker:
32 |
35 |
上面兩張圖,基本上是 OpenCV AR 能接受的兩種不同形式的 marker。 左邊的圖基本上是一種比較簡單的形式。他基本上是放大過的圖,原圖大小應該要是 10×10 個像素,每個像素都只有黑或白兩種可能;而由於實際上為了確保外圍的邊還是四邊形,所以外面是不能有白色的;也就是資訊都會記錄在裡面的 8×8、共有 64 個像素的矩形裡。(大小可以透過修改程式來調整) 右邊的則是一個黑底、再加上一般圖片,基本上只要確定黑底在外圍可以構成完整的四邊形、同時影像是方形的,就可以了~而雖然他是給彩色的圖片,不過為了減少環境光造成的影響、同時也減低計算量,所以實際上在內部處理的時候,都是轉換成灰階來做計算的。圖片的大小在程式中並沒有做額外的限制,但是由於圖片越大計算量會越多,所以建議不要用太大的圖檔當作 marker。
36 |
前者基本上比較單純,在程式裡面會轉換成編碼過的資料,直接進行比對;基本上不但效率比較好、準確性也比較高;但是相對的,可以變化的幅度就比較小了。
37 |
下方是在進行每一個畫面處理時,比較完整的流程圖:
38 |
41 |
首先,右邊的「Template Markers」是在初始化階段,讀取進來的 marker 的資料,是用來做比對用的樣板(以下稱為 template marker)。而左邊的「Recognized Markers」則是用來記錄成功辨識到的 marker、以及其相對的位置資訊;這個資料在完成後並不會被清除,而是留給外部做存取、以及給下一個畫面做追蹤用的。
42 |
在開始後,他會從攝影機取得當下的畫面,做一些處理後,就開始在畫面中偵測四邊形、也就是上圖中「Find Square」的區塊。而在找到畫面中的四邊形後,針對每一個找到的四邊形,會去和之前找到的 marker(Recognized Markers)做比對,如果夠接近的話,就當作是同一個 Marker,然後直接更新它的位置;這基本上算是用追蹤位置的方法,來簡化整個流程的計算量。 而在做完這個動作後,則是會把其他沒有更新過的 Marker 從記錄中刪除,讓 Recognized Markers 裡面只保存在這個畫面,已經追蹤到的 marker。
43 |
接下來,則是針對剩下來、沒辦法用 track 來解決的四邊形、則是會把它轉換成正方形的影像後,針對 template markers 裡的所有樣板資料,一筆一筆去做比對,藉此來找出這個四邊形最像的 template marker;這也是整個流程裡面,計算量最大的地方。
44 |
47 |
上圖就是這邊的示意圖,左邊的藍色框框,就是找到的四邊形,而強制把他轉換成正方形的影像後,就會像右邊的圖一樣;之後就是透過這個轉換後的影像,來進行比對的。而針對不同類型的 marker,其實也有不同的比對方法,這個就等之後講到這部分再來說了~ 而這樣找出來的結果,可能會有不同的四邊形,都對應到同一個 template marker;而為了避免這樣的問題,這裡則會再去針對每一個 template marker、都去做一次比較,來找出相似度最高的四變形,並以此為最終的結果,存入 Recognized Markers 中。 如果找完後,完全沒有找到四邊形的話,他會去修改進行 binary threshold 時的 threshold 值(他是用亂數產生),來試著讓程式可以在下一個畫面,找到更多四邊形進行測試。 最後,下面就是最後結果的示意圖。左邊是用來偵錯的畫面,裡面的藍色和紅色框框,就是有偵測到的四邊形;由於只是在偵測四邊形,所以可以看到,裡面的手指間,也被認為是一個四邊形了~=而其中藍色是代表有偵測到,但是沒有找到對應的 template marker,而紅色則是有找到對應的,所以可以看到在右邊的結果畫面裡,「Tw」這個 Maker 已經被一個台灣的 3D 物件取代了~
48 |
51 |
這篇基本上是概念和架構性的文章,大概就先到這邊了。之後有時間,再來寫實作的內容介紹吧~
52 |
【本文來自 Heresy's Space 的網誌,原文網址為: http://kheresy.wordpress.com/2012/12/27/ar-by-opencv/ ,由 Heresy 捐出網誌給程式人雜誌,經陳鍾誠編輯後納入雜誌】
53 |
54 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/code/js/bak/jparser.js:
--------------------------------------------------------------------------------
1 | var fs = require("fs");
2 | require("./ccc");
3 |
4 | var tokens = [];
5 | var tokenIdx = 0;
6 |
7 | var scan=function(text) {
8 | var re = new RegExp(/(\/\*[\s\S]*?\*\/)|(\/\/[^\r\n])|(".*?")|(\d+(\.\d*)?)|([a-zA-Z]\w*)|([>== tokens.length)
40 | return false;
41 | var token = tokens[tokenIdx].token;
42 | if (o instanceof RegExp) {
43 | return token.match(o);
44 | } else {
45 | return (token == o);
46 | }
47 | }
48 |
49 | var isNextType=function(pattern) {
50 | var type = tokens[tokenIdx].type;
51 | return (("|"+pattern+"|").indexOf("|"+type+"|")>=0);
52 | }
53 |
54 | var compile=function(text) {
55 | printf("text=%s\n", text);
56 | scan(text);
57 | printf("tokens=%j\n", tokens);
58 | PROG();
59 | }
60 |
61 | // NTS = NT { sep NT }*
62 | var repeat=function(f, sep) {
63 | f();
64 | while (isNext(sep)) {
65 | next(sep);
66 | f();
67 | }
68 | }
69 |
70 | // PROG = STMTS
71 | var PROG=function() {
72 | repeat(STMT, ';');
73 | }
74 |
75 | // BLOCK = { STMTS }
76 | var BLOCK=function() {
77 | next("{");
78 | if (!isNext("}")) repeat(STMT, ";");
79 | next("}");
80 | }
81 |
82 | // STMT = FOR | WHILE | IF | return EXP | ASSIGN
83 | var STMT=function() {
84 | if (isNext("for")) {
85 | FOR();
86 | } else if (isNext("while")) {
87 | WHILE();
88 | } else if (isNext("if")) {
89 | IF();
90 | } else if (isNext("return")) {
91 | next("return");
92 | EXP();
93 | } else {
94 | ASSIGN();
95 | }
96 | }
97 |
98 | // FOR = for (STMT; EXP; STMT) BLOCK
99 | var FOR=function() {
100 | next("for"); next("("); STMT(); next(";"); EXP(); next(";"); STMT(); next(")"); BLOCK();
101 | }
102 |
103 | // WHILE = while (EXP) BLOCK
104 | var WHILE=function() {
105 | next("while"); next("("); EXP(); next(")"); BLOCK();
106 | }
107 |
108 | // IF = if (EXP) BLOCK (else if (EXP) BLOCK)* (else BLOCK)?
109 | var IF=function() {
110 | next("if"); next("("); EXP(); next(")"); BLOCK();
111 | while (isNext("else")) {
112 | next("else");
113 | if (isNext("if")) {
114 | next("if"); next("("); EXP(); next(")"); BLOCK();
115 | } else {
116 | BLOCK();
117 | }
118 | }
119 | }
120 |
121 | // ASSIGN = (ID[++|--]?)?(=EXP)?
122 | var ASSIGN=function() {
123 | var id="", op="";
124 | if (isNextType("id")) {
125 | id = next(null);
126 | if (isNext("++") || isNext("--"))
127 | op = next(null);
128 | }
129 | if (isNext("=")) {
130 | next("=");
131 | EXP();
132 | }
133 | }
134 |
135 | // EXP=TERM (op2 TERM)?
136 | var EXP=function() {
137 | t1 = TERM();
138 | if (isNextType("op2")) {
139 | var op = next(null);
140 | TERM();
141 | }
142 | }
143 |
144 | // TERM=STRING | INTEGER | FLOAT | FUNCTION | ARRAY | TABLE | ID (TERMS)? | ( EXP )
145 | var TERM=function() {
146 | if (isNextType("string|integer|float")) {
147 | return next(null);
148 | } else if (isNext("function")) {
149 | FUNCTION();
150 | } else if (isNext("[")) {
151 | ARRAY();
152 | } else if (isNext("{")) {
153 | TABLE();
154 | } else if (isNextType("id")) {
155 | id=next(null);
156 | } else if (isNext("(")) {
157 | next("("); EXP(); next(")");
158 | if (isNext("(")) { next("("); TERMS(); next(")"); }
159 | } else error();
160 | }
161 |
162 | // FUNCTION = function(IDS) BLOCK
163 | var FUNCTION = function() {
164 | next("function"); next("(");
165 | if (!isNext(")")) repeat(ID, ",");
166 | next(")"); BLOCK();
167 | }
168 |
169 | // ARRAY = [ TERMS ];
170 | var ARRAY = function() {
171 | next("[");
172 | if (!isNext("]")) repeat(TERM, ",");
173 | next("]");
174 | }
175 |
176 | // TABLE = { PAIRS }
177 | var TABLE = function() {
178 | next("{");
179 | if (!isNext("}")) repeat(PAIR, ",");
180 | next("}");
181 | }
182 |
183 | // PAIR = TERM:TERM
184 | var PAIR = function() {
185 | TERM(); next(":"); TERM();
186 | }
187 |
188 | var source = fs.readFileSync("test.j1", "utf8");
189 | compile(source);
190 | compile(fs.readFileSync("test2.j1", "utf8"));
--------------------------------------------------------------------------------
/code/js/as0.js:
--------------------------------------------------------------------------------
1 | var c = require("./ccc"); // 引用基本函式庫 ccc.js
2 | var as = require("./as"); // 引用抽象組譯器物件 as.js
3 | var code = require("./code"); // 引用指令物件 code.js
4 | var cpu0 = require("./cpu0"); // 引用處理器物件 cpu0.js
5 |
6 | var as0 = new as(cpu0.opTable); // 建立 as0 組譯器物件
7 |
8 | as0.parse = function(line) { // 剖析組合語言指令,建立 code 物件
9 | return new code(line, this.opTable);
10 | }
11 |
12 | as0.translate = function(code) { // 指令的編碼函數
13 | var ra=0, rb=0, rc=0, cx=0;
14 | var pc = code.address + 4; // 提取後PC為位址+4
15 | var args = code.args, parseR = code.parseR; // 取得 code 物件的函數
16 | var labelCode = null; // JMP label 中 label 所對應行的物件,稱為 labelCode
17 | if (code.op == undefined) { // 如果沒有指令碼 (只有標記),則清空目的碼
18 | code.obj = "";
19 | return;
20 | }
21 | switch (code.op.type) { // 根據指令型態
22 | case 'J' : // 處理 J 型指令,編出目的碼 OP Ra+cx
23 | switch (code.op.name) {
24 | case "RET": case "IRET" : // 如果式返回或中斷返回,則只要輸出 op 碼
25 | break;
26 | case "SWI" : // 如果是軟體中斷指令,則只有 cx 參數有常數值
27 | cx = parseInt(args[0]);
28 | break;
29 | default : // 其他跳躍指令,例如 JMP label, JLE label 等
30 | labelCode = this.symTable[args[0]]; // 取得 label 符號位址
31 | cx = labelCode.address - pc; // 計算 cx 欄位
32 | break;
33 | }
34 | code.obj = c.hex(code.op.id,2)+c.hex(cx, 6); // 編出目的碼 OP Ra+cx
35 | break;
36 | case 'L' : // 處理 L 型指令,編出目的碼 OP Ra, Rb, cx
37 | ra = parseR(args[0]); // 取得 Ra 欄位
38 | switch (code.op.name) {
39 | case "LDI" : // 處理 LDI 指令
40 | cx = parseInt(args[1]); // 取得 cx 欄位
41 | break;
42 | default : // 處理 LD, ST, LDB, STB 指令
43 | if (args[1].match(/^[a-zA-Z]/)){ // 如果是 LD LABEL 這類情況
44 | labelCode = this.symTable[args[1]]; // 取得標記的 code 物件
45 | rb = 15; // R[15] is PC
46 | cx = labelCode.address - pc; // 計算標記與 PC 之間的差值
47 | } else { // 否則,若是像 LD Ra, Rb+100 這樣的指令
48 | rb = parseR(args[2]); // 取得 rb 欄位
49 | cx = parseInt(args[3]); // 取得 cx 欄位 (例如 100)
50 | }
51 | break;
52 | }
53 | code.obj = c.hex(code.op.id, 2)+c.hex(ra, 1)+c.hex(rb, 1)+c.hex(cx, 4); // 編出目的碼 OP Ra, Rb, cx
54 | break;
55 | case 'A' : // 處理 A 型指令,編出目的碼 OP Ra, Rb, Rc, cx
56 | ra = parseR(args[0]); // 取得 Ra 欄位
57 | switch (code.op.name) {
58 | case "LDR": case "LBR": case "STR": case "SBR": // 處理 LDR, LBR, STR, SBR 指令,例如 LDR Ra, Rb+Rc
59 | rb = parseR(args[1]); // 取得 Rb 欄位
60 | rc = parseR(args[2]); // 取得 Rc 欄位
61 | break;
62 | case "CMP": case "MOV" : // 處理 CMP 與 MOV 指令,CMP Ra, Rb; MOV Ra, Rb
63 | rb = parseR(args[1]); // 取得 Rb
64 | break;
65 | case "SHL": case "SHR": case "ADDI": // 處理 SHL, SHR, ADDI 指令,例如 SHL Ra, Rb, Cx
66 | rb = parseR(args[1]); // 取得 Rb 欄位
67 | cx = parseInt(args[2]); // 取得 cx 欄位 (例如 3)
68 | break;
69 | case "PUSH": case "POP": case "PUSHB": case "POPB" : // 處理 PUSH, POP, PUSHB, POPB
70 | break; // 例如 PUSH Ra, 只要處理 Ra 就好,A 型一進入就已經處理 Ra 了。
71 | default : // 其他情況,像是 ADD, SUB, MUL, DIV, AND, OR, XOR 等,例如 ADD Ra, Rb, Rc
72 | rb = parseR(args[1]); // 取得 Rb 欄位
73 | rc = parseR(args[2]); // 取得 Rc 欄位
74 | break;
75 | }
76 | code.obj = c.hex(code.op.id, 2)+c.hex(ra, 1)+c.hex(rb, 1)+c.hex(rc,1)+c.hex(cx, 3); // 編出目的碼 OP Ra, Rb, Rc, cx
77 | break;
78 | case 'D' : { // 我們將資料宣告 RESW, RESB, WORD, BYTE 也視為一種指令,其形態為 D
79 | var unitSize = 1; // 預設的型態為 BYTE,資料大小 = 1
80 | switch (code.op.name) {
81 | case "RESW": case "RESB": // 如果是 RESW 或 RESB,例如 a:RESB 2
82 | code.obj = c.dup('0', this.size(code)*2); // 1 個 byte 的空間要用兩個16進位的 00 去填充
83 | break; // 例如:a RESB 2 會編為 '0000'
84 | case "WORD": // 如果是 WORD ,佔 4 個 byte
85 | unitSize = 4;
86 | case "BYTE": { // 如果是 BYTE ,佔 1 個 byte
87 | code.obj = ""; // 一開始目的碼為空的
88 | for (var i in args) { // 對於每個參數,都要編為目的碼
89 | if (args[i].match(/^\".*?\"$/)) { // 該參數為字串,例如: "Hello!" 轉為 68656C6C6F21
90 | var str = args[i].substring(1, args[i].length-1); // 取得 "..." 中間的字串內容
91 | code.obj += c.str2hex(str); // 將字串內容 (例如 Hello!) 轉為 16 進位 (例如 68656C6C6F21)
92 | } else if (args[i].match(/^\d+$/)) { // 該參數為常數,例如 26
93 | code.obj += c.hex(parseInt(args[i]), unitSize*2); // 將常數轉為 16 進位目的碼 (例如 26 轉為 1A)
94 | } else { // 該參數為標記,將標記轉為記憶體位址,例如 msgptr: WORD msg 中的 msg 轉為位址 (例如:00000044)
95 | labelCode = this.symTable[args[i]]; // 取得符號表內的物件
96 | code.obj += c.hex(labelCode.address, unitSize*2); // 取得位址並轉為 16 進位,塞入目的碼中。
97 | }
98 | }
99 | break;
100 | } // case BYTE:
101 | } // switch
102 | break;
103 | } // case 'D'
104 | }
105 | }
106 |
107 | // 使用範例 node as0 sum.as0 sum.ob0
108 | // 其中 argv[2] 為組合語言檔, argv[3] 為目的檔
109 | as0.assemble(process.argv[2], process.argv[3]);
110 |
--------------------------------------------------------------------------------
/htm/article6.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
28 |
29 |
30 |
31 |
34 |
35 |
40 |
41 |
最近"Z"和"b"的比較鬧得沸沸揚揚,身為R的重度使用者,當然也要來好好的探討一下這個問題。立馬來試試看:
42 |
"Z" > "b"
43 |
## [1] TRUE
44 |
沒錯,R語言在這兩者的比較上是支持 Z大於b 的!
45 |
作者我呢也對此抱持著堅定不移的信念,直到某天我在linux上使用crontab跑的R程式出現了Bug。
46 |
這個Bug非常的隱晦,最困難的地方在於,當我開Rstudio or R console來跑的時候,結果是正確的,但是當我把它丟到crontab下跑的時候,結果卻完全不同。
47 |
經過層層追尋之後,我發現問題就在 Z大於b 上啊。在crontab裡, Z小於b !
48 |
這是怎麼回事?原來這和locale有關。根據?Comparison的描述:
49 |
The collating sequence of locales such as en_US is normally different from C (which should use ASCII) and can be surprising.
50 |
也就是說,當locale(語系)這個環境變數不同的時候,R在做字母的比較是不同的。當語系設定為如"en_US"或"zh_TW"的時候,大小順序應是: A > B > ... > Z > a > b > ... > z,但是當語系設為"C"的時候,R 會把字元轉成對應的ASCII 的整數做比較而此時Z是0x5a,b是0x62,所以Z > b就錯了。
51 |
眼就為憑,我們馬上做點實驗:
52 |
Sys.setlocale (locale = "zh_TW" )
53 |
## [1] "zh_TW/zh_TW/zh_TW/C/zh_TW/zh_TW.UTF-8"
54 |
"Z" > "b"
55 |
## [1] TRUE
56 |
Sys.setlocale (locale = "C" )
57 |
## [1] "C/C/C/C/C/zh_TW.UTF-8"
58 |
"Z" > "b"
59 |
## [1] FALSE
60 |
而一般我們裝R之後,預設會使用如en_US或zh_TW等語系,但是crontab這類環境卻是使用C這個語系。
61 |
也因此,我們可以得出結論:
62 |
63 | Z大於b不一定是對的,一切都要看你身處的環境啊!
64 |
65 |
71 |
80 |
81 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/css/pmag.css:
--------------------------------------------------------------------------------
1 | html, body, div, span, applet, object, iframe,
2 | p, blockquote, pre,
3 | a, abbr, acronym, address, big, cite, code,
4 | del, dfn, em, img, ins, kbd, q, s, samp,
5 | small, strike, strong, sub, sup, tt, var,
6 | b, u, i, center,
7 | dl, dt, dd,
8 | fieldset, form, label, legend,
9 | table, caption, tbody, tfoot, thead, tr, th, td,
10 | article, aside, canvas, details, embed,
11 | figure, figcaption, footer, header, hgroup,
12 | menu, nav, output, ruby, section, summary,
13 | time, mark, audio, video {
14 | margin: 0;
15 | padding: 0;
16 | border: 0;
17 | font: inherit;
18 | vertical-align: baseline;
19 | line-height:160%;
20 | }
21 |
22 | #cover-image {
23 | width:100%;
24 | }
25 |
26 | div>ol.toc {
27 | list-style-type:disc;
28 | }
29 |
30 | ol.toc {
31 | list-style-type:circle;
32 | }
33 |
34 | blockquote {
35 | margin: 10px;
36 | padding-left: 10px;
37 | padding-right: 10px;
38 | padding-top: 1px;
39 | padding-bottom: 1px;
40 | border: 3px double #373737;
41 | color:#333333;
42 | }
43 |
44 | h1, h1 a { font-size: xx-large; color:#050505; text-align:center; margin:30px; font-weight: bold; font-family: 'DFKai-sb', 'Tahoma'; }
45 |
46 | h2, h2 a { font-size: x-large; color:#8B008B; margin-top:30px; margin-bottom:30px; font-weight: bold; font-family: 'DFKai-sb', 'Tahoma'; }
47 |
48 | h3, h3 a { font-size: large; color:#000080; font-weight: bold; font-family: 'DFKai-sb', 'Tahoma'; }
49 |
50 | h4, h4 a { font-size: medium; color:#4B0082; font-weight: bold; font-family: 'DFKai-sb', 'Tahoma'; }
51 |
52 | h5, h5 a { font-size: small ; color:#708090; font-weight: bold; font-family: 'DFKai-sb', 'Tahoma'; }
53 |
54 | h6, h6 a { font-size: x-small; color:#000080; }
55 |
56 | p {
57 | font-family: 'Pmingliu', 'Tahoma';
58 | margin: 10px 0 15px 0;
59 | font-size:100%;
60 | color:#353535;
61 | }
62 |
63 | li {
64 | font-size:100%;
65 | }
66 |
67 | footer p {
68 | color: #f2f2f2;
69 | }
70 |
71 | a {
72 | text-decoration: none;
73 | color: #007edf;
74 | text-shadow: none;
75 |
76 | transition: color 0.5s ease;
77 | transition: text-shadow 0.5s ease;
78 | -webkit-transition: color 0.5s ease;
79 | -webkit-transition: text-shadow 0.5s ease;
80 | -moz-transition: color 0.5s ease;
81 | -moz-transition: text-shadow 0.5s ease;
82 | -o-transition: color 0.5s ease;
83 | -o-transition: text-shadow 0.5s ease;
84 | -ms-transition: color 0.5s ease;
85 | -ms-transition: text-shadow 0.5s ease;
86 | }
87 |
88 | table {
89 | border-collapse: collapse;
90 | border-spacing: 0;
91 | border: 1px solid #373737;
92 | margin-bottom: 20px;
93 | text-align: left;
94 | margin-left:auto;
95 | margin-right:auto;
96 | }
97 |
98 | th {
99 | padding: 10px;
100 | background-color:black;
101 | color:white;
102 | }
103 |
104 | td {
105 | padding: 10px;
106 | vertical-align: middle;
107 | border: 1px solid #373737;
108 | }
109 |
110 | em {
111 | color: blue;
112 | }
113 |
114 | strong {
115 | font-weight:bold;
116 | color: red;
117 | }
118 |
119 | #header_wrap {
120 | margin: 0;
121 | padding: 16px;
122 | border: 0;
123 | font: inherit;
124 | vertical-align: baseline;
125 | background-color:black;
126 | color:white;
127 | }
128 |
129 | #header_wrap h1, #header_wrap h1 sub, #header_wrap h1 a {
130 | color:white;
131 | font-family: 'DFKai-sb', 'Tahoma';
132 | }
133 |
134 | #header_wrap sub {
135 | color:white;
136 | font-size:60%;
137 | }
138 |
139 | .title, .author, .date {
140 | color:#333333;
141 | text-align:center;
142 | font-family: 'DFKai-sb', 'Tahoma' ;
143 | }
144 |
145 | .title { font-size:xx-large; line-height:800%; }
146 |
147 | .author { font-size:large; line-height:300%; }
148 |
149 | .date { font-size:large; line-height:300%; }
150 |
151 |
152 | #content {
153 | margin:10px;
154 | padding:10px;
155 | }
156 |
157 | pre {
158 | border: 1px solid #373737;
159 | background-color:#efefef;
160 | color:#3f3f3f;
161 | font-size:medium;
162 | width:95%;
163 | padding:10px;
164 | /* wrap ,自動換行 */
165 | white-space: pre-wrap; /* css-3 */
166 | white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
167 | white-space: -pre-wrap; /* Opera 4-6 */
168 | white-space: -o-pre-wrap; /* Opera 7 */
169 | word-wrap: break-word; /* Internet Explorer 5.5+ */
170 | }
171 |
172 | code {
173 | // font-family: SimSun;
174 | // color:blue;
175 | font-size:100%;
176 | }
177 |
178 | pre code {
179 | font-family: SimSun;
180 | font-size:125%;
181 | }
182 |
183 | .figure {
184 | margin:10px;
185 | padding:10px;
186 | margin-left: auto;
187 | margin-right: auto;
188 | display: block;
189 | }
190 |
191 | img { vertical-align:middle; }
192 |
193 | .figure img {
194 | margin-left: auto;
195 | margin-right: auto;
196 | display: block;
197 | }
198 |
199 | .figure .caption {
200 | text-align:center;
201 | }
202 |
203 | #TOC {
204 | }
205 |
206 | #footer {
207 | text-align:center;
208 | font-size:small;
209 | color:#6f6f6f;
210 | margin: 10px;
211 | padding: 10px;
212 | }
213 |
214 | /* JavaScript Style */
215 | table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode {
216 | margin: 0; padding: 0; vertical-align: baseline; border: none; }
217 | table.sourceCode { width: 100%; }
218 | td.lineNumbers { text-align: right; padding-right: 4px; padding-left: 4px; color: #afafaf; border-right: 1px solid #aaaaaa; }
219 | td.sourceCode { padding-left: 5px; }
220 | code > span.kw { color: #007020; font-weight: bold; }
221 | code > span.dt { color: #902000; }
222 | code > span.dv { color: #40a070; }
223 | code > span.bn { color: #40a070; }
224 | code > span.fl { color: #40a070; }
225 | code > span.ch { color: #4070a0; }
226 | code > span.st { color: #4070a0; }
227 | code > span.co { color: #60a0b0; font-style: italic; }
228 | code > span.ot { color: #007020; }
229 | code > span.al { color: #ff0000; font-weight: bold; }
230 | code > span.fu { color: #06287e; }
231 | code > span.er { color: #ff0000; font-weight: bold; }
232 |
233 |
--------------------------------------------------------------------------------
/code/js/bak/vm0.js.bk1:
--------------------------------------------------------------------------------
1 | var c = require("./ccc");
2 | var cpu1 = require("./cpu0");
3 | var Memory = require("./memory");
4 |
5 | var IR = 16, PC = 15, LR = 14, SP = 13, SW = 12;
6 | var ID = function(op) { return cpu1.opTable[op].id; }
7 |
8 | var run = function(objFile) {
9 | R = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 13, -1, 0, 16];
10 | m = new Memory(1);
11 | m.load(objFile);
12 | m.dump();
13 | var stop = false;
14 | while (!stop) { // 如果尚未結束
15 | var tpc = R[PC];
16 | R[0] = 0; // R[0] 永遠為 0
17 | R[IR] = m.geti(R[PC]); // 指令擷取,IR=[PC..PC+3]
18 | R[PC] += 4; // 擷取完將 PC 加 4,指向下一個指令
19 | var op = c.bits(R[IR], 24, 31); // 取得 op 欄位,IR[24..31]
20 | var ra = c.bits(R[IR], 20, 23); // 取得 ra 欄位,IR[20..23]
21 | var rb = c.bits(R[IR], 16, 19); // 取得 rb 欄位,IR[16..19]
22 | var rc = c.bits(R[IR], 12, 15); // 取得 rc 欄位,IR[12..15]
23 | var c24= c.signbits(R[IR], 0, 23); // 取得 24 位元的 cx
24 | var c16= c.signbits(R[IR], 0, 15); // 取得 16 位元的 cx
25 | var c5 = c.bits(R[IR], 0, 4); // 取得 16 位元的 cx
26 | var addr = R[rb]+c16;
27 | var raddr = R[rb]+R[rc]; // 取得位址[Rb+Rc]
28 | var N = c.bits(R[SW], 31, 31);
29 | var Z = c.bits(R[SW], 30, 30);
30 | // c.log("IR=%s ra=%d rb=%d rc=%d c24=%s c16=%s addr=%s", c.hex(R[IR], 8), ra, rb, rc, c.hex(c24, 6), c.hex(c16, 4), c.hex(addr, 8))
31 | switch (op) { // 根據op執行動作
32 | case ID("LD") : R[ra] = m.geti(addr); break; // 處理 LD 指令
33 | case ID("ST") : // 處理 ST 指令
34 | m.seti(addr, R[ra]);
35 | c.log("m[%s]=%s", c.hex(addr,4), m.geti(addr));
36 | break;
37 | case ID("LDB"): R[ra] = m.getb(addr); break; // 處理 LDB 指令
38 | case ID("STB"): m.setb(addr, R[ra]); break; // 處理 STB 指令
39 | case ID("LDR"): R[ra] = m.geti(raddr); break; // 處理 LDR 指令
40 | case ID("STR"): m.seti(raddr, R[ra]); break; // 處理 STR 指令
41 | case ID("LBR"): R[ra] = m.getb(raddr); break; // 處理 LBR 指令
42 | case ID("SBR"): m.setb(raddr, R[ra]); break; // 處理 SBR 指令
43 | case ID("LDI"): R[ra] = c16; break; // 處理 LDI 指令
44 | case ID("CMP"): { // 處理 CMP指令,根據比較結果,設定 N,Z 旗標
45 | if (R[ra] > R[rb]) { // > : SW(N=0, Z=0)
46 | R[SW] &= 0x3FFFFFFF; // N=0, Z=0
47 | } else if (R[ra] < R[rb]) { // < : SW(N=1, Z=0, ....)
48 | R[SW] |= 0x80000000; // N=1;
49 | R[SW] &= 0xBFFFFFFF; // Z=0;
50 | } else { // = : SW(N=0, Z=1)
51 | R[SW] &= 0x7FFFFFFF; // N=0;
52 | R[SW] |= 0x40000000; // Z=1;
53 | }
54 | ra = 12;
55 | break;
56 | }
57 | case ID("MOV"): R[ra] = R[rb]; break; // 處理MOV指令
58 | case ID("ADD"): R[ra] = R[rb]+R[rc]; break; // 處理ADD指令
59 | case ID("SUB"): R[ra] = R[rb]-R[rc]; break; // 處理SUB指令
60 | case ID("MUL"): R[ra] = R[rb]*R[rc]; break; // 處理MUL指令
61 | case ID("DIV"): R[ra] = R[rb]/R[rc]; break; // 處理DIV指令
62 | case ID("AND"): R[ra] = R[rb]&R[rc]; break; // 處理AND指令
63 | case ID("OR") : R[ra] = R[rb]|R[rc]; break; // 處理OR指令
64 | case ID("XOR"): R[ra] = R[rb]^R[rc]; break; // 處理XOR指令
65 | case ID("SHL"): R[ra] = R[rb]<>c5; break; // 處理SHR指令
67 | case ID("ADDI"):R[ra] = R[rb] + c16; break; // 處理 ADDI 指令
68 | case ID("JEQ"): if (Z==1) R[PC] += c24; break; // 處理JEQ指令 Z=1
69 | case ID("JNE"): if (Z==0) R[PC] += c24; break; // 處理JNE指令 Z=0
70 | case ID("JLT"): if (N==1&&Z==0) R[PC] += c24; break; // 處理JLT指令 NZ=10
71 | case ID("JGT"): if (N==0&&Z==0) R[PC] += c24; break; // 處理JGT指令 NZ=00
72 | case ID("JLE"): if ((N==1&&Z==0)||(N==0&&Z==1)) R[PC]+=c24; break; // 處理JLE指令 NZ=10 or 01
73 | case ID("JGE"): if ((N==0&&Z==0)||(N==0&&Z==1)) R[PC]+=c24; break; // 處理JGE指令 NZ=00 or 01
74 | case ID("JMP"): R[PC]+=c24; break; // 處理JMP指令
75 | case ID("SWI"): /* R[LR] = R[PC]; R[PC]=c24; */ break; // 處理SWI指令
76 | case ID("JSUB"):R[LR] = R[PC]; R[PC]+=c24; break; // 處理JSUB指令
77 | case ID("RET"): if (R[LR]<0) stop=true; else R[PC]=LR; break; // 處理RET指令
78 | case ID("PUSH"):R[SP]-=4; R[ra]=m.geti(addr); m.seti(R[SP], R[ra]); break; // 處理PUSH指令
79 | case ID("POP"): R[ra] = m.geti(R[SP]); R[SP]+=4; break; // 處理POP指令
80 | case ID("PUSHB"):R[SP]--; R[ra]=m.getb(addr); m.setb(R[SP], R[ra]); break; // 處理PUSH指令
81 | case ID("POPB"):R[ra] = m.getb(R[SP]); R[SP]++; break; // 處理POPB指令
82 | default: c.log("Error:invalid op (%s)", c.hex(op, 2));
83 | } // switch
84 | c.log("PC=%s IR=%s SW=%s R[%s]=0x%s=%d", // 印出 PC, IR, R[ra]暫存器的值,以利觀察
85 | c.hex(tpc,4), c.hex(R[IR],8), c.hex(R[SW],8), c.hex(ra,2), c.hex(R[ra], 8), R[ra]);
86 | } // while
87 | }
88 |
89 | run(process.argv[2]);
90 |
--------------------------------------------------------------------------------
/code/js/bak/as1_bak.js:
--------------------------------------------------------------------------------
1 | var fs = require("fs");
2 | var util = require("util");
3 | var c = require("./ccc");
4 | var opTable = require("./cpu1").opTable;
5 | var Memory = require("./memory");
6 |
7 | var parseR = function(str) {
8 | var rmatch = /R(\d+)/.exec(str);
9 | if (rmatch == null)
10 | return NaN;
11 | return parseInt(rmatch[1]);
12 | }
13 |
14 | var Assembler = function() {
15 |
16 | this.symTable = {};
17 |
18 | this.assemble = function(asmFile, objFile) { // 組譯器的主要函數
19 | c.log("\nAssembler:asmFile=%s objFile=%s\n", asmFile, objFile); // 輸入組合語言、輸出目的檔
20 | c.log("===============Assemble=============\n");
21 |
22 | var text = fs.readFileSync(asmFile, "utf8"); // 讀取檔案到 text 字串中
23 | this.lines = text.split(/[\r\n]+/); // 將組合語言分割成一行一行
24 | c.log(this.lines);
25 |
26 | this.pass1(this.lines); // 第一階段:計算位址
27 | c.log("===============SYMBOL TABLE=========\n"); // 印出符號表
28 | for (s in this.symTable) {
29 | c.log("%s %s\n", c.fill(' ',s,8), c.hex(this.symTable[s].address, 4));
30 | }
31 | this.pass2(this.codes); // 第二階段:建構目的碼
32 | this.saveObjFile(objFile); // 輸出目的檔
33 | }
34 |
35 | this.pass1 = function(lines) { // 第一階段的組譯
36 | var address = 0;
37 | c.log("\n=================PASS1================\n");
38 | this.codes = [];
39 | for (i in lines) { // 對於每一行
40 | if (lines[i].length == 0) continue;
41 | var code = new Code(lines[i]); // 剖析並建立 code 物件
42 | code.address = address; // 設定該行的位址
43 | if (code.label.length != "") // 如果有標記符號
44 | this.symTable[code.label] = code; // 加入符號表中
45 | this.codes.push(code);
46 | code.print();
47 | address += code.size(); // 計算下一個指令位址
48 | }
49 | }
50 |
51 | this.pass2 = function() { // 組譯器的第二階段
52 | c.log("=============PASS2==============\n");
53 | for (i in this.codes) { // 對每一個指令
54 | var code = this.codes[i];
55 | this.translate(code); // 進行編碼動作
56 | code.print();
57 | }
58 | }
59 |
60 | this.translate = function(code) { // 指令的編碼函數
61 | var ra=0, rb=0, rc=0, cx=0;
62 | var pc = code.address + 4; // 提取後PC為位址+4
63 | var args = code.args;
64 | var labelCode = null;
65 | var symTable = this.symTable;
66 | if (code.op.type == "N") {
67 | code.obj = c.hex(code.op.id, 2) + c.hex(0, 6);
68 | } else if (code.op.type == "C") {
69 | ra = parseR(args[0]);
70 | rb = parseR(args[1]);
71 | cx = parseInt(args[2]);
72 | code.obj = c.hex(code.op.id, 2)+c.hex(ra, 1)+c.hex(rb, 1)+c.hex(cx, 4);
73 | } else if (code.op.type == "L") {
74 | ra = parseR(args[0]);
75 | rb = 15;
76 | labelCode = symTable[args[1]];
77 | cx = labelCode.address - pc;
78 | code.obj = c.hex(code.op.id, 2)+c.hex(ra, 1)+c.hex(rb, 1)+c.hex(cx, 4);
79 | } else if (code.op.type == "D") {
80 | var unitSize = 1;
81 | switch (code.op.name) {
82 | case "RESW": // 如果是 RESW
83 | case "RESB": // 或 RESB
84 | code.obj = c.dup('0', code.size()*2);
85 | break;
86 | case "WORD": // 如果是 WORD:
87 | unitSize = 4;
88 | case "BYTE": { // 如果是 BYTE : 輸出格式為 %2x
89 | code.obj = "";
90 | for (i in args) {
91 | if (args[i].match(/\d+/)) // 常數
92 | code.obj += c.hex(parseInt(args[i]), unitSize*2);
93 | else { // 標記
94 | labelCode = symTable[args[i]];
95 | code.obj += c.hex(labelCode.address, unitSize*2);
96 | }
97 | }
98 | break;
99 | } // case BYTE:
100 | } // switch
101 | } // if
102 | }
103 |
104 | this.saveObjFile = function(objFile) {
105 | c.log("\n=================SAVE OBJ FILE================\n");
106 | var obj = "";
107 | for (i in this.codes) {
108 | obj += this.codes[i].obj;
109 | }
110 | c.log("%s\n", obj);
111 |
112 | var m = new Memory(1);
113 | m.loadhex(obj);
114 | m.save(objFile);
115 | }
116 | }
117 |
118 | var Code = function(line) {
119 | this.print = function() {
120 | c.log(this.toString());
121 | }
122 |
123 | this.toString = function() {
124 | return util.format("%s %s %s %s %s %s", c.hex(this.address,-4),
125 | c.fill(' ',this.label,8), c.fill(' ',this.op.name, 8),
126 | c.fill(' ',this.args, 16), c.hex(this.op.id,2), this.obj);
127 | }
128 |
129 | this.size = function() {
130 | switch (this.op.name) { // 根據運算碼 op
131 | case "RESW" : return 4 * parseInt(this.args[0]); // 如果是 RESW, 大小為 4*保留量(參數 0)
132 | case "RESB" : return 1 * parseInt(this.args[0]); // 如果是 RESB, 大小為 1*保留量(參數 0)
133 | case "WORD" : return 4 * this.args.length; // 如果是WORD, 大小是 4*參數個數
134 | case "BYTE" : return 1 * this.args.length; // 如果是BYTE, 大小是 1*參數個數
135 | case "" : return 0; // 如果只是標記, 大小為 0
136 | default : return 4; // 其他情形 (指令), 大小為 4
137 | }
138 | }
139 |
140 | var labCmd = /^((\w+):)?\s*([^;]*)/;
141 | var parts = labCmd.exec(line); // 分割出標記與命令
142 | this.label = c.nonull(parts[2]); // 取出標記 (\w+)
143 | var tokens = parts[3].split(/[ ,\t\r]+/); // 將命令分割成基本單元
144 | var opName = tokens[0]; // 取出指令名稱
145 | this.args = tokens.slice(1); // 取出參數部份
146 | this.op = opTable[opName];
147 | this.obj = "";
148 | }
149 |
150 | var argv = process.argv;
151 | var a = new Assembler();
152 | a.assemble(argv[2], argv[3]);
153 |
154 | module.exports = Assembler;
--------------------------------------------------------------------------------
/htm/article4.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
28 |
29 |
30 |
31 |
34 |
35 |
40 |
41 |
什麼是奇數魔術方陣 (?)
42 |
43 | 魔術方陣是許多人想要解決的一個古老的數學問題,您可能在一些雜誌上看過,也可能您的老師有介紹過。一個魔術方陣是在於安排數字在一矩陣[n x n]中,從1到n2, 每一數字僅出現一次, 而且,任一列、任一行或任一對角線的總和都相同。求總和的公式要證明為n [ ( n2 + 1) / 2 ],並不是很困難,若我們利用這個公式,對[5x5]矩陣而言,其總和為5 [ ( 52 + 1 ) / 2 ] = 65,其對應的魔術方陣輸出如下:
44 |
45 |
48 |
51 |
'# [Visual Basic 6.0] 奇數魔術方陣(Odd Magic Square)
52 | '# 0xDe
53 | Dim InputN
54 | Dim Squate()
55 | Private Sub Form_Activate()
56 | '------------------------------
57 | InputN = 3 ' 輸入 (必須為奇數)
58 | '------------------------------
59 |
60 |
61 | '------------------------------
62 | If InputN Mod 2 = 0 Then Exit Sub ' 判斷是否為奇數
63 | '------------------------------
64 | ReDim Square(InputN - 1, InputN - 1)
65 | '------------------------------
66 | Print "N= " & InputN & "的奇數魔術方陣" & vbCrLf
67 | Randomize Timer ' 亂數產生
68 | TempX = Int(Rnd * InputN) ' 隨機起始 X
69 | TempY = Int(Rnd * InputN) ' 隨機起始 Y
70 | '------------------------------
71 | Do Until N = (InputN ^ 2) ' 直到放滿
72 | If Square(TempX, TempY) = "" Then
73 | N = N + 1
74 | Square(TempX, TempY) = N
75 |
76 | TempX = TempX - 1 ' 向上移
77 | If TempX < 0 Then TempX = InputN - 1
78 | TempY = TempY + 1 ' 向右移
79 | If TempY > InputN - 1 Then TempY = 0
80 | Else
81 | ' 恢復原本的狀態往下
82 | TempX = TempX + 1
83 | If TempX > InputN - 1 Then TempX = 0
84 | TempY = TempY - 1
85 | If TempY < 0 Then TempY = InputN - 1
86 | ' 往下
87 | TempX = TempX + 1
88 | If TempX > InputN - 1 Then TempX = 0
89 | End If
90 | Loop
91 | '------------------------------
92 | For I = 0 To InputN - 1 ' 將結果輸出
93 | For J = 0 To InputN - 1
94 | Print Square(I, J);
95 | Next J
96 | Print
97 | Next I
98 | '------------------------------
99 | End Sub
100 |
103 |
【本文作者為「廖憲得」,原文網址為: http://www.dotblogs.com.tw/0xde/archive/2013/11/13/129187.aspx ,由陳鍾誠編輯後納入本雜誌】
104 |
105 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/code/js/vm0.js:
--------------------------------------------------------------------------------
1 | var c = require("./ccc");
2 | var cpu1 = require("./cpu0");
3 | var Memory = require("./memory");
4 |
5 | var isDump = process.argv[3] == "-d";
6 |
7 | var IR = 16, PC = 15, LR = 14, SP = 13, SW = 12;
8 | var ID = function(op) { return cpu1.opTable[op].id; }
9 |
10 | var run = function(objFile) {
11 | R = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 13, -1, 0, 16];
12 | m = new Memory(1);
13 | m.load(objFile);
14 | if (isDump) m.dump();
15 | var stop = false;
16 | while (!stop) { // 如果尚未結束
17 | var tpc = R[PC];
18 | R[0] = 0; // R[0] 永遠為 0
19 | R[IR] = m.geti(R[PC]); // 指令擷取,IR=[PC..PC+3]
20 | R[PC] += 4; // 擷取完將 PC 加 4,指向下一個指令
21 | var op = c.bits(R[IR], 24, 31); // 取得 op 欄位,IR[24..31]
22 | var ra = c.bits(R[IR], 20, 23); // 取得 ra 欄位,IR[20..23]
23 | var rb = c.bits(R[IR], 16, 19); // 取得 rb 欄位,IR[16..19]
24 | var rc = c.bits(R[IR], 12, 15); // 取得 rc 欄位,IR[12..15]
25 | var c24= c.signbits(R[IR], 0, 23); // 取得 24 位元的 cx
26 | var c16= c.signbits(R[IR], 0, 15); // 取得 16 位元的 cx
27 | var c5 = c.bits(R[IR], 0, 4); // 取得 16 位元的 cx
28 | var addr = R[rb]+c16;
29 | var raddr = R[rb]+R[rc]; // 取得位址[Rb+Rc]
30 | var N = c.bits(R[SW], 31, 31);
31 | var Z = c.bits(R[SW], 30, 30);
32 | // c.log("IR=%s ra=%d rb=%d rc=%d c24=%s c16=%s addr=%s", c.hex(R[IR], 8), ra, rb, rc, c.hex(c24, 6), c.hex(c16, 4), c.hex(addr, 8))
33 | switch (op) { // 根據op執行動作
34 | case ID("LD") : R[ra] = m.geti(addr); break; // 處理 LD 指令
35 | case ID("ST") : // 處理 ST 指令
36 | m.seti(addr, R[ra]);
37 | if (isDump) c.log("m[%s]=%s", c.hex(addr,4), m.geti(addr));
38 | break;
39 | case ID("LDB"): R[ra] = m.getb(addr); break; // 處理 LDB 指令
40 | case ID("STB"): m.setb(addr, R[ra]); break; // 處理 STB 指令
41 | case ID("LDR"): R[ra] = m.geti(raddr); break; // 處理 LDR 指令
42 | case ID("STR"): m.seti(raddr, R[ra]); break; // 處理 STR 指令
43 | case ID("LBR"): R[ra] = m.getb(raddr); break; // 處理 LBR 指令
44 | case ID("SBR"): m.setb(raddr, R[ra]); break; // 處理 SBR 指令
45 | case ID("LDI"): R[ra] = c16; break; // 處理 LDI 指令
46 | case ID("CMP"): { // 處理 CMP指令,根據比較結果,設定 N,Z 旗標
47 | if (R[ra] > R[rb]) { // > : SW(N=0, Z=0)
48 | R[SW] &= 0x3FFFFFFF; // N=0, Z=0
49 | } else if (R[ra] < R[rb]) { // < : SW(N=1, Z=0, ....)
50 | R[SW] |= 0x80000000; // N=1;
51 | R[SW] &= 0xBFFFFFFF; // Z=0;
52 | } else { // = : SW(N=0, Z=1)
53 | R[SW] &= 0x7FFFFFFF; // N=0;
54 | R[SW] |= 0x40000000; // Z=1;
55 | }
56 | ra = 12;
57 | break;
58 | }
59 | case ID("MOV"): R[ra] = R[rb]; break; // 處理MOV指令
60 | case ID("ADD"): R[ra] = R[rb]+R[rc]; break; // 處理ADD指令
61 | case ID("SUB"): R[ra] = R[rb]-R[rc]; break; // 處理SUB指令
62 | case ID("MUL"): R[ra] = R[rb]*R[rc]; break; // 處理MUL指令
63 | case ID("DIV"): R[ra] = R[rb]/R[rc]; break; // 處理DIV指令
64 | case ID("AND"): R[ra] = R[rb]&R[rc]; break; // 處理AND指令
65 | case ID("OR") : R[ra] = R[rb]|R[rc]; break; // 處理OR指令
66 | case ID("XOR"): R[ra] = R[rb]^R[rc]; break; // 處理XOR指令
67 | case ID("SHL"): R[ra] = R[rb]<>c5; break; // 處理SHR指令
69 | case ID("ADDI"):R[ra] = R[rb] + c16; break; // 處理 ADDI 指令
70 | case ID("JEQ"): if (Z==1) R[PC] += c24; break; // 處理JEQ指令 Z=1
71 | case ID("JNE"): if (Z==0) R[PC] += c24; break; // 處理JNE指令 Z=0
72 | case ID("JLT"): if (N==1&&Z==0) R[PC] += c24; break; // 處理JLT指令 NZ=10
73 | case ID("JGT"): if (N==0&&Z==0) R[PC] += c24; break; // 處理JGT指令 NZ=00
74 | case ID("JLE"): if ((N==1&&Z==0)||(N==0&&Z==1)) R[PC]+=c24; break; // 處理JLE指令 NZ=10 or 01
75 | case ID("JGE"): if ((N==0&&Z==0)||(N==0&&Z==1)) R[PC]+=c24; break; // 處理JGE指令 NZ=00 or 01
76 | case ID("JMP"): R[PC]+=c24; break; // 處理JMP指令
77 | case ID("SWI"): // 處理SWI指令
78 | switch (c24) {
79 | case 3: c.printf("%s", m.getstr(R[9])); break;
80 | case 4: c.printf("%d", R[9]); break;
81 | default:
82 | var emsg = c.format("SWI cx=%d not found!", c24);
83 | c.error(emsg, null);
84 | break;
85 | }
86 | break;
87 | case ID("JSUB"):R[LR] = R[PC]; R[PC]+=c24; break; // 處理JSUB指令
88 | case ID("RET"): if (R[LR]<0) stop=true; else R[PC]=LR; break; // 處理RET指令
89 | case ID("PUSH"):R[SP]-=4; R[ra]=m.geti(addr); m.seti(R[SP], R[ra]); break; // 處理PUSH指令
90 | case ID("POP"): R[ra] = m.geti(R[SP]); R[SP]+=4; break; // 處理POP指令
91 | case ID("PUSHB"):R[SP]--; R[ra]=m.getb(addr); m.setb(R[SP], R[ra]); break; // 處理PUSH指令
92 | case ID("POPB"):R[ra] = m.getb(R[SP]); R[SP]++; break; // 處理POPB指令
93 | default: c.error("OP not found!", null);
94 | } // switch
95 | if (isDump)
96 | c.log("PC=%s IR=%s SW=%s R[%s]=0x%s=%d", // 印出 PC, IR, R[ra]暫存器的值,以利觀察
97 | c.hex(tpc,4), c.hex(R[IR],8), c.hex(R[SW],8), c.hex(ra,2), c.hex(R[ra], 8), R[ra]);
98 | } // while
99 | }
100 |
101 | run(process.argv[2]);
102 |
--------------------------------------------------------------------------------
/htm/focus2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
28 |
29 |
30 |
31 |
34 |
35 |
40 |
41 |
42 |
以下是「爬山演算法」 (Hill-Climbing Algorithm) 的一個簡易版本,其方法超簡單,就是一直看旁邊有沒有更好的解,如果有就移過去。然後反覆的作這樣的動作,直到旁邊的解都比現在的更差時,程式就停止,然後將那個位於山頂的解傳回,就完成了。
43 |
Algorithm HillClimbing(f, x)
44 | x = 隨意設定一個解。
45 | while (x 有鄰居 x' 比 x 更高)
46 | x = x';
47 | end
48 | return x;
49 | end
50 |
當然、這種演算法只能找到「局部最佳解」(local optimal),當整個空間有很多山頂的時候,這種方法會爬到其中一個山頂就停了,並不一定會爬到最高的山頂。
51 |
52 |
檔案: HillClimbingSimple.js
53 |
var util = require ("util" );
54 | var log = console .log ;
55 |
56 | function f (x) { return -1 *(x*x+3 *x+5 ); }
57 | // function f(x) { return -1*Math.abs(x*x-4); }
58 |
59 | var dx = 0.01 ;
60 |
61 | function hillClimbing (f, x) {
62 | while (true ) {
63 | log ("f(%s)=%s" , x .toFixed (4 ), f (x).toFixed (4 ));
64 | if (f (x+dx) >= f (x))
65 | x = x+dx;
66 | else if (f (x-dx) >= f (x))
67 | x = x-dx;
68 | else
69 | break ;
70 | }
71 | }
72 |
73 | hillClimbing (f, 0.0 );
74 |
75 |
求解 : 的最高點,也就是 的最低點。
76 |
D:\Dropbox\Public\web\ai\code\optimize>node hillClimbingSimple
77 | f(0.0000)=-5.0000
78 | f(-0.0100)=-4.9701
79 | f(-0.0200)=-4.9404
80 | f(-0.0300)=-4.9109
81 | f(-0.0400)=-4.8816
82 | f(-0.0500)=-4.8525
83 | ...
84 | f(-1.4500)=-2.7525
85 | f(-1.4600)=-2.7516
86 | f(-1.4700)=-2.7509
87 | f(-1.4800)=-2.7504
88 | f(-1.4900)=-2.7501
89 | f(-1.5000)=-2.7500
90 |
如果我們將上述程式的 f(x) 換成註解中的那個,也就是將 f(x) 換成如下版本:
91 |
function f(x) { return -1*Math.abs(x*x-4); }
92 |
那麼就可以用來求解 的最低點,也就是尋找 4 的平方根,以下是執行結果:
93 |
D:\Dropbox\Public\web\ai\code\optimize>node hillClimbingSimple
94 | f(0.0000)=-4.0000
95 | f(0.0100)=-3.9999
96 | f(0.0200)=-3.9996
97 | f(0.0300)=-3.9991
98 | f(0.0400)=-3.9984
99 | f(0.0500)=-3.9975
100 | ...
101 | f(1.9500)=-0.1975
102 | f(1.9600)=-0.1584
103 | f(1.9700)=-0.1191
104 | f(1.9800)=-0.0796
105 | f(1.9900)=-0.0399
106 | f(2.0000)=-0.0000
107 |
您可以看到上述程式正確的找到 4 的平方根是 2,而我們所用的方法與求解 的最高點幾乎是一模一樣的,只是把函數換掉而已。
108 |
109 |
您可以看到上述用爬山演算法尋找函數最高點或最低點的程式,其實非常的簡單,只不過是看看兩邊是否有更好的解,如果有就移過去罷了。
110 |
但是、這麼簡單的演算法,其實威力是非常強大的,這種方法可以求解的問題非常的多,很多人工智慧上非常重要的問題,其實都只不過是在進行函數優化的動作,也就是尋找某個函數的低點或高點而已,這些問題其實大部分都可以使用爬山演算法來求解。
111 |
當然、要能尋找更複雜函數的「區域最佳解」,還必須進一步的對上述程式進行封裝與抽象化,我們將在下一篇文章中解說將上述爬山程式抽象化後的版本,並用該程式來求更複雜函數的解。
112 |
113 |
117 |
【本文由陳鍾誠取材並修改自 維基百科 ,採用創作共用的 姓名標示、相同方式分享 授權】
118 |
119 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/source/focus4.md0:
--------------------------------------------------------------------------------
1 | ## 通用的「模擬退火法」架構 - 使用 JavaScript+Node.js 實作
2 |
3 | ### 前言
4 |
5 | 在上一篇文章中,我們介紹了一個通用的爬山演算法架構,而模擬退火法其實是爬山演算法的一個改良版,其設計理念是參考打鐵時金屬從高溫逐漸緩慢降溫,可以讓結構更緊緻的這種概念,在「流水下山演算法」上加入了溫度的概念。
6 |
7 | 當溫度很高的時候,模擬退火法基本上就像隨機亂走一樣,但是當溫度逐漸下降之後,模擬退火法就會逐漸凝固,只能朝著較好的解前進,向著較差解前進的機率會逐漸縮小。
8 |
9 | 當溫度幾乎降到零的時候,模擬退火法基本上就會退化成爬山演算法,於是最後還是會固定在某個「區域最佳解」上面。但是由於經過從高溫緩慢降溫的過程,所以模擬退火法比較有機會在高溫時跳出區域最佳解,然後找到更好的解,甚至是全域最佳解之後才凝固,這就是「模擬退火法」的設計原理了。
10 |
11 | 以下是模擬退火法的演算法的簡要寫法:
12 |
13 | ```
14 | Algorithm SimulatedAnnealing(s)
15 | while (溫度還不夠低,或還可以找到比 s 更好的解 s' 的時候)
16 | 根據能量差與溫度,用機率的方式決定是否要移動到新解 s'。
17 | 將溫度降低一些
18 | end
19 | end
20 | ```
21 |
22 | 在上述演算法中,所謂的機率的方式,是採用 $\exp(\frac{e-e'}{T})$ 這個機率公式,去判斷是否要從 s 移動到 s',其中 e 是 s 的能量值,而 e' 是 s' 的能量值。
23 |
24 | 接著,就讓我們來實作一個通用的模擬退火法架構吧 (與前述爬山演算法共用的「解答表示」solution 部份,我們就不再重複貼出了)
25 |
26 | ### 通用的模擬退火法架構
27 |
28 | 檔案:simulatedAnnealing.js
29 |
30 | ```javascript
31 | var simulatedAnnealing = function() {} // 模擬退火法的物件模版 (類別)
32 |
33 | simulatedAnnealing.prototype.P = function(e, enew, T) { // 模擬退火法的機率函數
34 | if (enew < e)
35 | return 1;
36 | else
37 | return Math.exp((e-enew)/T);
38 | }
39 |
40 | simulatedAnnealing.prototype.run = function(s, maxGens) { // 模擬退火法的主要函數
41 | var sbest = s; // sbest:到目前為止的最佳解
42 | var ebest = s.energy(); // ebest:到目前為止的最低能量
43 | var T = 100; // 從 100 度開始降溫
44 | for (var gens=0; gens Math.random()) { // 根據溫度與能量差擲骰子,若通過
50 | s = snew; // 則移動到新的鄰居解
51 | console.log("%d T=%s %s", gens, T.toFixed(3), s.toString()); // 印出觀察
52 | }
53 | if (enew < ebest) { // 如果新解的能量比最佳解好,則更新最佳解。
54 | sbest = snew;
55 | ebest = enew;
56 | }
57 | }
58 | console.log("solution: %s", sbest.toString()); // 印出最佳解
59 | return sbest; // 傳回最佳解
60 | }
61 |
62 | module.exports = simulatedAnnealing; // 將模擬退火演算法的類別匯出。
63 | ```
64 |
65 | ### 實例:求解平方根
66 |
67 | 在此,我們將以求解 4 的平方根為例,測試一下上述演算法是否能夠找到正確的解答。
68 |
69 | 檔案:simulatedAnnealingNumber.js
70 |
71 | ```javascript
72 | var simulatedAnnealing = require("./simulatedAnnealing"); // 引入模擬退火法類別
73 | var solutionNumber = require("./solutionNumber"); // 引入平方根解答類別
74 |
75 | var sa = new simulatedAnnealing(); // 建立模擬退火法物件
76 | // 執行模擬退火法 (從「解答=0.0」開始尋找, 最多一萬代。
77 | sa.run(new solutionNumber(0.0), 10000);
78 | ```
79 |
80 | 執行結果:
81 |
82 | ```
83 | 0 T=99.900 energy(-0.010)=4.000
84 | 1 T=99.800 energy(0.000)=4.000
85 | ...
86 | 12 T=98.708 energy(-0.010)=4.000
87 | 13 T=98.609 energy(-0.020)=4.000
88 | 14 T=98.510 energy(-0.030)=3.999
89 | 15 T=98.412 energy(-0.020)=4.000
90 | 16 T=98.314 energy(-0.030)=3.999
91 | 17 T=98.215 energy(-0.040)=3.998
92 | 18 T=98.117 energy(-0.050)=3.998
93 | 19 T=98.019 energy(-0.040)=3.998
94 | ...
95 | 5072 T=0.625 energy(1.250)=2.437
96 | 5073 T=0.624 energy(1.240)=2.462
97 | 5074 T=0.624 energy(1.230)=2.487
98 | 5075 T=0.623 energy(1.240)=2.462
99 | 5076 T=0.622 energy(1.250)=2.437
100 | 5077 T=0.622 energy(1.260)=2.412
101 | 5078 T=0.621 energy(1.270)=2.387
102 | 5079 T=0.620 energy(1.280)=2.362
103 | ...
104 | 6615 T=0.133 energy(1.950)=0.197
105 | 6617 T=0.133 energy(1.940)=0.236
106 | 6618 T=0.133 energy(1.930)=0.275
107 | 6619 T=0.133 energy(1.920)=0.314
108 | 6620 T=0.133 energy(1.930)=0.275
109 | 6621 T=0.133 energy(1.940)=0.236
110 | 6622 T=0.133 energy(1.930)=0.275
111 | ...
112 | 9377 T=0.008 energy(1.990)=0.040
113 | 9378 T=0.008 energy(2.000)=0.000
114 | 9396 T=0.008 energy(2.010)=0.040
115 | 9397 T=0.008 energy(2.000)=0.000
116 | 9528 T=0.007 energy(2.010)=0.040
117 | 9531 T=0.007 energy(2.000)=0.000
118 | solution: energy(2.000)=0.000
119 | ```
120 |
121 | 您可以看到上述模擬退火法程式,在一開始的時候幾乎都在亂走,因此浪費了很多時間,但也正是因為這種特性,模擬退火法比較有機會跳脫那些小山谷,而有機會找到更深的山谷,這正式模擬退火法的特性。
122 |
123 | 雖然花的比較多的時間,但是模擬退火法最後還是正確的找到了 4 的平方根,傳回了 2.000 的結果。
124 |
125 | ### 實例:多變數函數的最佳化
126 |
127 | 在此,我們將以求解 $x^2+3y^2+z^2-4x-3y-5z+8$ 這個函數的最低點,看看上述演算法對多變數函數是否能正常運作。
128 |
129 | 檔案:simulatedAnnealingArray.js
130 |
131 | ```javascript
132 | var simulatedAnnealing = require("./simulatedAnnealing"); // 引入模擬退火法類別
133 | var solutionArray = require("./solutionArray"); // 引入多變數解答類別 (x^2+3y^2+z^2-4x-3y-5z+8)
134 |
135 | var sa = new simulatedAnnealing(); // 建立模擬退火法物件
136 | // 執行模擬退火法 (從「解答(x,y,z)=(1,1,1)」開始尋找, 最多執行 2 萬代。
137 | sa.run(new solutionArray([1,1,1]), 20000);
138 | ```
139 |
140 | 執行結果:
141 |
142 | ```
143 | 0 T=99.900 energy( 1.000 1.000 0.990 )=1.030
144 | 1 T=99.800 energy( 1.000 0.990 0.990 )=1.000
145 | 2 T=99.700 energy( 1.000 0.980 0.990 )=0.971
146 | 3 T=99.601 energy( 0.990 0.980 0.990 )=0.991
147 | 4 T=99.501 energy( 0.990 0.990 0.990 )=1.021
148 | 5 T=99.401 energy( 1.000 0.990 0.990 )=1.000
149 | 6 T=99.302 energy( 1.000 0.990 1.000 )=0.970
150 | ...
151 | 5985 T=0.251 energy( 0.870 1.260 1.770 )=0.543
152 | 5986 T=0.250 energy( 0.870 1.250 1.770 )=0.497
153 | 5987 T=0.250 energy( 0.870 1.250 1.760 )=0.512
154 | 5988 T=0.250 energy( 0.870 1.250 1.750 )=0.527
155 | 5989 T=0.250 energy( 0.870 1.250 1.760 )=0.512
156 | 5990 T=0.249 energy( 0.860 1.250 1.760 )=0.535
157 | ...
158 | 15036 T=0.000 energy( 2.000 0.500 2.510 )=-3.000
159 | 15038 T=0.000 energy( 2.000 0.500 2.500 )=-3.000
160 | 15173 T=0.000 energy( 2.010 0.500 2.500 )=-3.000
161 | 15174 T=0.000 energy( 2.000 0.500 2.500 )=-3.000
162 | 15261 T=0.000 energy( 2.000 0.500 2.490 )=-3.000
163 | 15265 T=0.000 energy( 2.000 0.500 2.500 )=-3.000
164 | solution: energy( 2.000 0.500 2.500 )=-3.000
165 | ```
166 |
167 | 您可以看到,上述的模擬退火法程式,總共花了一萬五千多代,終於找到了該多變數函數的谷底,雖然速度不快,但也總算是達成任務了。
168 |
169 | ### 結語
170 |
171 | 當然,模擬退火法雖然比較有機會跳脫小山谷,去找到更深的山谷,但這並不表示模擬退火法一定可以找到最深的山谷。
172 |
173 | 當溫度已經降到很低的時後,模擬退火法就會逐漸凝固,於是就會固定在某個山谷不出來了。
174 |
175 | 事實上、沒有任何一種優化方法可以在「多項式時間內」保證找到任何函數的最低點,否則「NP-Complete」問題不就被解掉了,而「NP-Complete」問題事實上在計算理論領域裡,一直還是個最困難的未解之謎啊!
176 |
177 | ### 參考文獻
178 | * [Wikipedia:Simulated annealing](http://en.wikipedia.org/wiki/Simulated_annealing)
179 | * [維基百科:模擬退火](http://zh.wikipedia.org/wiki/%E6%A8%A1%E6%8B%9F%E9%80%80%E7%81%AB)
180 |
181 | 【本文由陳鍾誠取材並修改自 [維基百科],採用創作共用的 [姓名標示、相同方式分享] 授權】
182 |
183 |
--------------------------------------------------------------------------------
/source/focus4.md:
--------------------------------------------------------------------------------
1 | ## 通用的「模擬退火法」架構 - 使用 JavaScript+Node.js 實作
2 |
3 | ### 前言
4 |
5 | 在上一篇文章中,我們介紹了一個通用的爬山演算法架構,而模擬退火法其實是爬山演算法的一個改良版,其設計理念是參考打鐵時金屬從高溫逐漸緩慢降溫,可以讓結構更緊緻的這種概念,在「流水下山演算法」上加入了溫度的概念。
6 |
7 | 當溫度很高的時候,模擬退火法基本上就像隨機亂走一樣,但是當溫度逐漸下降之後,模擬退火法就會逐漸凝固,只能朝著較好的解前進,向著較差解前進的機率會逐漸縮小。
8 |
9 | 當溫度幾乎降到零的時候,模擬退火法基本上就會退化成爬山演算法,於是最後還是會固定在某個「區域最佳解」上面。但是由於經過從高溫緩慢降溫的過程,所以模擬退火法比較有機會在高溫時跳出區域最佳解,然後找到更好的解,甚至是全域最佳解之後才凝固,這就是「模擬退火法」的設計原理了。
10 |
11 | 以下是模擬退火法的演算法的簡要寫法:
12 |
13 | ```
14 | Algorithm SimulatedAnnealing(s)
15 | while (溫度還不夠低,或還可以找到比 s 更好的解 s' 的時候)
16 | 根據能量差與溫度,用機率的方式決定是否要移動到新解 s'。
17 | 將溫度降低一些
18 | end
19 | end
20 | ```
21 |
22 | 在上述演算法中,所謂的機率的方式,是採用  這個機率公式,去判斷是否要從 s 移動到 s',其中 e 是 s 的能量值,而 e' 是 s' 的能量值。
23 |
24 | 接著,就讓我們來實作一個通用的模擬退火法架構吧 (與前述爬山演算法共用的「解答表示」solution 部份,我們就不再重複貼出了)
25 |
26 | ### 通用的模擬退火法架構
27 |
28 | 檔案:simulatedAnnealing.js
29 |
30 | ```javascript
31 | var simulatedAnnealing = function() {} // 模擬退火法的物件模版 (類別)
32 |
33 | simulatedAnnealing.prototype.P = function(e, enew, T) { // 模擬退火法的機率函數
34 | if (enew < e)
35 | return 1;
36 | else
37 | return Math.exp((e-enew)/T);
38 | }
39 |
40 | simulatedAnnealing.prototype.run = function(s, maxGens) { // 模擬退火法的主要函數
41 | var sbest = s; // sbest:到目前為止的最佳解
42 | var ebest = s.energy(); // ebest:到目前為止的最低能量
43 | var T = 100; // 從 100 度開始降溫
44 | for (var gens=0; gens Math.random()) { // 根據溫度與能量差擲骰子,若通過
50 | s = snew; // 則移動到新的鄰居解
51 | console.log("%d T=%s %s", gens, T.toFixed(3), s.toString()); // 印出觀察
52 | }
53 | if (enew < ebest) { // 如果新解的能量比最佳解好,則更新最佳解。
54 | sbest = snew;
55 | ebest = enew;
56 | }
57 | }
58 | console.log("solution: %s", sbest.toString()); // 印出最佳解
59 | return sbest; // 傳回最佳解
60 | }
61 |
62 | module.exports = simulatedAnnealing; // 將模擬退火演算法的類別匯出。
63 | ```
64 |
65 | ### 實例:求解平方根
66 |
67 | 在此,我們將以求解 4 的平方根為例,測試一下上述演算法是否能夠找到正確的解答。
68 |
69 | 檔案:simulatedAnnealingNumber.js
70 |
71 | ```javascript
72 | var simulatedAnnealing = require("./simulatedAnnealing"); // 引入模擬退火法類別
73 | var solutionNumber = require("./solutionNumber"); // 引入平方根解答類別
74 |
75 | var sa = new simulatedAnnealing(); // 建立模擬退火法物件
76 | // 執行模擬退火法 (從「解答=0.0」開始尋找, 最多一萬代。
77 | sa.run(new solutionNumber(0.0), 10000);
78 | ```
79 |
80 | 執行結果:
81 |
82 | ```
83 | 0 T=99.900 energy(-0.010)=4.000
84 | 1 T=99.800 energy(0.000)=4.000
85 | ...
86 | 12 T=98.708 energy(-0.010)=4.000
87 | 13 T=98.609 energy(-0.020)=4.000
88 | 14 T=98.510 energy(-0.030)=3.999
89 | 15 T=98.412 energy(-0.020)=4.000
90 | 16 T=98.314 energy(-0.030)=3.999
91 | 17 T=98.215 energy(-0.040)=3.998
92 | 18 T=98.117 energy(-0.050)=3.998
93 | 19 T=98.019 energy(-0.040)=3.998
94 | ...
95 | 5072 T=0.625 energy(1.250)=2.437
96 | 5073 T=0.624 energy(1.240)=2.462
97 | 5074 T=0.624 energy(1.230)=2.487
98 | 5075 T=0.623 energy(1.240)=2.462
99 | 5076 T=0.622 energy(1.250)=2.437
100 | 5077 T=0.622 energy(1.260)=2.412
101 | 5078 T=0.621 energy(1.270)=2.387
102 | 5079 T=0.620 energy(1.280)=2.362
103 | ...
104 | 6615 T=0.133 energy(1.950)=0.197
105 | 6617 T=0.133 energy(1.940)=0.236
106 | 6618 T=0.133 energy(1.930)=0.275
107 | 6619 T=0.133 energy(1.920)=0.314
108 | 6620 T=0.133 energy(1.930)=0.275
109 | 6621 T=0.133 energy(1.940)=0.236
110 | 6622 T=0.133 energy(1.930)=0.275
111 | ...
112 | 9377 T=0.008 energy(1.990)=0.040
113 | 9378 T=0.008 energy(2.000)=0.000
114 | 9396 T=0.008 energy(2.010)=0.040
115 | 9397 T=0.008 energy(2.000)=0.000
116 | 9528 T=0.007 energy(2.010)=0.040
117 | 9531 T=0.007 energy(2.000)=0.000
118 | solution: energy(2.000)=0.000
119 | ```
120 |
121 | 您可以看到上述模擬退火法程式,在一開始的時候幾乎都在亂走,因此浪費了很多時間,但也正是因為這種特性,模擬退火法比較有機會跳脫那些小山谷,而有機會找到更深的山谷,這正式模擬退火法的特性。
122 |
123 | 雖然花的比較多的時間,但是模擬退火法最後還是正確的找到了 4 的平方根,傳回了 2.000 的結果。
124 |
125 | ### 實例:多變數函數的最佳化
126 |
127 | 在此,我們將以求解  這個函數的最低點,看看上述演算法對多變數函數是否能正常運作。
128 |
129 | 檔案:simulatedAnnealingArray.js
130 |
131 | ```javascript
132 | var simulatedAnnealing = require("./simulatedAnnealing"); // 引入模擬退火法類別
133 | var solutionArray = require("./solutionArray"); // 引入多變數解答類別 (x^2+3y^2+z^2-4x-3y-5z+8)
134 |
135 | var sa = new simulatedAnnealing(); // 建立模擬退火法物件
136 | // 執行模擬退火法 (從「解答(x,y,z)=(1,1,1)」開始尋找, 最多執行 2 萬代。
137 | sa.run(new solutionArray([1,1,1]), 20000);
138 | ```
139 |
140 | 執行結果:
141 |
142 | ```
143 | 0 T=99.900 energy( 1.000 1.000 0.990 )=1.030
144 | 1 T=99.800 energy( 1.000 0.990 0.990 )=1.000
145 | 2 T=99.700 energy( 1.000 0.980 0.990 )=0.971
146 | 3 T=99.601 energy( 0.990 0.980 0.990 )=0.991
147 | 4 T=99.501 energy( 0.990 0.990 0.990 )=1.021
148 | 5 T=99.401 energy( 1.000 0.990 0.990 )=1.000
149 | 6 T=99.302 energy( 1.000 0.990 1.000 )=0.970
150 | ...
151 | 5985 T=0.251 energy( 0.870 1.260 1.770 )=0.543
152 | 5986 T=0.250 energy( 0.870 1.250 1.770 )=0.497
153 | 5987 T=0.250 energy( 0.870 1.250 1.760 )=0.512
154 | 5988 T=0.250 energy( 0.870 1.250 1.750 )=0.527
155 | 5989 T=0.250 energy( 0.870 1.250 1.760 )=0.512
156 | 5990 T=0.249 energy( 0.860 1.250 1.760 )=0.535
157 | ...
158 | 15036 T=0.000 energy( 2.000 0.500 2.510 )=-3.000
159 | 15038 T=0.000 energy( 2.000 0.500 2.500 )=-3.000
160 | 15173 T=0.000 energy( 2.010 0.500 2.500 )=-3.000
161 | 15174 T=0.000 energy( 2.000 0.500 2.500 )=-3.000
162 | 15261 T=0.000 energy( 2.000 0.500 2.490 )=-3.000
163 | 15265 T=0.000 energy( 2.000 0.500 2.500 )=-3.000
164 | solution: energy( 2.000 0.500 2.500 )=-3.000
165 | ```
166 |
167 | 您可以看到,上述的模擬退火法程式,總共花了一萬五千多代,終於找到了該多變數函數的谷底,雖然速度不快,但也總算是達成任務了。
168 |
169 | ### 結語
170 |
171 | 當然,模擬退火法雖然比較有機會跳脫小山谷,去找到更深的山谷,但這並不表示模擬退火法一定可以找到最深的山谷。
172 |
173 | 當溫度已經降到很低的時後,模擬退火法就會逐漸凝固,於是就會固定在某個山谷不出來了。
174 |
175 | 事實上、沒有任何一種優化方法可以在「多項式時間內」保證找到任何函數的最低點,否則「NP-Complete」問題不就被解掉了,而「NP-Complete」問題事實上在計算理論領域裡,一直還是個最困難的未解之謎啊!
176 |
177 | ### 參考文獻
178 | * [Wikipedia:Simulated annealing](http://en.wikipedia.org/wiki/Simulated_annealing)
179 | * [維基百科:模擬退火](http://zh.wikipedia.org/wiki/%E6%A8%A1%E6%8B%9F%E9%80%80%E7%81%AB)
180 |
181 | 【本文由陳鍾誠取材並修改自 [維基百科],採用創作共用的 [姓名標示、相同方式分享] 授權】
182 |
183 |
--------------------------------------------------------------------------------
/source/message1.md:
--------------------------------------------------------------------------------
1 | ## 數學短訊:邏輯世界的歷史
2 |
3 | ### 簡介
4 |
5 | 邏輯學是西方科學中淵遠流長的一門學問,從西元前 350 年亞里斯多德的三段論開始,就開啟了歐洲文明對邏輯學的興趣之窗。然而這一個興趣同樣隨著西方文明的發展而起伏不定,直到西元 1850 年左右,George Boole (布爾) 開始研究布林代數,才讓邏輯學成為近代數學的一個重要領域。接著,Gottlob Frege 在 1870 年左右所提出的一階邏輯系統,繼承布林系統並向上延伸,形成一個數學基礎穩固且強大的邏輯系統,於是整個經典的邏輯系統建立完成。
6 |
7 | 雖然如此,這些邏輯系統仍然是掌上的玩物,而且沒有人能確定這樣的邏輯系統,其能力到底有多強,是否一致且完備,是否有某些極限。希爾伯特在 1900 年所提出的 25 個數學問題中,這個問題被排在第二個提出。然而,希爾伯特並沒有能證明一階邏輯系統的完備性,而是在 1929 年由哥德爾證明完成了。
8 |
9 | 哥德爾的成就不僅於此,1931 年他更進一步證明了一個非常令人驚訝的定理,在「一階邏輯的擴充系統 - 皮諾數論系統」當中,不具有完備性,而且它證明了假如該系統是完備的,將會導致矛盾。
10 |
11 | 哥德爾在證明完備定理與不完備定理時,採用的都是矛盾証法,也就是透過排中律所證明的,這樣的証明並非建構性的,因此即使建立了完備定理,也沒有人能構造出一個建構式的証明方法,可以檢證一階邏輯的定理。
12 |
13 | 1965 年,Robinson 提出了一條非常簡單的邏輯證明規則 -- Resolution,並且說明了如何利用矛盾檢證程序 Refutation,證明邏輯規則在某系統中的真假,這個方法既簡單又優美,因此廣為數學界與計算機科學界所稱道。以下,我們將更詳細的說明上述人物在邏輯學上的貢獻。
14 |
15 | ### 亞里斯多德 (Aristotle) (出生於西元前 322 年)
16 |
17 | 亞里斯多德在其其理則學 (zoology) 研究中,提出了下列的三段式推論規則 Barbara,簡稱為三段論。
18 |
19 | | 類型 | 語句 | 說明 |
20 | |--------|-------------------|----------|
21 | | 大前提 | 所有人都終會死亡 | 普遍原理 |
22 | | 小前提 | 蘇格拉底是人 | 特殊陳述 |
23 | | 結論 | 蘇格拉底終會死亡 | 推論結果 |
24 |
25 |
26 | ### 布爾 (Boole) (出生於 1815年)
27 |
28 | Boole 研究邏輯時,提出了一種只有真值與假值的邏輯,稱為二值邏輯,通常我們用 0 代表假值,1 代表真值。布爾研究這種邏輯系統,並寫出了一些代數規則,稱為布林代數,以下是其中的一些代數規則。
29 |
30 | | 規則 (數學寫法) | 名稱 |
31 | |-------------------------------------------------|---------------|
32 | |  | OR 的結合律 |
33 | |  | OR 的交換律 |
34 | |  | AND 的結合律 |
35 | |  | AND 的交換律 |
36 | |  | 狄摩根定律(1) |
37 | |  | 狄摩根定律(2) |
38 |
39 | 說明:上述規則中的  代表邏輯或 (AND) (在程式語言裏常寫為 `&` 或 and),  代表邏輯或 (OR) (在程式語言裏常寫為 `|` 或 or)。所以若改用程式領域的寫法,可改寫如下。
40 |
41 | | 規則 (數學寫法) | 名稱 |
42 | |-----------------------------|---------------|
43 | | `x | (y | z) = (x | y) | z` | OR 的結合律 |
44 | | `x | y = y | z` | OR 的交換律 |
45 | | `x & (y & z) = (x & y) & z` | AND 的結合律 |
46 | | `x & y = y & x` | AND 的交換律 |
47 | | `-(x|y) = -x & -y` | 狄摩根定律(1) |
48 | | `-(x&y) = -x | -y` | 狄摩根定律(2) |
49 |
50 | ### 福雷格 (Frege) (出生於 1848年)
51 |
52 | Frege 在研究邏輯系統時,將函數的概念引入到邏輯系統當中,這種函數被稱為謂詞,因此該邏輯系統被稱為謂詞邏輯。然後,Frege 又引入了兩個量詞運算,  (對於所有) 與  (存在),透過謂詞的限定作用,以及這兩個量詞,Frege 架構出了這種具有函數的邏輯系統,後來被稱為一階邏輯系統 (First Order Logic)。
53 |
54 | 以下是我們將亞里斯多德的三段論,轉化為一階邏輯後,所寫出的一階邏輯規則。
55 |
56 | | 類型 | 語句 | 說明 |
57 | |--------|----------------------------------------------------|------------------|
58 | | 大前提 |  | 所有人都終會死亡 |
59 | | 小前提 |  | 蘇格拉底是人 |
60 | | 結論 |  | 蘇格拉底終會死亡 |
61 |
62 | ### 希爾伯特 (David Hilbert) (出生於 1862年)
63 |
64 | 事實上,在電腦被發明之前,數學界早已開始探索「公理系統」的能力極限。在西元 1900 年時,德國的偉大數學家希爾伯特 (Hilbert),提出了著名的 23 個數學問題,其中的第二個問題如下所示。
65 |
66 | > 證明算術公理系統的無矛盾性 The compatibility of the arithmetical axioms.
67 |
68 | 在上述問題中,希爾伯特的意思是要如何證明算術公理系統的 Compatibility,Compatibility 這個詞意謂著必須具有「一致性」 (Consistency) 與「完備性」(Completeness)。
69 |
70 | 所謂的「一致性」,是指公理系統本身不會具有矛盾的現象。假如我們用 A 代表該公理系統,那麼 A 具有一致性就是 A 不可能導出兩個矛盾的結論,也就是 A => P 與 A=> -P 不可能同時成立。
71 |
72 | 所謂的「完備性」,是指所有「永遠為真的算式」(也就是定理) 都是可以被証明的,沒有任何一個定理可以逃出該公理系統的掌握範圍。
73 |
74 | 然而,希爾伯特耗盡了整個後半生,卻也無法證明整數公理系統的一致性與完備性。或許是造化弄人,這個任務竟然被希爾伯特的一位優秀學生 - 哥德爾 (Godel) 所解決了,或者應該說是否決了。
75 |
76 | ### 哥德爾 (Kurt Gödel) (出生於 1906 年)
77 |
78 | 哥德爾實際上證明了兩個定理,第一個是 1929 年提出的「哥德爾完備定理」(Gödel's Complete Theorem),第二個是 1931 年證明的「哥德爾不完備定理」(Gödel's Incomplete Theorem),這兩個定理看來似乎相當矛盾,但事實上不然,因為兩者所討論的是不同的公理系統,前者的焦點是「一階邏輯系統」(First Order Logic),而後者的焦點則是「具備整數運算體系的一階邏輯系統」。
79 |
80 | 哥德爾完備定理證明了下列數學陳述:
81 |
82 | > 一階邏輯系統是一致且完備的
83 |
84 | 一致性代表一階邏輯系統不會具有矛盾的情況,而完備性則說明了一階邏輯當中的所有算式都可以被証明或否証。
85 |
86 | 哥德爾不完備定理證明了下列數學陳述:
87 |
88 | > 任何一致且完備的「數學形式化系統」中,只要它強到足以蘊涵「皮亞諾算術公理」,就可以在其中構造在體系內「既不能證明也不能否證的命題」。
89 |
90 | 哥德爾不完備定理改用另一個說法,如下所示:
91 |
92 | > 如果一個包含算術的公理系統可以用來描述它自身時,那麼它要麼是不完備的,要麼是不一致的,不可能兩者皆有!
93 |
94 | (筆者註:若該公理系統包含無限條公理時,必須是可列舉的 recursive enumerable)
95 |
96 | ### 羅賓遜 (John Alan Robinson) (出生於 1928 年)
97 |
98 | 雖然哥德爾證明了一階邏輯是完備的,但是卻沒有給出一個建構式的方法,可以推理出所有的的一階邏輯定理。這個問題由 John Alan Robinson 在 1965 年解決了。
99 |
100 | Robinson 提出的 refutation 邏輯推論法是一種反證法,任何一階邏輯的算式 P 只要在系統 S 當中是真的,只要將 -P 加入該系統 S 中,就可以經由反證法導出矛盾。如果 P 在系統 S 當中不是真的,那麼將 P 加入 S 當中就無法導出矛盾。
101 |
102 | 所謂的 refutation 反證法是依靠一個稱為 resolution 的邏輯規則,該規則如下所示:
103 |
104 | 
105 |
106 | 假如我們將上述算式中的  寫為 A,將  寫為 B,則上述算式可以改寫如下:
107 |
108 | 
109 |
110 | ### 結語
111 |
112 | 邏輯學在西方文化中扮演了非常重要的角色,而且可以說是「現代科學」會出現在歐洲的重要原因,假如將「邏輯學」從西方文化中拿掉,或許工業革命就不會出現在歐洲了?
113 |
114 | 您可以想像「孔子」整天追根究柢,常常和人辯論一件事情到底是真的還假,而且要轉換成符號,並且用邏輯的方式去證明嗎?
115 |
116 | 但是「亞里斯多德」在那個年代就是這樣追根究柢的,所以他才會去研究解剖學,把動物給切開看看裡面有甚麼,我想這也是他提出三段論背後的原因吧!
117 |
118 |
119 | ### 參考文獻
120 | * [維基百科:亞里斯多德](http://zh.wikipedia.org/zh-tw/%E4%BA%9A%E9%87%8C%E5%A3%AB%E5%A4%9A%E5%BE%B7)
121 | * [維基百科:喬治·布爾](http://zh.wikipedia.org/zh-tw/%E4%B9%94%E6%B2%BB%C2%B7%E5%B8%83%E5%B0%94)
122 | * [維基百科:三段論](http://zh.wikipedia.org/zh-tw/%E4%B8%89%E6%AE%B5%E8%AB%96)
123 | * [維基百科:哥德爾不完備定理](http://zh.wikipedia.org/zh-tw/%E5%93%A5%E5%BE%B7%E5%B0%94%E4%B8%8D%E5%AE%8C%E5%A4%87%E5%AE%9A%E7%90%86)
124 | * [維基百科:哥德爾完全性定理](http://zh.wikipedia.org/zh-tw/%E5%93%A5%E5%BE%B7%E5%B0%94%E5%AE%8C%E5%A4%87%E6%80%A7%E5%AE%9A%E7%90%86)
125 | * [維基百科:戈特洛布·弗雷格](http://zh.wikipedia.org/zh-tw/%E6%88%88%E7%89%B9%E6%B4%9B%E5%B8%83%C2%B7%E5%BC%97%E9%9B%B7%E6%A0%BC)
126 | * [維基百科:大衛·希爾伯特](http://zh.wikipedia.org/wiki/%E5%A4%A7%E5%8D%AB%C2%B7%E5%B8%8C%E5%B0%94%E4%BC%AF%E7%89%B9)
127 | * [維基百科:希爾伯特的23個問題](http://zh.wikipedia.org/zh-tw/%E5%B8%8C%E5%B0%94%E4%BC%AF%E7%89%B9%E7%9A%8423%E4%B8%AA%E9%97%AE%E9%A2%98)
128 | * [維基百科:庫爾特·哥德爾](http://zh.wikipedia.org/wiki/%E5%BA%93%E5%B0%94%E7%89%B9%C2%B7%E5%93%A5%E5%BE%B7%E5%B0%94)
129 | * [Wikipedia:Zoology](http://en.wikipedia.org/wiki/Zoology)
130 | * [Wikipedia:Aristotle](http://en.wikipedia.org/wiki/Aristotle)
131 | * [Wikipedia:Boolean Logic](http://en.wikipedia.org/wiki/Boolean_logic)
132 | * [Wikipedia:George Boole](http://en.wikipedia.org/wiki/George_Boole)
133 | * [Wikipedia:Frege](http://en.wikipedia.org/wiki/Frege)
134 | * [Wikipedia:Hilbert's_problems](http://en.wikipedia.org/wiki/Hilbert_problem)
135 | * [Wikipedia:John Alan Robinson](http://en.wikipedia.org/wiki/J._Alan_Robinson)
136 | * [Wikipedia:Resolution (Logic)](http://en.wikipedia.org/wiki/Resolution_logic)
137 | * [Hilbert's Mathematical Problems](http://aleph0.clarku.edu/~djoyce/hilbert/toc.html)
138 | * [Wikipedia:Kurt_Gödel](http://en.wikipedia.org/wiki/Kurt_Gödel)
139 |
140 | 【本文由陳鍾誠取材並修改自 [維基百科],採用創作共用的 [姓名標示、相同方式分享] 授權】
141 |
142 |
--------------------------------------------------------------------------------