├── source ├── focus.md ├── article.md ├── discuss.md ├── message.md ├── preface.md ├── pdffooter.htm ├── md0.js ├── Makefile ├── convert.exe ├── mimetex.exe ├── title.md ├── metadata.xml ├── tex2img.bat ├── header.htm ├── footer.htm ├── license.md ├── editor.md ├── reflink.md9 ├── discuss1.md ├── home.md ├── article3.md ├── article6.md ├── article4.md ├── info.md ├── focus2.md0 ├── focus2.md ├── article2.md ├── focus1.md ├── focus1.md0 ├── focus4.md0 ├── focus4.md └── message1.md ├── code.zip ├── book ├── A4.pdf ├── A4.epub ├── ipad.epub └── ipad.pdf ├── code └── js │ ├── ccc.js │ ├── j0c.js │ ├── code.js │ ├── os0t.as0 │ ├── sum.ob0 │ ├── sum.ob1 │ ├── sum.obj0 │ ├── test.obj │ ├── ir2as0.js │ ├── memory.js │ ├── os0ts.as0 │ ├── bak │ ├── inherit2.js │ ├── test.obj.bak │ ├── test.j0.bak │ ├── inherit.js │ ├── jc1.js │ ├── jexpc.js │ ├── vm.js.bak │ ├── jc1parser.js │ ├── vm0.js.bak │ ├── jparser.js │ ├── vm0.js.bk1 │ └── as1_bak.js │ ├── test.c0 │ ├── test.j1 │ ├── test2.j1 │ ├── while.j │ ├── while.j0 │ ├── mcu0i1.hex │ ├── sum.j0 │ ├── sum.as1 │ ├── scan.js │ ├── test.j │ ├── test.j0 │ ├── cpu1.js │ ├── cpu0.js │ ├── optable.js │ ├── test.ir0 │ ├── sum.as0 │ ├── as1.js │ ├── expc.js │ ├── vm1.js │ ├── test.as0 │ ├── as.js │ ├── cc1.js │ ├── as0.js │ └── vm0.js ├── img ├── Normal.gif ├── cover.jpg ├── Gradient.jpg ├── coverA4.png ├── ArduinoClip.png ├── ArduinoICSP.png ├── MagicSquare.jpg ├── opencvar1.png ├── Arduino2board.png ├── ArduinoATMega.png ├── ArduinoAVRISP.png ├── GoogleGraph2D.jpg ├── GoogleGraph3D.jpg ├── MagicSquare2.jpg ├── Arduino2board2.png ├── Arduino_ToolsUI1.png ├── Arduino_ToolsUI2.png ├── Arduino_ToolsUI3.png ├── Arduino_ToolsUI4.png ├── OpenCV_ar-debug.jpg ├── ArduinoInterface1.png ├── ArduinoIsp3_image1.png ├── OpenCV_AR_convert.jpg ├── Arduino_BreadBoard1.png ├── Arduino_BreadBoard2.png ├── Arduino_BreadBoard3.png ├── Arduino_BreadBoard4.png ├── Arduino_BreadBoard5.png ├── GoogleGraph2D2vally.jpg ├── GoogleGraph2DMountain.jpg ├── OpenCV_AR_actag-large.png └── OpenCV_AR_nakaohome.jpg ├── timg ├── 0a7a7dbd3516.jpg ├── 121a89b66301.jpg ├── 489e5db6d4d6.jpg ├── 4d0383fdb044.jpg ├── 658cc096866d.jpg ├── 7621e383e2fc.jpg ├── 9205615feebe.jpg ├── b2a89b129682.jpg ├── b2cb861ede12.jpg ├── c7c64bbfca16.jpg ├── fb18212e251c.jpg └── fb1e12546ddc.jpg ├── README.md ├── htm ├── article.html ├── message.html ├── people.html ├── video.html ├── preface.html ├── focus.html ├── science.html ├── discuss.html ├── title.html ├── editor.html ├── license.html ├── discuss1.html ├── people2.html ├── article3.html ├── video1.html ├── home.html ├── people1.html ├── info.html ├── article2.html ├── article6.html ├── article4.html └── focus2.html ├── submit └── ZandB.md └── css └── pmag.css /source/focus.md: -------------------------------------------------------------------------------- 1 | # 本期焦點 2 | -------------------------------------------------------------------------------- /source/article.md: -------------------------------------------------------------------------------- 1 | 2 | # 程式人文集 3 | -------------------------------------------------------------------------------- /source/discuss.md: -------------------------------------------------------------------------------- 1 | # 程式人討論區 2 | 3 | -------------------------------------------------------------------------------- /source/message.md: -------------------------------------------------------------------------------- 1 | 2 | # 程式人短訊 3 | -------------------------------------------------------------------------------- /source/preface.md: -------------------------------------------------------------------------------- 1 | # 前言 2 | 3 | 4 | -------------------------------------------------------------------------------- /source/pdffooter.htm: -------------------------------------------------------------------------------- 1 |
_PAGENUM_
2 | -------------------------------------------------------------------------------- /code.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/code.zip -------------------------------------------------------------------------------- /book/A4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/book/A4.pdf -------------------------------------------------------------------------------- /book/A4.epub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/book/A4.epub -------------------------------------------------------------------------------- /book/ipad.epub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/book/ipad.epub -------------------------------------------------------------------------------- /book/ipad.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/book/ipad.pdf -------------------------------------------------------------------------------- /code/js/ccc.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/code/js/ccc.js -------------------------------------------------------------------------------- /code/js/j0c.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/code/js/j0c.js -------------------------------------------------------------------------------- /img/Normal.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/Normal.gif -------------------------------------------------------------------------------- /img/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/cover.jpg -------------------------------------------------------------------------------- /source/md0.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/source/md0.js -------------------------------------------------------------------------------- /code/js/code.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/code/js/code.js -------------------------------------------------------------------------------- /code/js/os0t.as0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/code/js/os0t.as0 -------------------------------------------------------------------------------- /code/js/sum.ob0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/code/js/sum.ob0 -------------------------------------------------------------------------------- /code/js/sum.ob1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/code/js/sum.ob1 -------------------------------------------------------------------------------- /code/js/sum.obj0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/code/js/sum.obj0 -------------------------------------------------------------------------------- /code/js/test.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/code/js/test.obj -------------------------------------------------------------------------------- /img/Gradient.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/Gradient.jpg -------------------------------------------------------------------------------- /img/coverA4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/coverA4.png -------------------------------------------------------------------------------- /source/Makefile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/source/Makefile -------------------------------------------------------------------------------- /code/js/ir2as0.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/code/js/ir2as0.js -------------------------------------------------------------------------------- /code/js/memory.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/code/js/memory.js -------------------------------------------------------------------------------- /code/js/os0ts.as0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/code/js/os0ts.as0 -------------------------------------------------------------------------------- /img/ArduinoClip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/ArduinoClip.png -------------------------------------------------------------------------------- /img/ArduinoICSP.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/ArduinoICSP.png -------------------------------------------------------------------------------- /img/MagicSquare.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/MagicSquare.jpg -------------------------------------------------------------------------------- /img/opencvar1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/opencvar1.png -------------------------------------------------------------------------------- /source/convert.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/source/convert.exe -------------------------------------------------------------------------------- /source/mimetex.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/source/mimetex.exe -------------------------------------------------------------------------------- /img/Arduino2board.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/Arduino2board.png -------------------------------------------------------------------------------- /img/ArduinoATMega.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/ArduinoATMega.png -------------------------------------------------------------------------------- /img/ArduinoAVRISP.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/ArduinoAVRISP.png -------------------------------------------------------------------------------- /img/GoogleGraph2D.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/GoogleGraph2D.jpg -------------------------------------------------------------------------------- /img/GoogleGraph3D.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/GoogleGraph3D.jpg -------------------------------------------------------------------------------- /img/MagicSquare2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/MagicSquare2.jpg -------------------------------------------------------------------------------- /timg/0a7a7dbd3516.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/timg/0a7a7dbd3516.jpg -------------------------------------------------------------------------------- /timg/121a89b66301.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/timg/121a89b66301.jpg -------------------------------------------------------------------------------- /timg/489e5db6d4d6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/timg/489e5db6d4d6.jpg -------------------------------------------------------------------------------- /timg/4d0383fdb044.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/timg/4d0383fdb044.jpg -------------------------------------------------------------------------------- /timg/658cc096866d.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/timg/658cc096866d.jpg -------------------------------------------------------------------------------- /timg/7621e383e2fc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/timg/7621e383e2fc.jpg -------------------------------------------------------------------------------- /timg/9205615feebe.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/timg/9205615feebe.jpg -------------------------------------------------------------------------------- /timg/b2a89b129682.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/timg/b2a89b129682.jpg -------------------------------------------------------------------------------- /timg/b2cb861ede12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/timg/b2cb861ede12.jpg -------------------------------------------------------------------------------- /timg/c7c64bbfca16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/timg/c7c64bbfca16.jpg -------------------------------------------------------------------------------- /timg/fb18212e251c.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/timg/fb18212e251c.jpg -------------------------------------------------------------------------------- /timg/fb1e12546ddc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/timg/fb1e12546ddc.jpg -------------------------------------------------------------------------------- /code/js/bak/inherit2.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/code/js/bak/inherit2.js -------------------------------------------------------------------------------- /code/js/bak/test.obj.bak: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/code/js/bak/test.obj.bak -------------------------------------------------------------------------------- /code/js/test.c0: -------------------------------------------------------------------------------- 1 | sum = 0; 2 | for (i=0; i<=10; i++) 3 | { 4 | sum = sum + i; 5 | } 6 | return sum; 7 | -------------------------------------------------------------------------------- /code/js/test.j1: -------------------------------------------------------------------------------- 1 | sum = 0; 2 | for (i=0; i<=10; i++) 3 | { 4 | sum = sum + i; 5 | } 6 | return sum; 7 | -------------------------------------------------------------------------------- /code/js/test2.j1: -------------------------------------------------------------------------------- 1 | sum = 0; 2 | for (i=0; i<=10; i++) 3 | { 4 | sum = sum + i; 5 | } 6 | return sum; 7 | -------------------------------------------------------------------------------- /img/Arduino2board2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/Arduino2board2.png -------------------------------------------------------------------------------- /img/Arduino_ToolsUI1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/Arduino_ToolsUI1.png -------------------------------------------------------------------------------- /img/Arduino_ToolsUI2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/Arduino_ToolsUI2.png -------------------------------------------------------------------------------- /img/Arduino_ToolsUI3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/Arduino_ToolsUI3.png -------------------------------------------------------------------------------- /img/Arduino_ToolsUI4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/Arduino_ToolsUI4.png -------------------------------------------------------------------------------- /img/OpenCV_ar-debug.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/OpenCV_ar-debug.jpg -------------------------------------------------------------------------------- /source/title.md: -------------------------------------------------------------------------------- 1 | % [程式人雜誌](https://www.facebook.com/groups/programmerMagazine/) 2 | % 2014 年 5 月號 3 | % 本期焦點:爬山演算法 -------------------------------------------------------------------------------- /code/js/while.j: -------------------------------------------------------------------------------- 1 | sum = 0; 2 | i=1; 3 | while (i<=10) { 4 | sum = sum + i; 5 | i++; 6 | } 7 | return sum; 8 | -------------------------------------------------------------------------------- /img/ArduinoInterface1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/ArduinoInterface1.png -------------------------------------------------------------------------------- /img/ArduinoIsp3_image1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/ArduinoIsp3_image1.png -------------------------------------------------------------------------------- /img/OpenCV_AR_convert.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/OpenCV_AR_convert.jpg -------------------------------------------------------------------------------- /code/js/while.j0: -------------------------------------------------------------------------------- 1 | sum = 0; 2 | i=1; 3 | while (i<=10) { 4 | sum = sum + i; 5 | i++; 6 | } 7 | return sum; 8 | -------------------------------------------------------------------------------- /img/Arduino_BreadBoard1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/Arduino_BreadBoard1.png -------------------------------------------------------------------------------- /img/Arduino_BreadBoard2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/Arduino_BreadBoard2.png -------------------------------------------------------------------------------- /img/Arduino_BreadBoard3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/Arduino_BreadBoard3.png -------------------------------------------------------------------------------- /img/Arduino_BreadBoard4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/Arduino_BreadBoard4.png -------------------------------------------------------------------------------- /img/Arduino_BreadBoard5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/Arduino_BreadBoard5.png -------------------------------------------------------------------------------- /img/GoogleGraph2D2vally.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/GoogleGraph2D2vally.jpg -------------------------------------------------------------------------------- /img/GoogleGraph2DMountain.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/GoogleGraph2DMountain.jpg -------------------------------------------------------------------------------- /img/OpenCV_AR_actag-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/OpenCV_AR_actag-large.png -------------------------------------------------------------------------------- /img/OpenCV_AR_nakaohome.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/programmermagazine/201405/gh-pages/img/OpenCV_AR_nakaohome.jpg -------------------------------------------------------------------------------- /source/metadata.xml: -------------------------------------------------------------------------------- 1 | Creative Commons Non-Commercial Share Alike 3.0 2 | en-US 3 | -------------------------------------------------------------------------------- /source/tex2img.bat: -------------------------------------------------------------------------------- 1 | if not exist {%2.jpg} ( 2 | mimetex -d %1 -e %2.gif 3 | convert %2.gif %2.jpg 4 | rm %2.gif 5 | ) 6 | -------------------------------------------------------------------------------- /code/js/mcu0i1.hex: -------------------------------------------------------------------------------- 1 | 00 08 // 0 LD A 2 | 10 08 // 4 ST B 3 | E3 00 // 8 RET 4 | 00 03 // c A: 3 5 | 00 05 // 10 B: 5 6 | -------------------------------------------------------------------------------- /code/js/sum.j0: -------------------------------------------------------------------------------- 1 | s = sum(10); 2 | return s; 3 | 4 | function sum(n) { 5 | s = 0; 6 | i=1; 7 | while (i<=10) { 8 | s = s + i; 9 | i++; 10 | } 11 | return s; 12 | } 13 | 14 | -------------------------------------------------------------------------------- /source/header.htm: -------------------------------------------------------------------------------- 1 |
2 |

程式人雜誌 -- 2014 年 5 月號 (開放公益出版品)

3 |
4 |
5 | -------------------------------------------------------------------------------- /source/footer.htm: -------------------------------------------------------------------------------- 1 |
2 | 5 | -------------------------------------------------------------------------------- /code/js/sum.as1: -------------------------------------------------------------------------------- 1 | LD R1, sum 2 | LD R2, i 3 | ADDI R3, R0, 10 4 | ADDI R4, R0, 10 5 | FOR: ADDI R5, R2,-11 6 | JZ R5, EXIT 7 | ADD R1, i 8 | ADDI R2, R2, 1 9 | ST R2, i 10 | JZ R0, FOR 11 | EXIT: RET 12 | x: BYTE 3,18,2 13 | i: WORD 1 14 | sum: WORD 0 15 | back: WORD 0 -------------------------------------------------------------------------------- /code/js/scan.js: -------------------------------------------------------------------------------- 1 | // 本來應該用 .*? 來比對 /*...*/ 註解的,但 javascript 的 . 並不包含 \n, 因此用 \s\S 代替 . 就可以了。 2 | var re = /(\/\*[\s\S]*?\*\/)|(\/\/[^\r\n])|(\d+)|([a-zA-Z]\w*)|(\r?\n)|(.)/gm; // *?, +? non greedy, m for multiline 3 | text = "a=3+x-num*99; // comment\r\n y=5; /* block \r\n comment 1 */ z=3; /* block comment 2 */"; 4 | var tokens = text.match(re); 5 | console.log(tokens); 6 | -------------------------------------------------------------------------------- /source/license.md: -------------------------------------------------------------------------------- 1 | ## 授權聲明 2 | 3 | 本雜誌許多資料修改自維基百科,採用 創作共用:[姓名標示、相同方式分享] 授權,若您想要修改本書產生衍生著作時,至少應該遵守下列授權條件: 4 | 5 | 1. 標示原作者姓名 (包含該文章作者,若有來自維基百科的部份也請一併標示)。 6 | 3. 採用 創作共用:[姓名標示、相同方式分享] 的方式公開衍生著作。 7 | 8 | 另外、當本雜誌中有文章或素材並非採用 [姓名標示、相同方式分享] 時,將會在該文章或素材後面標示其授權,此時該文章將以該標示的方式授權釋出,請修改者注意這些授權標示,以避免產生侵權糾紛。 9 | 10 | 例如有些文章可能不希望被作為「商業性使用」,此時就可能會採用創作共用:[姓名標示、非商業性、相同方式分享] 的授權,此時您就不應當將該文章用於商業用途上。 11 | 12 | 最後、懇請勿移除公益捐贈的相關描述,以便讓愛心得以持續散播! 13 | 14 | -------------------------------------------------------------------------------- /source/editor.md: -------------------------------------------------------------------------------- 1 | ## 編輯小語 2 | 3 | 在本期的「程式人雜誌」中,聚焦的主題是「爬山演算法」。 4 | 5 | 「爬山演算法」在人工智慧領域是用來做優化 (最佳化) 的一種簡單演算法。這方法雖然簡單,但是卻非常好用,因此我常用「爬山演算法」來解各式各樣的優化問題。而且在「人工智慧」課程時用「爬山演算法」做為第一個學習的優化算法。 6 | 7 | 「模擬退火法」則是爬山演算法的一個變形,但在優化的想法上有點不同,爬山演算法顧名思義會不斷的往高處爬,而「模擬退火法」則會不斷的向著能量低的方向走。當我們把爬山的高度轉換成位能函數時,就變成了一種「流水下山演算法」,然後我們再加入用溫度控制的遷移機率之後,可以讓這個演算法有機會跳出谷底,找到另一個更好的解。 8 | 9 | 當然、本期不只有「爬山演算法」的相關文章,還有更精彩的 Arduino, VB, OpenCV 與擴增實境, 開放電腦計畫等內容,希望讀者會喜歡這期的「程式人雜誌」! 10 | 11 | ---- (程式人雜誌編輯 - 陳鍾誠) 12 | -------------------------------------------------------------------------------- /code/js/test.j: -------------------------------------------------------------------------------- 1 | s = sum(10); 2 | m = max(3,5); 3 | 4 | function sum(n) { 5 | s = 0; 6 | i=1; 7 | while (i<=10) { 8 | s = s + i; 9 | i++; 10 | } 11 | return s; 12 | } 13 | 14 | function max(a, b) { 15 | if (a > b) 16 | return a; 17 | else 18 | return b; 19 | } 20 | 21 | function total(a) { 22 | s = 0; 23 | for (i in a) { 24 | s = s + a[i]; 25 | } 26 | return s; 27 | } 28 | 29 | a = [ 1, 3, 7, 2, 6]; 30 | t = total(a); 31 | word = { e:"dog", c:"狗" }; 32 | -------------------------------------------------------------------------------- /code/js/bak/test.j0.bak: -------------------------------------------------------------------------------- 1 | s = sum(10); 2 | m = max(3,5); 3 | 4 | function sum(n) { 5 | s = 0; 6 | i=1; 7 | while (i<=10) { 8 | s = s + i; 9 | i++; 10 | } 11 | return s; 12 | } 13 | 14 | function max(a, b) { 15 | if (a > b) 16 | return a; 17 | else 18 | return b; 19 | } 20 | 21 | function total(a) { 22 | s = 0; 23 | for (i in a) { 24 | s = s + a[i]; 25 | } 26 | return s; 27 | } 28 | 29 | a = [ 1, 3, 7, 2, 6]; 30 | t = total(a); 31 | word = { e:"dog", c:"狗" }; 32 | -------------------------------------------------------------------------------- /code/js/test.j0: -------------------------------------------------------------------------------- 1 | s = sum(10); 2 | 3 | function sum(n) { 4 | s = 0; 5 | i=1; 6 | while (i<=10) { 7 | s = s + i; 8 | i++; 9 | } 10 | return s; 11 | } 12 | 13 | m = max(3,5); 14 | 15 | function max(a, b) { 16 | if (a > b) 17 | return a; 18 | else 19 | return b; 20 | } 21 | 22 | function total(a) { 23 | s = 0; 24 | for (i in a) { 25 | s = s + a[i]; 26 | } 27 | return s; 28 | } 29 | 30 | a = [ 1, 3, 7, 2, 6]; 31 | t = total(a); 32 | word = { e:"dog", c:"狗" }; 33 | -------------------------------------------------------------------------------- /code/js/cpu1.js: -------------------------------------------------------------------------------- 1 | var opTable = require("./optable"); 2 | var opList = ["LD 00 L", "ST 01 L", "LDB 02 L", "STB 03 L", 3 | "ADDI 12 C", "ADD 13 L", "SUB 14 L", "MUL 15 L", "DIV 16 L", "AND 18 L", 4 | "OR 19 L", "XOR 1A L", "ROL 1C L", "ROR 1D L", "SHL 1E L", "SHR 1F L", 5 | "JZ 20 L", "RET 2C N", "PUSH 30 L", "POP 31 L", "PUSHB 32 L", "POPB 33 L", 6 | "RESW F0 D", "RESB F1 D", "WORD F2 D", "BYTE F3 D"]; 7 | 8 | var cpu = { "opTable" : new opTable(opList) }; 9 | // cpu.opTable.dump(); 10 | module.exports = cpu; 11 | -------------------------------------------------------------------------------- /source/reflink.md9: -------------------------------------------------------------------------------- 1 | [程式人雜誌社團]: https://www.facebook.com/groups/programmerMagazine/ 2 | [科學玩具實驗室]:https://www.facebook.com/groups/sciencetoy/ 3 | [姓名標示、相同方式分享]: http://creativecommons.org/licenses/by-sa/3.0/tw/ 4 | [姓名標示、非商業性、相同方式分享]: http://creativecommons.org/licenses/by-nc-sa/3.0/tw/ 5 | [馬萬圳]: http://coopermaa2nd.blogspot.tw/ 6 | [陳鍾誠]: http://ccckmit.wikidot.com/ 7 | [雜誌訂閱]: https://docs.google.com/spreadsheet/viewform?fromEmail=true&formkey=dG1TcER6Q3h1ZkpacFpDeEVFTDBLeVE6MQ 8 | [College Physics]:http://openstaxcollege.org/textbooks/college-physics 9 | [維基百科]:http://zh.wikipedia.org/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### 關於程式人雜誌 2 | 3 | [程式人雜誌] 是一個結合「開放原始碼與公益捐款活動」的雜誌,簡稱「開放公益雜誌」。開放公益雜誌本著「讀書做善事、寫書做公益」的精神,我們非常歡迎程式人認養專欄、或者捐出您的網誌。 4 | 5 | ## 授權聲明 6 | 7 | 本雜誌許多資料修改自維基百科,採用 創作共用:[姓名標示、相同方式分享] 授權,若您想要修改本書產生衍生著作時,至少應該遵守下列授權條件: 8 | 9 | 1. 標示原作者姓名 (包含該文章作者,若有來自維基百科的部份也請一併標示)。 10 | 3. 採用 創作共用:[姓名標示、相同方式分享] 的方式公開衍生著作。 11 | 12 | 另外、當本雜誌中有文章或素材並非採用 [姓名標示、相同方式分享] 時,將會在該文章或素材後面標示其授權,此時該文章將以該標示的方式授權釋出,請修改者注意這些授權標示,以避免產生侵權糾紛。 13 | 14 | 例如有些文章可能不希望被作為「商業性使用」,此時就可能會採用創作共用:[姓名標示、非商業性、相同方式分享] 的授權,此時您就不應當將該文章用於商業用途上。 15 | 16 | 最後、懇請勿移除公益捐贈的相關描述,以便讓愛心得以持續散播! 17 | 18 | 19 | [程式人雜誌]: https://www.facebook.com/groups/programmerMagazine/ 20 | [姓名標示、相同方式分享]: http://creativecommons.org/licenses/by-sa/3.0/tw/ 21 | [姓名標示、非商業性、相同方式分享]: http://creativecommons.org/licenses/by-nc-sa/3.0/tw/ 22 | -------------------------------------------------------------------------------- /code/js/cpu0.js: -------------------------------------------------------------------------------- 1 | var opTable = require("./optable"); 2 | var opList = [ "LD 00 L", "ST 01 L", "LDB 02 L", "STB 03 L", "LDR 04 L", 3 | "STR 05 L", "LBR 06 L", "SBR 07 L", "LDI 08 L", "CMP 10 A", "MOV 12 A", 4 | "ADD 13 A", "SUB 14 A", "MUL 15 A", "DIV 16 A", "AND 18 A", "OR 19 A", "XOR 1A A", 5 | "ADDI 1B A", "ROL 1C A", "ROR 1D A", "SHL 1E A", "SHR 1F A", 6 | "JEQ 20 J", "JNE 21 J", "JLT 22 J", "JGT 23 J", "JLE 24 J", "JGE 25 J", "JMP 26 J", 7 | "SWI 2A J", "JSUB 2B J","RET 2C J", "PUSH 30 J", "POP 31 J", "PUSHB 32 J", 8 | "POPB 33 J", "RESW F0 D", "RESB F1 D", "WORD F2 D", "BYTE F3 D"]; 9 | 10 | var cpu = { "opTable" : new opTable(opList) }; 11 | 12 | if (process.argv[2] == "-d") 13 | cpu.opTable.dump(); 14 | 15 | module.exports = cpu; 16 | -------------------------------------------------------------------------------- /code/js/optable.js: -------------------------------------------------------------------------------- 1 | var c = require("./ccc"); 2 | 3 | var Op = function(line) { 4 | var tokens = line.split(/\s+/); 5 | this.name = tokens[0]; 6 | this.id = parseInt(tokens[1], 16); 7 | this.type = tokens[2]; 8 | } 9 | 10 | var opTable = function(opList) { 11 | for (i in opList) { 12 | var op = new Op(opList[i]); 13 | this[op.name] = op; 14 | } 15 | } 16 | 17 | opTable.prototype.ID = function(op) { 18 | return this[op].id; 19 | } 20 | 21 | opTable.prototype.dump=function() { 22 | for (key in this) { 23 | var op = this[key]; 24 | if (typeof(op)!="function") 25 | c.log("%s %s %s", c.fill(' ', op.name, 8), c.hex(op.id, 2), op.type); 26 | } 27 | } 28 | 29 | module.exports = opTable; 30 | -------------------------------------------------------------------------------- /source/discuss1.md: -------------------------------------------------------------------------------- 1 | ## 討論:本月討論精選 2 | 3 | 4 | * 簡單的圖表說明各種不同的開放原始碼授權 -- 5 | * 6 | * 7 | 8 | * 直接用JavaScript對網頁圖片裡的英文字做文字辨識(OCR),需要插入一個大約1mb大小的JavaScript檔案在網頁裡,有demo,不過效果沒很好. 9 | * 10 | 11 | * 不錯的雜誌:编程狂人 - 推酷 -- 12 | * 13 | 14 | * 機器語言,是由什麼語言寫的? -- 15 | 16 | * 是否有文章介紹如何做出一個程式語言?一個程式語言設計的極限是什麼? -- 17 | 18 | 19 | ### 參考文獻 20 | 21 | -------------------------------------------------------------------------------- /code/js/bak/inherit.js: -------------------------------------------------------------------------------- 1 | var util = require("util"); 2 | 3 | function Parent() { 4 | this.age = 40; 5 | } 6 | 7 | Parent.prototype.toStr = function() { 8 | return "Parent:toStr age="+this.age; 9 | } 10 | 11 | Parent.prototype.callf = function() { 12 | console.log("Parent:callf"); 13 | return this.f(); 14 | } 15 | 16 | function Child() { 17 | this.age = 10; 18 | } 19 | 20 | util.inherits(Child, Parent); 21 | 22 | Child.prototype.toStr = function() { 23 | return "Child:toStr age="+this.age; 24 | } 25 | 26 | Child.prototype.f = function() { 27 | console.log("Child:f"); 28 | } 29 | 30 | Child.prototype.print = function() { 31 | return console.log(this.toStr()); 32 | } 33 | 34 | parent = new Parent(); 35 | console.log(parent.toStr()); 36 | // parent.print(); 37 | child = new Child(); 38 | console.log(child.toStr()); 39 | child.print(); 40 | child.callf(); 41 | -------------------------------------------------------------------------------- /code/js/test.ir0: -------------------------------------------------------------------------------- 1 | arg 10 2 | call T1 sum 3 | = s T1 4 | sum function 5 | param n 6 | = s 0 7 | = i 1 8 | L1 9 | <= T2 i 10 10 | if0 T2 L2 11 | + T3 s i 12 | = s T3 13 | ++ i 14 | goto L1 15 | L2 16 | return s 17 | endf 18 | arg 3 19 | arg 5 20 | call T4 max 21 | = m T4 22 | max function 23 | param a 24 | param b 25 | > T5 a b 26 | if0 T5 L3 27 | return a 28 | L3 29 | return b 30 | endf 31 | total function 32 | param a 33 | = s 0 34 | = i 0 35 | L4 length T6 a 36 | < T7 i T6 37 | if0 T7 L5 38 | [] T8 a i 39 | + T9 s T8 40 | = s T9 41 | goto L4 42 | L5 43 | return s 44 | endf 45 | array T10 46 | apush T10 1 47 | apush T10 3 48 | apush T10 7 49 | apush T10 2 50 | apush T10 6 51 | = a T10 52 | arg a 53 | call T11 total 54 | = t T11 55 | table T12 56 | map T12 e "dog" 57 | map T12 c "狗" 58 | = word T12 59 | -------------------------------------------------------------------------------- /code/js/sum.as0: -------------------------------------------------------------------------------- 1 | LD R1, sum ; R1 = sum = 0 2 | LD R2, i ; R2 = i = 1 3 | LDI R3, 10 ; R3 = 10 4 | FOR: CMP R2, R3 ; if (R2 > R3) 5 | JGT EXIT ; goto EXIT 6 | ADD R1, R1, R2 ; R1 = R1 + R2 (sum = sum + i) 7 | ADDI R2, R2, 1 ; R2 = R2 + 1 ( i = i + 1) 8 | JMP FOR ; goto FOR 9 | EXIT: ST R1, sum ; sum = R1 10 | ST R2, i ; i = R2 11 | LD R9, msgptr ; R9= pointer(msg) = &msg 12 | SWI 3 ; SWI 3 : 印出 R9 (=&msg) 中的字串 13 | MOV R9, R1 ; R9 = R1 = sum 14 | SWI 4 ; SWI 2 : 印出 R9 (=R1=sum) 中的整數 15 | RET ; return 返回上一層呼叫函數 16 | i: RESW 1 ; int i 17 | sum: WORD 0 ; int sum=0 18 | msg: BYTE "1+...+10=", 0 ; char *msg = "sum=" 19 | msgptr: WORD msg ; char &msgptr = &msg -------------------------------------------------------------------------------- /htm/article.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

程式人雜誌 -- 2014 年 5 月號 (開放公益出版品)

14 |
15 |
16 |

# 程式人文集

17 |
18 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /htm/message.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

程式人雜誌 -- 2014 年 5 月號 (開放公益出版品)

14 |
15 |
16 |

# 程式人短訊

17 |
18 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /htm/people.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

程式人雜誌 -- 2014 年 5 月號 (開放公益出版品)

14 |
15 |
16 |

# 人物速寫

17 |
18 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /htm/video.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

程式人雜誌 -- 2014 年 5 月號 (開放公益出版品)

14 |
15 |
16 |

# 影音頻道

17 |
18 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /htm/preface.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

程式人雜誌 -- 2014 年 5 月號 (開放公益出版品)

14 |
15 |
16 |
17 | 20 |
21 |

前言

22 |
23 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /htm/focus.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

程式人雜誌 -- 2014 年 5 月號 (開放公益出版品)

14 |
15 |
16 |
17 | 20 |
21 |

本期焦點

22 |
23 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /htm/science.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

程式人雜誌 -- 2014 年 5 月號 (開放公益出版品)

14 |
15 |
16 |
17 | 20 |
21 |

程式與科學

22 |
23 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /htm/discuss.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

程式人雜誌 -- 2014 年 5 月號 (開放公益出版品)

14 |
15 |
16 |
17 | 20 |
21 |

程式人討論區

22 |
23 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /htm/title.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 程式人雜誌 9 | 10 | 11 | 12 | 13 |
14 |

程式人雜誌 -- 2014 年 5 月號 (開放公益出版品)

15 |
16 |
17 | 22 | 23 |
24 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /source/home.md: -------------------------------------------------------------------------------- 1 | ### 關於程式人雜誌 2 | 3 | 程式人雜誌是一個結合「開放原始碼與公益捐款活動」的雜誌,簡稱「開放公益雜誌」。開放公益雜誌本著「讀書做善事、寫書做公益」的精神,我們非常歡迎程式人認養專欄、或者捐出您的網誌。 4 | 5 | ### 雜誌下載 6 | 7 | 出刊年月 epub ipad:PDF A4:PDF 單頁 HTM 原始碼 全部下載 8 | ------------ ---------- ----------- -------- ----------- ---------- ------------- 9 | 2014年5月 [epub] [ipad.pdf] [A4.pdf] [pmag.html] [code.zip] [github] 10 | 11 | ### 本期內容 (焦點:爬山演算法) 12 | * 前言 13 | * [編輯小語](editor.html) 14 | * [授權聲明](license.html) 15 | * 本期焦點 16 | * [從爬山演算法到梯度下降法](focus1.html) 17 | * [以爬山演算法尋找函數最高點 - 使用 JavaScript+Node.js 實作](focus2.html) 18 | * [通用的爬山演算法架構 - 使用 JavaScript+Node.js 實作](focus3.html) 19 | * [通用的「模擬退火法」架構 - 使用 JavaScript+Node.js 實作](focus4.html) 20 | * 程式人文集 21 | * [Arduino入門教學(17) – 如何用 Arduino 燒錄 AVR 晶片 (作者:Cooper Maa)](article1.html) 22 | * [使用 OpenCV 實作 AR -- 概念篇 (作者: Heresy Ku )](article2.html) 23 | * [亂數產生器 (作者:Bridan)](article3.html) 24 | * [Visual Basic 6.0:奇數魔術方陣(Odd Magic Square) 詳細解法 (作者:廖憲得 0xde)](article4.html) 25 | * [開放電腦計畫 (11) – 中間碼轉組合語言編譯器:使用 node.js + javascript 實作 (作者:陳鍾誠)](article5.html) 26 | * [Z > b 還是 Z < b (作者:Wush Wu)](article6.html) 27 | * [雜誌訊息](info.html) 28 | 29 | ### 雜誌取得 30 | 31 | 程式人雜誌預定於每個月 1 日出刊,您可以從下列網址取得程式人雜誌的所有內容 (包含當月最新出刊的雜誌)。 32 | 33 | * 34 | 35 | ### 連絡我們 36 | 37 | 竭誠歡迎程式人投稿,或者成為本雜誌的專欄作家,現在就可以加入 [程式人雜誌社團] 一同共襄盛舉。 38 | 39 | 本雜誌編輯為「陳鍾誠 (@ccckmit)」,若要聯絡編輯,請寄信到 。 40 | 41 | [epub]: ../book/A4.epub 42 | [ipad.pdf]: ../book/ipad.pdf 43 | [A4.pdf]: ../book/A4.pdf 44 | [code.zip]: ../code.zip 45 | [pmag.html]: ../book/pmag.html 46 | [github]: https://github.com/programmermagazine/201405 47 | 48 | -------------------------------------------------------------------------------- /htm/editor.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

程式人雜誌 -- 2014 年 5 月號 (開放公益出版品)

14 |
15 |
16 |
17 | 20 |
21 |

編輯小語

22 |

在本期的「程式人雜誌」中,聚焦的主題是「爬山演算法」。

23 |

「爬山演算法」在人工智慧領域是用來做優化 (最佳化) 的一種簡單演算法。這方法雖然簡單,但是卻非常好用,因此我常用「爬山演算法」來解各式各樣的優化問題。而且在「人工智慧」課程時用「爬山演算法」做為第一個學習的優化算法。

24 |

「模擬退火法」則是爬山演算法的一個變形,但在優化的想法上有點不同,爬山演算法顧名思義會不斷的往高處爬,而「模擬退火法」則會不斷的向著能量低的方向走。當我們把爬山的高度轉換成位能函數時,就變成了一種「流水下山演算法」,然後我們再加入用溫度控制的遷移機率之後,可以讓這個演算法有機會跳出谷底,找到另一個更好的解。

25 |

當然、本期不只有「爬山演算法」的相關文章,還有更精彩的 Arduino, VB, OpenCV 與擴增實境, 開放電腦計畫等內容,希望讀者會喜歡這期的「程式人雜誌」!

26 |

---- (程式人雜誌編輯 - 陳鍾誠)

27 |
28 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /source/article3.md: -------------------------------------------------------------------------------- 1 | ## 亂數產生器 (作者:Bridan) 2 | 3 | 想過電腦中亂數產生器如何設計的嗎? 4 | 5 | 大多數亂數產生器採用 [線性同餘法](http://zh.wikipedia.org/w/index.php?title=%E7%B7%9A%E6%80%A7%E5%90%8C%E9%A4%98%E6%96%B9%E6%B3%95&variant=zh-tw) 設計 ( [Linear Congruential Generator, LCG](http://en.wikipedia.org/wiki/Linear_congruential_generator) ),因為方法簡單以及亂數 [均勻分佈](http://zh.wikipedia.org/w/index.php?title=%E5%9D%87%E5%8B%BB%E5%88%86%E4%BD%88&variant=zh-tw) ( [Uniform distribution](http://en.wikipedia.org/wiki/Uniform_distribution) )。 6 | 7 | 其原理為: 8 | 9 | ``` 10 | Xn = a Xn-1 + b 將一正整數乘以 a 常數再加上 b 常數, 11 | 0<= Xn<M 除 M 取餘數,這個 Xn 可重複代入上式計算下一個亂數。 12 | ``` 13 | 14 | 各位可以參考 [EXCEL 檔](https://docs.google.com/spreadsheet/ccc?key=0AvTFWEwZaQ8_dGpvOW96a3kySlh1cWwxeG04QkV2N0E&hl=zh_TW#gid=0) ,我選用 a = 49,b = 0,M = 215 = 32768,C 欄位就是亂數,介於零與一之間,從 E、F 欄位可以看出數值分部非常均勻。 15 | 另外,從 A 欄位會發現,每 2048 筆資料會重複循環,這裡所舉的例子是方便讀者明瞭原理,商用軟體至少用32 bits,並挑選合適的 a b 值,所以很難發現重複性。 16 | 17 | 通常均勻分佈的亂數產生器就夠用,不過與統計有關的程式還需要 [常態分佈](http://zh.wikipedia.org/w/index.php?title=%E5%B8%B8%E6%85%8B%E5%88%86%E4%BD%88&variant=zh-tw) ( [Normal distribution](http://en.wikipedia.org/wiki/Normal_distribution) ) 的亂數產生器,那如何設計呢?還記得 [中央極限定理](http://zh.wikipedia.org/w/index.php?title=%E4%B8%AD%E5%BF%83%E6%9E%81%E9%99%90%E5%AE%9A%E7%90%86&variant=zh-tw) ( [Central limit theorem](http://en.wikipedia.org/wiki/Central_limit_theorem) ) 吧!從未知分佈的母群體中抽樣,只要能計算出這群體平均數 μ 以及有限的變異數 σ2,那麼抽出 n 個隨機樣本取平均值,當 n 趨近無窮大,它的平均值抽樣分配將近似於常態分佈。再以 EXCEL 檔內容為例,一般程式計算時間有限不可能無窮計算下去,因此 **只取連續六筆資料平均再正規化** ,原均勻分佈的 C 欄位值,經過計算後就成為 H 欄位常態分佈的亂數,請參考 J 欄位的機率分佈。 18 | 19 | ![](../img/Normal.gif) 20 | 21 | 另外,常態分佈亂數方法二,請參考 。 22 | 23 | (本文來自「研發養成所」 Bridan 的網誌,原文網址為 ,由陳鍾誠編輯後納入程式人雜誌) 24 | 25 | -------------------------------------------------------------------------------- /htm/license.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

程式人雜誌 -- 2014 年 5 月號 (開放公益出版品)

14 |
15 |
16 |
17 | 20 |
21 |

授權聲明

22 |

本雜誌許多資料修改自維基百科,採用 創作共用:姓名標示、相同方式分享 授權,若您想要修改本書產生衍生著作時,至少應該遵守下列授權條件:

23 |
    24 |
  1. 標示原作者姓名 (包含該文章作者,若有來自維基百科的部份也請一併標示)。
  2. 25 |
  3. 採用 創作共用:姓名標示、相同方式分享 的方式公開衍生著作。
  4. 26 |
27 |

另外、當本雜誌中有文章或素材並非採用 姓名標示、相同方式分享 時,將會在該文章或素材後面標示其授權,此時該文章將以該標示的方式授權釋出,請修改者注意這些授權標示,以避免產生侵權糾紛。

28 |

例如有些文章可能不希望被作為「商業性使用」,此時就可能會採用創作共用:姓名標示、非商業性、相同方式分享 的授權,此時您就不應當將該文章用於商業用途上。

29 |

最後、懇請勿移除公益捐贈的相關描述,以便讓愛心得以持續散播!

30 |
31 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /submit/ZandB.md: -------------------------------------------------------------------------------- 1 | # Z > b 還是 Z < b 2 | 3 | 最近"Z"和"b"的比較鬧得沸沸揚揚,身為R的重度使用者,當然也要來好好的探討一下這個問題。立馬來試試看: 4 | 5 | 6 | ```r 7 | "Z" > "b" 8 | ``` 9 | 10 | ``` 11 | ## [1] TRUE 12 | ``` 13 | 14 | 15 | 沒錯,R語言在這兩者的比較上是支持 *Z大於b* 的! 16 | 17 | 作者我呢也對此抱持著堅定不移的信念,直到某天我在linux上使用crontab跑的R程式出現了Bug。 18 | 19 | 這個Bug非常的隱晦,最困難的地方在於,當我開Rstudio or R console來跑的時候,結果是正確的,但是當我把它丟到crontab下跑的時候,結果卻完全不同。 20 | 21 | 經過層層追尋之後,我發現問題就在 *Z大於b* 上啊。在crontab裡, *Z小於b* ! 22 | 23 | 這是怎麼回事?原來這和locale有關。根據`?Comparison`的描述: 24 | 25 | ``` 26 | The collating sequence of locales such as en_US is normally different from C (which should use ASCII) and can be surprising. 27 | ``` 28 | 29 | 也就是說,當locale(語系)這個環境變數不同的時候,R在做字母的比較是不同的。當語系設定為如"en\_US"或"zh\_TW"的時候,大小順序應是: `A > B > ... > Z > a > b > ... > z`,但是當語系設為"C"的時候,R 會把字元轉成對應[的ASCII](http://en.wikipedia.org/wiki/ASCII)的整數做比較而此時Z是`0x5a`,b是`0x62`,所以Z > b就錯了。 30 | 31 | 眼就為憑,我們馬上做點實驗: 32 | 33 | 34 | 35 | 36 | 37 | ```r 38 | Sys.setlocale(locale = "zh_TW") 39 | ``` 40 | 41 | ``` 42 | ## [1] "zh_TW/zh_TW/zh_TW/C/zh_TW/zh_TW.UTF-8" 43 | ``` 44 | 45 | ```r 46 | "Z" > "b" 47 | ``` 48 | 49 | ``` 50 | ## [1] TRUE 51 | ``` 52 | 53 | ```r 54 | Sys.setlocale(locale = "C") 55 | ``` 56 | 57 | ``` 58 | ## [1] "C/C/C/C/C/zh_TW.UTF-8" 59 | ``` 60 | 61 | ```r 62 | "Z" > "b" 63 | ``` 64 | 65 | ``` 66 | ## [1] FALSE 67 | ``` 68 | 69 | 70 | 71 | 72 | 73 | 而一般我們裝R之後,預設會使用如en\_US或zh\_TW等語系,但是crontab這類環境卻是使用C這個語系。 74 | 75 | 也因此,我們可以得出結論: 76 | 77 | # Z大於b不一定是對的,一切都要看你身處的環境啊! 78 | 79 | 作者 80 | 81 | ## Wush Wu ([wush978@gmail.com](mailto:wush978@gmail.com)) 82 | 83 | - [Taiwan R User Group](https://www.facebook.com/Tw.R.User) Organizer 84 | - R 相關著作: 85 | - [RMessenger](http://cran.r-project.org/web/packages/RMessenger/index.html)的作者 86 | - [RSUS](https://bitbucket.org/wush_iis/rsus),這是[On Shortest Unique Substring Query](http://www.cs.sfu.ca/~jpei/publications/MISQ_ICDE12.pdf)的實作 87 | - 研究領域:Large Scale Learning,[Text Mining](http://www.cs.sfu.ca/~jpei/publications/MISQ_ICDE12.pdf)和[Uncertain Time Series](http://www.cs.sfu.ca/~jpei/publications/Shortest%20Unique%20Substring%20Queries%20ICDE13.pdf) 88 | 89 | -------------------------------------------------------------------------------- /source/article6.md: -------------------------------------------------------------------------------- 1 | ## Z > b 還是 Z < b (作者:Wush Wu) 2 | 3 | 最近"Z"和"b"的比較鬧得沸沸揚揚,身為R的重度使用者,當然也要來好好的探討一下這個問題。立馬來試試看: 4 | 5 | 6 | ```r 7 | "Z" > "b" 8 | ``` 9 | 10 | ``` 11 | ## [1] TRUE 12 | ``` 13 | 14 | 15 | 沒錯,R語言在這兩者的比較上是支持 *Z大於b* 的! 16 | 17 | 作者我呢也對此抱持著堅定不移的信念,直到某天我在linux上使用crontab跑的R程式出現了Bug。 18 | 19 | 這個Bug非常的隱晦,最困難的地方在於,當我開Rstudio or R console來跑的時候,結果是正確的,但是當我把它丟到crontab下跑的時候,結果卻完全不同。 20 | 21 | 經過層層追尋之後,我發現問題就在 *Z大於b* 上啊。在crontab裡, *Z小於b* ! 22 | 23 | 這是怎麼回事?原來這和locale有關。根據`?Comparison`的描述: 24 | 25 | ``` 26 | The collating sequence of locales such as en_US is normally different from C (which should use ASCII) and can be surprising. 27 | ``` 28 | 29 | 也就是說,當locale(語系)這個環境變數不同的時候,R在做字母的比較是不同的。當語系設定為如"en\_US"或"zh\_TW"的時候,大小順序應是: `A > B > ... > Z > a > b > ... > z`,但是當語系設為"C"的時候,R 會把字元轉成對應[的ASCII](http://en.wikipedia.org/wiki/ASCII)的整數做比較而此時Z是`0x5a`,b是`0x62`,所以Z > b就錯了。 30 | 31 | 眼就為憑,我們馬上做點實驗: 32 | 33 | 34 | 35 | 36 | 37 | ```r 38 | Sys.setlocale(locale = "zh_TW") 39 | ``` 40 | 41 | ``` 42 | ## [1] "zh_TW/zh_TW/zh_TW/C/zh_TW/zh_TW.UTF-8" 43 | ``` 44 | 45 | ```r 46 | "Z" > "b" 47 | ``` 48 | 49 | ``` 50 | ## [1] TRUE 51 | ``` 52 | 53 | ```r 54 | Sys.setlocale(locale = "C") 55 | ``` 56 | 57 | ``` 58 | ## [1] "C/C/C/C/C/zh_TW.UTF-8" 59 | ``` 60 | 61 | ```r 62 | "Z" > "b" 63 | ``` 64 | 65 | ``` 66 | ## [1] FALSE 67 | ``` 68 | 69 | 70 | 71 | 72 | 73 | 而一般我們裝R之後,預設會使用如en\_US或zh\_TW等語系,但是crontab這類環境卻是使用C這個語系。 74 | 75 | 也因此,我們可以得出結論: 76 | 77 | > Z大於b不一定是對的,一切都要看你身處的環境啊! 78 | 79 | ### 作者 : Wush Wu ([wush978@gmail.com](mailto:wush978@gmail.com)) 80 | 81 | - [Taiwan R User Group](https://www.facebook.com/Tw.R.User) Organizer 82 | - R 相關著作: 83 | - [RMessenger](http://cran.r-project.org/web/packages/RMessenger/index.html)的作者 84 | - [RSUS](https://bitbucket.org/wush_iis/rsus),這是[On Shortest Unique Substring Query](http://www.cs.sfu.ca/~jpei/publications/MISQ_ICDE12.pdf)的實作 85 | - 研究領域:Large Scale Learning,[Text Mining](http://www.cs.sfu.ca/~jpei/publications/MISQ_ICDE12.pdf)和[Uncertain Time Series](http://www.cs.sfu.ca/~jpei/publications/Shortest%20Unique%20Substring%20Queries%20ICDE13.pdf) 86 | 87 | -------------------------------------------------------------------------------- /code/js/as1.js: -------------------------------------------------------------------------------- 1 | var c = require("./ccc"); 2 | var as = require("./as"); 3 | var code = require("./code"); 4 | var cpu1 = require("./cpu1"); 5 | 6 | var as1 = new as(cpu1.opTable); 7 | 8 | as1.parse = function(line) { 9 | return new code(line, this.opTable); 10 | } 11 | 12 | as1.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; 16 | var labelCode = null; 17 | switch (code.op.type) { 18 | case "N": 19 | code.obj = c.hex(code.op.id, 2) + c.hex(0, 6); 20 | break; 21 | case "C": 22 | ra = parseR(args[0]); 23 | rb = parseR(args[1]); 24 | cx = parseInt(args[2]); 25 | code.obj = c.hex(code.op.id, 2)+c.hex(ra, 1)+c.hex(rb, 1)+c.hex(cx, 4); 26 | break; 27 | case "L": 28 | ra = parseR(args[0]); 29 | rb = 15; 30 | labelCode = this.symTable[args[1]]; 31 | cx = labelCode.address - pc; 32 | code.obj = c.hex(code.op.id, 2)+c.hex(ra, 1)+c.hex(rb, 1)+c.hex(cx, 4); 33 | break; 34 | case "D": 35 | var unitSize = 1; 36 | switch (code.op.name) { 37 | case "RESW": // 如果是 RESW 38 | case "RESB": // 或 RESB 39 | code.obj = c.dup('0', code.size()*2); 40 | break; 41 | case "WORD": // 如果是 WORD: 42 | unitSize = 4; 43 | case "BYTE": { // 如果是 BYTE : 輸出格式為 %2x 44 | code.obj = ""; 45 | for (var i in args) { 46 | if (args[i].match(/\d+/)) // 常數 47 | code.obj += c.hex(parseInt(args[i]), unitSize*2); 48 | else { // 標記 49 | labelCode = symTable[args[i]]; 50 | code.obj += c.hex(labelCode.address, unitSize*2); 51 | } 52 | } 53 | break; 54 | } // case BYTE: 55 | } // switch 56 | } // case "D" 57 | } 58 | 59 | as1.assemble(process.argv[2], process.argv[3]); 60 | -------------------------------------------------------------------------------- /source/article4.md: -------------------------------------------------------------------------------- 1 | ## Visual Basic 6.0:奇數魔術方陣(Odd Magic Square) 詳細解法 (作者:廖憲得 0xde) 2 | 3 | 什麼是奇數魔術方陣 (?) 4 | 5 | > 魔術方陣是許多人想要解決的一個古老的數學問題,您可能在一些雜誌上看過,也可能您的老師有介紹過。一個魔術方陣是在於安排數字在一矩陣[n x n]中,從1到n2, 每一數字僅出現一次, 而且,任一列、任一行或任一對角線的總和都相同。求總和的公式要證明為n [ ( n2 + 1) / 2 ],並不是很困難,若我們利用這個公式,對[5x5]矩陣而言,其總和為5 [ ( 52 + 1 ) / 2 ] = 65,其對應的魔術方陣輸出如下: 6 | 7 | ![](../img/MagicSquare.jpg) 8 | 9 | ![](../img/MagicSquare2.jpg) 10 | 11 | ```monobasic 12 | '# [Visual Basic 6.0] 奇數魔術方陣(Odd Magic Square) 13 | '# 0xDe 14 | Dim InputN 15 | Dim Squate() 16 | Private Sub Form_Activate() 17 | '------------------------------ 18 | InputN = 3 ' 輸入 (必須為奇數) 19 | '------------------------------ 20 | 21 | 22 | '------------------------------ 23 | If InputN Mod 2 = 0 Then Exit Sub ' 判斷是否為奇數 24 | '------------------------------ 25 | ReDim Square(InputN - 1, InputN - 1) 26 | '------------------------------ 27 | Print "N= " & InputN & "的奇數魔術方陣" & vbCrLf 28 | Randomize Timer ' 亂數產生 29 | TempX = Int(Rnd * InputN) ' 隨機起始 X 30 | TempY = Int(Rnd * InputN) ' 隨機起始 Y 31 | '------------------------------ 32 | Do Until N = (InputN ^ 2) ' 直到放滿 33 | If Square(TempX, TempY) = "" Then 34 | N = N + 1 35 | Square(TempX, TempY) = N 36 | 37 | TempX = TempX - 1 ' 向上移 38 | If TempX < 0 Then TempX = InputN - 1 39 | TempY = TempY + 1 ' 向右移 40 | If TempY > InputN - 1 Then TempY = 0 41 | Else 42 | ' 恢復原本的狀態往下 43 | TempX = TempX + 1 44 | If TempX > InputN - 1 Then TempX = 0 45 | TempY = TempY - 1 46 | If TempY < 0 Then TempY = InputN - 1 47 | ' 往下 48 | TempX = TempX + 1 49 | If TempX > InputN - 1 Then TempX = 0 50 | End If 51 | Loop 52 | '------------------------------ 53 | For I = 0 To InputN - 1 ' 將結果輸出 54 | For J = 0 To InputN - 1 55 | Print Square(I, J); 56 | Next J 57 | Print 58 | Next I 59 | '------------------------------ 60 | End Sub 61 | ``` 62 | 63 | * 原始碼下載: [Visual Basic 6.0:奇數魔術方陣(Odd Magic Square).rar](http://files.dotblogs.com.tw/0xde/1311/20131113153918434.rar) 64 | 65 | 【本文作者為「廖憲得」,原文網址為: ,由陳鍾誠編輯後納入本雜誌】 66 | -------------------------------------------------------------------------------- /source/info.md: -------------------------------------------------------------------------------- 1 | # 雜誌訊息 2 | 3 | ## 讀者訂閱 4 | 程式人雜誌是一個結合「開放原始碼與公益捐款活動」的雜誌,簡稱「開放公益雜誌」。開放公益雜誌本著「讀書做善事、寫書做公益」的精神,我們非常歡迎程式人認養專欄、或者捐出您的網誌,如果您願意成為本雜誌的專欄作家,請加入 [程式人雜誌社團] 一同共襄盛舉。 5 | 6 | 我們透過發行這本雜誌,希望讓大家可以讀到想讀的書,學到想學的技術,同時也讓寫作的朋友的作品能產生良好價值 – 那就是讓讀者根據雜誌的價值捐款給慈善團體。 7 | 讀雜誌做公益也不需要有壓力,您不需要每讀一本就急著去捐款,您可以讀了十本再捐,或者使用固定的月捐款方式,當成是雜誌訂閱費,或者是季捐款、一年捐一次等都 OK ! 甚至是單純當個讀者我們也都很歡迎! 8 | 9 | 本雜誌每期參考價:NT 50 元,如果您喜歡本雜誌,請將書款捐贈公益團體。例如可捐贈給「羅慧夫顱顏基金會 彰化銀行(009) 帳號:5234-01-41778-800」。(若匯款要加註可用「程式人雜誌」五個字) 10 | 11 | ## 投稿須知 12 | 13 | *給專欄寫作者:* 做公益不需要有壓力。如果您願意撰寫專欄,您可以輕鬆的寫,如果當月的稿件出不來,我們會安排其他稿件上場。 14 | 15 | *給網誌捐贈者:* 如果您沒時間寫專欄或投稿,沒關係,只要將您的網誌以 [創作共用的「姓名標示、非商業性、相同方式分享」授權] 並通知我們,我們會自動從中選取需要的文章進行編輯,放入適當的雜誌當中出刊。 16 | 17 | *給文章投稿者:* 程式人雜誌非常歡迎您加入作者的行列,如果您想撰寫任何文章或投稿,請用 markdown 或 LibreOffice 編輯好您的稿件,並於每個月 25 日前投稿到[程式人雜誌社團] 的檔案區,我們會盡可能將稿件編入隔月1號出版程式人雜誌當中,也歡迎您到社團中與我們一同討論。 18 | 19 | 如果您要投稿給程式人雜誌,我們最希望的格式是採用 markdown 的格式撰寫,然後將所有檔按壓縮為 zip 上傳到社團檔案區給我們, 如您想學習 markdown 的撰寫出版方式,可以參考 [看影片學 markdown 編輯出版流程] 一文。 20 | 21 | 如果您無法採用 markdown 的方式撰寫,也可以直接給我們您的稿件,像是 MS. Word 的 doc 檔或 LibreOffice 的 odt 檔都可以,我們 22 | 會將這些稿件改寫為 markdown 之後編入雜誌當中。 23 | 24 | ## 參與編輯 25 | 您也可以擔任程式人雜誌的編輯,甚至創造一個全新的公益雜誌,我們誠摯的邀請您加入「開放公益出版」的行列,如果您想擔任編輯或創造新雜誌,也歡迎到 [程式人雜誌社團] 來與我們討論相關事宜。 26 | 27 | ## 公益資訊 28 | 29 | ------------------------------------------------------------------------------------------------------------------------------------------------------------ 30 | 公益團體 聯絡資訊 服務對象 捐款帳號 31 | ------------------------------- ----------------------------- ----------------------------------------- ------------------------------------------- 32 | 財團法人羅慧夫顱顏基金會
顱顏患者 銀行:009彰化銀行民生分行
33 |
(如唇顎裂、小耳症或其他罕見顱顏缺陷) 帳號:5234-01-41778-800 34 | 02-27190408分機 232 35 | 36 | 社團法人台灣省兒童少年成長協會
單親、隔代教養.弱勢及一般家庭之兒童青少年 銀行:新光銀行
37 |
戶名:台灣省兒童少年成長協會
38 | 04-23058005 帳號:103-0912-10-000212-0 39 | ------------------------------- ----------------------------- ----------------------------------------- ------------------------------------------- 40 | 41 | [看影片學 markdown 編輯出版流程]:https://dl.dropboxusercontent.com/u/101584453/pmag/201304/htm/video1.html 42 | -------------------------------------------------------------------------------- /htm/discuss1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

程式人雜誌 -- 2014 年 5 月號 (開放公益出版品)

14 |
15 |
16 |
17 | 20 |
21 |

討論:本月討論精選

22 | 39 |

參考文獻

40 |
41 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /source/focus2.md0: -------------------------------------------------------------------------------- 1 | ## 以爬山演算法尋找函數最高點 - 使用 JavaScript+Node.js 實作 2 | 3 | ### 簡介 4 | 5 | 以下是「爬山演算法」 (Hill-Climbing Algorithm) 的一個簡易版本,其方法超簡單,就是一直看旁邊有沒有更好的解,如果有就移過去。然後反覆的作這樣的動作,直到旁邊的解都比現在的更差時,程式就停止,然後將那個位於山頂的解傳回,就完成了。 6 | 7 | ``` 8 | Algorithm HillClimbing(f, x) 9 | x = 隨意設定一個解。 10 | while (x 有鄰居 x' 比 x 更高) 11 | x = x'; 12 | end 13 | return x; 14 | end 15 | ``` 16 | 17 | 當然、這種演算法只能找到「局部最佳解」(local optimal),當整個空間有很多山頂的時候,這種方法會爬到其中一個山頂就停了,並不一定會爬到最高的山頂。 18 | 19 | ### 程式碼 20 | 21 | 檔案: HillClimbingSimple.js 22 | 23 | ```javascript 24 | var util = require("util"); 25 | var log = console.log; 26 | 27 | function f(x) { return -1*(x*x+3*x+5); } 28 | // function f(x) { return -1*Math.abs(x*x-4); } 29 | 30 | var dx = 0.01; 31 | 32 | function hillClimbing(f, x) { 33 | while (true) { 34 | log("f(%s)=%s", x.toFixed(4), f(x).toFixed(4)); 35 | if (f(x+dx) >= f(x)) 36 | x = x+dx; 37 | else if (f(x-dx) >= f(x)) 38 | x = x-dx; 39 | else 40 | break; 41 | } 42 | } 43 | 44 | hillClimbing(f, 0.0); 45 | ``` 46 | 47 | ### 執行結果 48 | 49 | 求解 : $-(x^2+3x+5)$ 的最高點,也就是 $x^2+3x+5$ 的最低點。 50 | 51 | ``` 52 | D:\Dropbox\Public\web\ai\code\optimize>node hillClimbingSimple 53 | f(0.0000)=-5.0000 54 | f(-0.0100)=-4.9701 55 | f(-0.0200)=-4.9404 56 | f(-0.0300)=-4.9109 57 | f(-0.0400)=-4.8816 58 | f(-0.0500)=-4.8525 59 | ... 60 | f(-1.4500)=-2.7525 61 | f(-1.4600)=-2.7516 62 | f(-1.4700)=-2.7509 63 | f(-1.4800)=-2.7504 64 | f(-1.4900)=-2.7501 65 | f(-1.5000)=-2.7500 66 | ``` 67 | 68 | 如果我們將上述程式的 f(x) 換成註解中的那個,也就是將 f(x) 換成如下版本: 69 | 70 | ``` 71 | function f(x) { return -1*Math.abs(x*x-4); } 72 | ``` 73 | 74 | 那麼就可以用來求解 $|x^2-4|$ 的最低點,也就是尋找 4 的平方根,以下是執行結果: 75 | 76 | ``` 77 | D:\Dropbox\Public\web\ai\code\optimize>node hillClimbingSimple 78 | f(0.0000)=-4.0000 79 | f(0.0100)=-3.9999 80 | f(0.0200)=-3.9996 81 | f(0.0300)=-3.9991 82 | f(0.0400)=-3.9984 83 | f(0.0500)=-3.9975 84 | ... 85 | f(1.9500)=-0.1975 86 | f(1.9600)=-0.1584 87 | f(1.9700)=-0.1191 88 | f(1.9800)=-0.0796 89 | f(1.9900)=-0.0399 90 | f(2.0000)=-0.0000 91 | ``` 92 | 93 | 您可以看到上述程式正確的找到 4 的平方根是 2,而我們所用的方法與求解 $-(x^2+3x+5)$ 的最高點幾乎是一模一樣的,只是把函數換掉而已。 94 | 95 | ### 結語 96 | 97 | 您可以看到上述用爬山演算法尋找函數最高點或最低點的程式,其實非常的簡單,只不過是看看兩邊是否有更好的解,如果有就移過去罷了。 98 | 99 | 但是、這麼簡單的演算法,其實威力是非常強大的,這種方法可以求解的問題非常的多,很多人工智慧上非常重要的問題,其實都只不過是在進行函數優化的動作,也就是尋找某個函數的低點或高點而已,這些問題其實大部分都可以使用爬山演算法來求解。 100 | 101 | 當然、要能尋找更複雜函數的「區域最佳解」,還必須進一步的對上述程式進行封裝與抽象化,我們將在下一篇文章中解說將上述爬山程式抽象化後的版本,並用該程式來求更複雜函數的解。 102 | 103 | ### 參考文獻 104 | * [Wikipedia:Hill climbing](http://en.wikipedia.org/wiki/Hill-climbing) 105 | * [維基百科:爬山演算法](http://zh.wikipedia.org/wiki/%E7%88%AC%E5%B1%B1%E7%AE%97%E6%B3%95) 106 | 107 | 【本文由陳鍾誠取材並修改自 [維基百科],採用創作共用的 [姓名標示、相同方式分享] 授權】 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /source/focus2.md: -------------------------------------------------------------------------------- 1 | ## 以爬山演算法尋找函數最高點 - 使用 JavaScript+Node.js 實作 2 | 3 | ### 簡介 4 | 5 | 以下是「爬山演算法」 (Hill-Climbing Algorithm) 的一個簡易版本,其方法超簡單,就是一直看旁邊有沒有更好的解,如果有就移過去。然後反覆的作這樣的動作,直到旁邊的解都比現在的更差時,程式就停止,然後將那個位於山頂的解傳回,就完成了。 6 | 7 | ``` 8 | Algorithm HillClimbing(f, x) 9 | x = 隨意設定一個解。 10 | while (x 有鄰居 x' 比 x 更高) 11 | x = x'; 12 | end 13 | return x; 14 | end 15 | ``` 16 | 17 | 當然、這種演算法只能找到「局部最佳解」(local optimal),當整個空間有很多山頂的時候,這種方法會爬到其中一個山頂就停了,並不一定會爬到最高的山頂。 18 | 19 | ### 程式碼 20 | 21 | 檔案: HillClimbingSimple.js 22 | 23 | ```javascript 24 | var util = require("util"); 25 | var log = console.log; 26 | 27 | function f(x) { return -1*(x*x+3*x+5); } 28 | // function f(x) { return -1*Math.abs(x*x-4); } 29 | 30 | var dx = 0.01; 31 | 32 | function hillClimbing(f, x) { 33 | while (true) { 34 | log("f(%s)=%s", x.toFixed(4), f(x).toFixed(4)); 35 | if (f(x+dx) >= f(x)) 36 | x = x+dx; 37 | else if (f(x-dx) >= f(x)) 38 | x = x-dx; 39 | else 40 | break; 41 | } 42 | } 43 | 44 | hillClimbing(f, 0.0); 45 | ``` 46 | 47 | ### 執行結果 48 | 49 | 求解 : ![](../timg/489e5db6d4d6.jpg) 的最高點,也就是 ![](../timg/b2a89b129682.jpg) 的最低點。 50 | 51 | ``` 52 | D:\Dropbox\Public\web\ai\code\optimize>node hillClimbingSimple 53 | f(0.0000)=-5.0000 54 | f(-0.0100)=-4.9701 55 | f(-0.0200)=-4.9404 56 | f(-0.0300)=-4.9109 57 | f(-0.0400)=-4.8816 58 | f(-0.0500)=-4.8525 59 | ... 60 | f(-1.4500)=-2.7525 61 | f(-1.4600)=-2.7516 62 | f(-1.4700)=-2.7509 63 | f(-1.4800)=-2.7504 64 | f(-1.4900)=-2.7501 65 | f(-1.5000)=-2.7500 66 | ``` 67 | 68 | 如果我們將上述程式的 f(x) 換成註解中的那個,也就是將 f(x) 換成如下版本: 69 | 70 | ``` 71 | function f(x) { return -1*Math.abs(x*x-4); } 72 | ``` 73 | 74 | 那麼就可以用來求解 ![](../timg/b2cb861ede12.jpg) 的最低點,也就是尋找 4 的平方根,以下是執行結果: 75 | 76 | ``` 77 | D:\Dropbox\Public\web\ai\code\optimize>node hillClimbingSimple 78 | f(0.0000)=-4.0000 79 | f(0.0100)=-3.9999 80 | f(0.0200)=-3.9996 81 | f(0.0300)=-3.9991 82 | f(0.0400)=-3.9984 83 | f(0.0500)=-3.9975 84 | ... 85 | f(1.9500)=-0.1975 86 | f(1.9600)=-0.1584 87 | f(1.9700)=-0.1191 88 | f(1.9800)=-0.0796 89 | f(1.9900)=-0.0399 90 | f(2.0000)=-0.0000 91 | ``` 92 | 93 | 您可以看到上述程式正確的找到 4 的平方根是 2,而我們所用的方法與求解 ![](../timg/489e5db6d4d6.jpg) 的最高點幾乎是一模一樣的,只是把函數換掉而已。 94 | 95 | ### 結語 96 | 97 | 您可以看到上述用爬山演算法尋找函數最高點或最低點的程式,其實非常的簡單,只不過是看看兩邊是否有更好的解,如果有就移過去罷了。 98 | 99 | 但是、這麼簡單的演算法,其實威力是非常強大的,這種方法可以求解的問題非常的多,很多人工智慧上非常重要的問題,其實都只不過是在進行函數優化的動作,也就是尋找某個函數的低點或高點而已,這些問題其實大部分都可以使用爬山演算法來求解。 100 | 101 | 當然、要能尋找更複雜函數的「區域最佳解」,還必須進一步的對上述程式進行封裝與抽象化,我們將在下一篇文章中解說將上述爬山程式抽象化後的版本,並用該程式來求更複雜函數的解。 102 | 103 | ### 參考文獻 104 | * [Wikipedia:Hill climbing](http://en.wikipedia.org/wiki/Hill-climbing) 105 | * [維基百科:爬山演算法](http://zh.wikipedia.org/wiki/%E7%88%AC%E5%B1%B1%E7%AE%97%E6%B3%95) 106 | 107 | 【本文由陳鍾誠取材並修改自 [維基百科],採用創作共用的 [姓名標示、相同方式分享] 授權】 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /code/js/expc.js: -------------------------------------------------------------------------------- 1 | var c = require("./ccc"); 2 | 3 | var symTable = {}; 4 | 5 | var tempIdx = 0; 6 | var nextTemp=function() { return "T"+tempIdx++; } 7 | 8 | var stack = []; 9 | var push=function(o) { stack.push(o); } 10 | var pop=function() { return stack.pop(); } 11 | 12 | var tokens = []; 13 | var tokenIdx = 0; 14 | // 本來應該用 .*? 來比對 /*...*/ 註解的,但 javascript 的 . 並不包含 \n, 因此用 \s\S 代替 . 就可以了。 15 | var retok = /(\/\*[\s\S]*?\*\/)|(\/\/[^\r\n])|(\d+)|([a-zA-Z]\w*)|(\r?\n)|(.)/gm; // *?, +? non greedy, m for multiline 16 | 17 | var source = ""; 18 | var scan=function(text) { 19 | tokenIdx = 0; 20 | tokens = text.match(retok); 21 | return tokens; 22 | } 23 | 24 | var error=function(o) { c.printf("Error: %j\n", o); } 25 | var pcode = function(op, t, t1, t2) { c.printf("%s %s %s %s\n", op, t, t1, t2); } 26 | 27 | var next=function(o) { 28 | if (isNext(o)) 29 | return tokens[tokenIdx++]; 30 | error(token); 31 | } 32 | 33 | var isNext=function(o) { 34 | if (tokenIdx >= tokens.length) 35 | return false; 36 | var token = tokens[tokenIdx]; 37 | if (o instanceof RegExp) { 38 | return token.match(o); 39 | } else { 40 | return (token == o); 41 | } 42 | } 43 | 44 | var compile=function(text) { 45 | scan(text); 46 | c.printf("text=%s\n", text); 47 | c.printf("tokens=%j\n", tokens); 48 | E(); 49 | c.printf("symTable=%j\n", symTable); 50 | } 51 | 52 | var E=function() { // E=T ([+-] T)* 53 | push('E'); 54 | t1 = T(); 55 | while (isNext(/[\+\-]/)) { 56 | op = next(/[\+\-]/); 57 | t = nextTemp(); 58 | t2 = T(); 59 | pcode(op, t, t1, t2); 60 | t1 = t; 61 | } 62 | pop('E'); 63 | return t1; 64 | } 65 | 66 | var T=function() { // T=F ([*/] F)* 67 | push('T'); 68 | f1 = F(); 69 | while (isNext(/[\*\/]/)) { 70 | op = next(/[*\/]/); 71 | f2 = F(); 72 | f = nextTemp(); 73 | pcode(op, f, f1, f2); 74 | f1 = f; 75 | } 76 | pop('T'); 77 | return f1; 78 | } 79 | 80 | var reNumber = /\d+/; 81 | var reId = /[a-zA-Z]\w*/; 82 | 83 | var F=function() { // F=NUMBER | ID | ( E ) 84 | push('F'); 85 | if (isNext("(")) { 86 | next("("); 87 | f = E(); 88 | next(")"); 89 | } else if (isNext(reNumber)) { 90 | number = next(reNumber); 91 | f = nextTemp(); 92 | pcode("=", f, number, ""); 93 | } else if (isNext(reId)) { 94 | f = id = next(reId); 95 | symTable[id] = "ID"; 96 | } 97 | pop('F'); 98 | return f; 99 | } 100 | 101 | c.printf("=== EBNF Grammar =====\n"); 102 | c.printf("E=T ([+-] T)*\n"); 103 | c.printf("T=F ([*/] F)*\n"); 104 | c.printf("F=NUMBER | ID | '(' E ')'\n"); 105 | compile("32+5*(182+degree*4-20)"); 106 | // printf("typeof(/\w+/)=%s /\w+/ instanceof RegExp=%s\n", typeof(/\w+/), /\w+/ instanceof RegExp); 107 | // printf("typeof(str)=%s\n", typeof("str")); 108 | -------------------------------------------------------------------------------- /htm/people2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

程式人雜誌 -- 2014 年 5 月號 (開放公益出版品)

14 |
15 |
16 | 21 |

偉大「數學家」的悲慘人生 -- 圖靈、牛頓、哥德爾

22 |

從前我總認為,那些在教科書中提到的偉大數學家,他們必然是備受推崇,令人尊敬的。因此他們應該都是屬於「人生勝利組」的那群人,但是後來,我發現我錯了!

23 |

圖靈 (Alan Turing) 應該是「電腦領域」最為人所之的偉大數學家了吧,但是如果你知道圖靈怎麼死的,那應該會覺得很驚訝且難過,因為圖靈在 1952 年由於同性戀而受審,結果在兩年後吃了一顆塗有「氰化物」的蘋果死了。

24 |

而物理學之父「牛頓」應該算是人生較為順利的了,他還創建了「微積分」這門重要的學問,而且曾經擔任英國皇家鑄幣局局長,擔任局長讓他的薪水大增,並且因為表現優秀,還被英國女皇授予「艾薩克爵士」的貴族稱號,這應該算是人生勝利組了吧?

25 |

但是在牛頓的生命中,除了關心物理與數學之外,他其實很著迷於「鍊金術」這門學問,他在鑄幣局局長任內,將黃金價格定為「每金衡盎司等於三英鎊十七先令10.5便士,讓英鎊成為金本位貨幣。

26 |

但也因為迷戀黃金的緣故,讓牛頓用個人儲蓄大舉投資「南海公司」,結果到了 1720 年暴發了「南海泡沫」事件,南海公司的股價從 1,000英鎊狂跌到 190 英磅以下,結果牛頓損失了超過兩萬英鎊的美元 (這相當於牛頓十幾年的鑄幣局長薪水),於是他說了下列這句經典名言:

27 |
28 |

我能精準計算天體的運行規律,卻無法預測人類行為的瘋狂。

29 |

I can calculate the motions of heavenly bodies, but not the madness of people.

30 |
31 |

而且、在牛頓死後,他的身體內發現了大量水銀,這很可能是研究鍊金術導致汞中毒,因此也讓牛頓晚年的一些怪異行徑得到了解釋。

32 |

而「哥德爾」這位二十世紀的傳奇數學家,他的人生也同樣並不精彩,而且可以說是充滿悲劇的!

33 |

「哥德爾」自幼多病,而且從小就患了強迫症,後來還得了憂鬱症,並且自殺過幾次。

34 |

在哥德爾的晚年,他疑神疑鬼,由於他認為別人給的飯菜有毒,因此拒絕吃其他人給的飯菜,只相信他太太 Adele Nimbursky 給的,但是後來他的太太也病倒了,結果最後哥德爾死於營養不良與進食不足,死時體重只有 65 磅,享年 72 歲。

35 |

參考文獻

36 | 41 |

【本文由陳鍾誠取材並修改自 維基百科,採用創作共用的 姓名標示、相同方式分享 授權】

42 |
43 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /code/js/vm1.js: -------------------------------------------------------------------------------- 1 | var c = require("./ccc"); 2 | var cpu1 = require("./cpu1"); 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, 14, 0, 16]; 10 | m = new Memory(1); 11 | m.load(objFile); 12 | var stop = false; 13 | while (!stop) { // 如果尚未結束 14 | var tpc = R[PC]; 15 | R[0] = 0; // R[0] 永遠為 0 16 | R[IR] = m.geti(R[PC]); // 指令擷取,IR=[PC..PC+3] 17 | R[PC] += 4; // 擷取完將 PC 加 4,指向下一個指令 18 | var op = c.bits(R[IR], 24, 31); // 取得 op 欄位,IR[24..31] 19 | var ra = c.bits(R[IR], 20, 23); // 取得 ra 欄位,IR[20..23] 20 | var rb = c.bits(R[IR], 16, 19); // 取得 rb 欄位,IR[16..19] 21 | var c16= c.signbits(R[IR], 0, 15); // 取得 16 位元的 cx 22 | var c5 = c.bits(R[IR], 0, 4); 23 | var addr = R[rb]+c16; 24 | switch (op) { // 根據op執行動作 25 | case ID("LD") : R[ra] = m.geti(addr); break; // 處理 LD 指令 26 | case ID("ST") : m.seti(addr, R[ra]); c.log("m[%s]=%s", c.hex(addr,4), m.geti(addr)); break; // 處理 ST 指令 27 | case ID("LDB"): R[ra] = m.getb(addr); break; // 處理 LDB 指令 28 | case ID("STB"): m.setb(addr, R[ra]); break; // 處理 STB 指令 29 | case ID("ADDI"):R[ra] = R[rb] + c16; break; // 處理 ADDI 指令 30 | case ID("ADD"): R[ra] += m.geti(addr); break; // 處理ADD指令 31 | case ID("SUB"): R[ra] -= m.geti(addr); break; // 處理SUB指令 32 | case ID("MUL"): R[ra] *= m.geti(addr); break; // 處理MUL指令 33 | case ID("DIV"): R[ra] /= m.geti(addr); break; // 處理DIV指令 34 | case ID("AND"): R[ra] &= m.geti(addr); break; // 處理AND指令 35 | case ID("OR") : R[ra] |= m.geti(addr); ; break; // 處理OR指令 36 | case ID("XOR"): R[ra] ^= m.geti(addr); ; break; // 處理XOR指令 37 | case ID("SHL"): R[ra] = R[rb]<>c5; break; // 處理SHR指令 39 | case ID("JZ") : if (R[ra]==0) R[PC] = R[rb]+c16; break; // 處理JZ指令 40 | case ID("PUSH"):R[SP]-=4; R[ra]=m.geti(addr); m.seti(R[SP], R[ra]); break; // 處理PUSH指令 41 | case ID("POP"): R[ra] = m.geti(R[SP]); R[SP]+=4; break; // 處理POP指令 42 | case ID("PUSHB"):R[SP]--; R[ra]=m.getb(addr); m.setb(R[SP], R[ra]); break; // 處理PUSH指令 43 | case ID("POPB"):R[ra] = m.getb(R[SP]); R[SP]++; break; // 處理POPB指令 44 | case ID("RET"): stop=true; R[PC]=R[LR]; break; // 處理RET指令 45 | default: c.log("Error:invalid op (%s)", hex(op)); 46 | } // switch 47 | c.log("PC=%s IR=%s SW=%s R[%s]=0x%s=%d", // 印出 PC, IR, R[ra]暫存器的值,以利觀察 48 | 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]); 49 | } // while 50 | } 51 | 52 | run(process.argv[2]); 53 | -------------------------------------------------------------------------------- /htm/article3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

程式人雜誌 -- 2014 年 5 月號 (開放公益出版品)

14 |
15 |
16 |
17 | 20 |
21 |

亂數產生器 (作者:Bridan)

22 |

想過電腦中亂數產生器如何設計的嗎?

23 |

大多數亂數產生器採用 線性同餘法 設計 ( Linear Congruential Generator, LCG ),因為方法簡單以及亂數 均勻分佈 ( Uniform distribution )。

24 |

其原理為:

25 |
Xn = a Xn-1 + b     將一正整數乘以 a 常數再加上 b 常數,
26 | 0<= Xn<M           除 M 取餘數,這個 Xn 可重複代入上式計算下一個亂數。
27 |

各位可以參考 EXCEL 檔 ,我選用 a = 49,b = 0,M = 215 = 32768,C 欄位就是亂數,介於零與一之間,從 E、F 欄位可以看出數值分部非常均勻。 另外,從 A 欄位會發現,每 2048 筆資料會重複循環,這裡所舉的例子是方便讀者明瞭原理,商用軟體至少用32 bits,並挑選合適的 a b 值,所以很難發現重複性。

28 |

通常均勻分佈的亂數產生器就夠用,不過與統計有關的程式還需要 常態分佈 ( Normal distribution ) 的亂數產生器,那如何設計呢?還記得 中央極限定理 ( Central limit theorem ) 吧!從未知分佈的母群體中抽樣,只要能計算出這群體平均數 μ 以及有限的變異數 σ2,那麼抽出 n 個隨機樣本取平均值,當 n 趨近無窮大,它的平均值抽樣分配將近似於常態分佈。再以 EXCEL 檔內容為例,一般程式計算時間有限不可能無窮計算下去,因此 只取連續六筆資料平均再正規化 ,原均勻分佈的 C 欄位值,經過計算後就成為 H 欄位常態分佈的亂數,請參考 J 欄位的機率分佈。

29 |
30 | 31 |
32 |

另外,常態分佈亂數方法二,請參考 http://4rdp.blogspot.com/2008/06/random-variable-of-normal-distribution.html

33 |

(本文來自「研發養成所」 Bridan 的網誌,原文網址為 http://4rdp.blogspot.tw/2008/06/blog-post_14.html ,由陳鍾誠編輯後納入程式人雜誌)

34 |
35 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /code/js/test.as0: -------------------------------------------------------------------------------- 1 | // arg 10 2 | LD R1 10 3 | PUSH R1 4 | // call T1 sum 5 | CALL sum 6 | ST R1 T1 7 | // = s T1 8 | LD R1 T1 9 | ST R1 s 10 | // sum function 11 | sum 12 | // param n 13 | POP R1 14 | ST R1 n 15 | // = s 0 16 | LD R1 0 17 | ST R1 s 18 | // = i 1 19 | LD R1 1 20 | ST R1 i 21 | // L1 22 | L1 23 | // <= T2 i 10 24 | LD R1 i 25 | LD R2 10 26 | CMP R1 R2 27 | JLE CSET0 28 | LDI R1 1 29 | JMP CEXIT0 30 | CSET0 LDI R1 0 31 | CEXIT0 ST R1 T2 32 | // if0 T2 L2 33 | CMP R0 T2 34 | JEQ L2 35 | // + T3 s i 36 | LD R1 s 37 | LD R2 i 38 | ADD R3 R1 R2 39 | ST R3 T3 40 | // = s T3 41 | LD R1 T3 42 | ST R1 s 43 | // ++ i 44 | LDI R1 1 45 | LD R2 i 46 | ADD R2 R1 R2 47 | ST R2 i undefined 48 | // goto L1 49 | JMP L1 50 | // L2 51 | L2 52 | // return s 53 | LD R1 s 54 | RET 55 | // endf 56 | // arg 3 57 | LD R1 3 58 | PUSH R1 59 | // arg 5 60 | LD R1 5 61 | PUSH R1 62 | // call T4 max 63 | CALL max 64 | ST R1 T4 65 | // = m T4 66 | LD R1 T4 67 | ST R1 m 68 | // max function 69 | max 70 | // param a 71 | POP R1 72 | ST R1 a 73 | // param b 74 | POP R1 75 | ST R1 b 76 | // > T5 a b 77 | LD R1 a 78 | LD R2 b 79 | CMP R1 R2 80 | JGT CSET0 81 | LDI R1 1 82 | JMP CEXIT0 83 | CSET0 LDI R1 0 84 | CEXIT0 ST R1 T5 85 | // if0 T5 L3 86 | CMP R0 T5 87 | JEQ L3 88 | // return a 89 | LD R1 a 90 | RET 91 | // L3 92 | L3 93 | // return b 94 | LD R1 b 95 | RET 96 | // endf 97 | // total function 98 | total 99 | // param a 100 | POP R1 101 | ST R1 a 102 | // = s 0 103 | LD R1 0 104 | ST R1 s 105 | // = i 0 106 | LD R1 0 107 | ST R1 i 108 | // L4 length T6 a 109 | L4 110 | LD R1 a 111 | CALL ALEN 112 | ST R1 T6 113 | // < T7 i T6 114 | LD R1 i 115 | LD R2 T6 116 | CMP R1 R2 117 | JLT CSET0 118 | LDI R1 1 119 | JMP CEXIT0 120 | CSET0 LDI R1 0 121 | CEXIT0 ST R1 T7 122 | // if0 T7 L5 123 | CMP R0 T7 124 | JEQ L5 125 | // [] T8 a i 126 | LD R1 a 127 | LD R2 i 128 | CALL AGET 129 | ST R1 T8 130 | // + T9 s T8 131 | LD R1 s 132 | LD R2 T8 133 | ADD R3 R1 R2 134 | ST R3 T9 135 | // = s T9 136 | LD R1 T9 137 | ST R1 s 138 | // goto L4 139 | JMP L4 140 | // L5 141 | L5 142 | // return s 143 | LD R1 s 144 | RET 145 | // endf 146 | // array T10 147 | LD R1 T10 148 | CALL ARRAY 149 | // apush T10 1 150 | LD R1 T10 151 | LD R2 1 152 | CALL APUSH 153 | // apush T10 3 154 | LD R1 T10 155 | LD R2 3 156 | CALL APUSH 157 | // apush T10 7 158 | LD R1 T10 159 | LD R2 7 160 | CALL APUSH 161 | // apush T10 2 162 | LD R1 T10 163 | LD R2 2 164 | CALL APUSH 165 | // apush T10 6 166 | LD R1 T10 167 | LD R2 6 168 | CALL APUSH 169 | // = a T10 170 | LD R1 T10 171 | ST R1 a 172 | // arg a 173 | LD R1 a 174 | PUSH R1 175 | // call T11 total 176 | CALL total 177 | ST R1 T11 178 | // = t T11 179 | LD R1 T11 180 | ST R1 t 181 | // table T12 182 | LD R1 T12 183 | CALL TABLE 184 | // map T12 e "dog" 185 | LD R1 T12 186 | LD R2 e 187 | LD R3 "dog" 188 | CALL TMAP 189 | // map T12 c "狗" 190 | LD R1 T12 191 | LD R2 c 192 | LD R3 "狗" 193 | CALL TMAP 194 | // = word T12 195 | LD R1 T12 196 | ST R1 word 197 | -------------------------------------------------------------------------------- /source/article2.md: -------------------------------------------------------------------------------- 1 | ## 使用 OpenCV 實作 AR -- 概念篇 (作者: Heresy Ku ) 2 | 3 | 擴增實境(Augmented Reality,簡稱 AR、 [維基百科](http://zh.wikipedia.org/wiki/%E6%93%B4%E5%A2%9E%E5%AF%A6%E5%A2%83) )這個技術,主要的概念就是把攝影機實際拍到的畫面,在顯示出來的同時,根據畫面的內容,加上其他的資訊、畫面,讓虛擬的物體,可以和真實的廠景融合在一起顯示。 4 | 5 | 由於把虛擬和現實融合,算是一個相當有趣、而且也相當特別的應用,所以其實在這方面的應用、研究,也都算滿多的;再加上現在許多行動手持裝置,不但配備了小型化的攝影機,也都有足夠的計算能力了,所以在近年來,也算是越來越熱門。不過實際上,這種概念並不算非常地新,在 1994 年就已經有人提出來了~到目前為止,也算是發展好一段時間了。 6 | 7 | ![](../img/OpenCV_AR_nakaohome.jpg) 8 | 9 | 而雖然 AR 的理想,是可以直接辨識畫面的內容,來做物體的辨識、定位,但是礙於實際上的計算效率、準確性,現階段比較普遍的應用,應該都還是需要特殊哪片、也就是需要「mark」來做辨識、定位的 AR。像上圖,就是一套算是相當成熟的 AR 開放原始碼函式庫、ARToolkit( [官網](http://www.hitl.washington.edu/artoolkit/) 、 [維基百科](http://en.wikipedia.org/wiki/ARToolKit) )的示意圖;裡面的人所拿著的,就是專門的 AR 卡片,上面就是即時辨識出這張卡片,並把虛擬人物放進去的效果。 10 | 11 | 不過,雖然說 ARToolKit 是一個相當成熟的函式庫,但是由於他已經沒有在繼續維護了(News 最後是 2007 年底、提供的 Windows 環境也是到 Visual Studio .Net 2003 而已),所以以現在的觀點來看,在使用上算是有相當地限制…像是他雖然是以 OpenGL 來做 3D 繪圖的函式庫,但是如果要和新的 Shader-based OpenGL 來做整合,似乎也有不少問題。所以當 Heresy 這邊要做 AR 相關的應用的時候,馬上就覺得他並不適和 Heresy 這邊的需求。 12 | 13 | 而 Heresy 後來使用的,則是使用以 OpenCV( [官網](http://opencv.org/) )這套電腦視覺函式庫所提供的功能為基礎的方法;實作上,主要則是參考 OpenCV-AR( [首頁](http://opencv-ar.sourceforge.net/) )這個 SourceForge 上的開放原始碼專案,來修改而實作完成的。 14 | 15 | 在這個函式庫的實作裡面,他主要概念,是去偵測畫面中的四邊形、然後把抓到的四邊形影像,轉換成為正方形的圖片,根據指定的樣板進行辨識;如果辨識的相似度夠高,則就視為是要處理的 marker,根據四邊形的四個點來計算出所應對應的矩陣,讓顯示的程式可以透過這個矩陣,來把 3D 的物體畫在對應的位置上。 16 | 17 | 而由於他是基於四邊形偵測來做處理的,而且辨識的過程又是把影像轉換成正方形,所以它基本上能接受的 marker,就變成一定是要是有明確的四邊形外框的正方形影像了~下面兩張圖,就是這個函式庫所給的兩個範例的 marker: 18 | 19 | ![](../img/OpenCV_AR_actag-large.png) 20 | 21 | 上面兩張圖,基本上是 OpenCV AR 能接受的兩種不同形式的 marker。 22 | 左邊的圖基本上是一種比較簡單的形式。他基本上是放大過的圖,原圖大小應該要是 10×10 個像素,每個像素都只有黑或白兩種可能;而由於實際上為了確保外圍的邊還是四邊形,所以外面是不能有白色的;也就是資訊都會記錄在裡面的 8×8、共有 64 個像素的矩形裡。(大小可以透過修改程式來調整) 23 | 右邊的則是一個黑底、再加上一般圖片,基本上只要確定黑底在外圍可以構成完整的四邊形、同時影像是方形的,就可以了~而雖然他是給彩色的圖片,不過為了減少環境光造成的影響、同時也減低計算量,所以實際上在內部處理的時候,都是轉換成灰階來做計算的。圖片的大小在程式中並沒有做額外的限制,但是由於圖片越大計算量會越多,所以建議不要用太大的圖檔當作 marker。 24 | 25 | 前者基本上比較單純,在程式裡面會轉換成編碼過的資料,直接進行比對;基本上不但效率比較好、準確性也比較高;但是相對的,可以變化的幅度就比較小了。 26 | 27 | 下方是在進行每一個畫面處理時,比較完整的流程圖: 28 | 29 | ![](../img/opencvar1.png) 30 | 31 | 首先,右邊的「Template Markers」是在初始化階段,讀取進來的 marker 的資料,是用來做比對用的樣板(以下稱為 template marker)。而左邊的「Recognized Markers」則是用來記錄成功辨識到的 marker、以及其相對的位置資訊;這個資料在完成後並不會被清除,而是留給外部做存取、以及給下一個畫面做追蹤用的。 32 | 33 | 在開始後,他會從攝影機取得當下的畫面,做一些處理後,就開始在畫面中偵測四邊形、也就是上圖中「Find Square」的區塊。而在找到畫面中的四邊形後,針對每一個找到的四邊形,會去和之前找到的 marker(Recognized Markers)做比對,如果夠接近的話,就當作是同一個 Marker,然後直接更新它的位置;這基本上算是用追蹤位置的方法,來簡化整個流程的計算量。 34 | 而在做完這個動作後,則是會把其他沒有更新過的 Marker 從記錄中刪除,讓 Recognized Markers 裡面只保存在這個畫面,已經追蹤到的 marker。 35 | 36 | 接下來,則是針對剩下來、沒辦法用 track 來解決的四邊形、則是會把它轉換成正方形的影像後,針對 template markers 裡的所有樣板資料,一筆一筆去做比對,藉此來找出這個四邊形最像的 template marker;這也是整個流程裡面,計算量最大的地方。 37 | 38 | ![](../img/OpenCV_AR_convert.jpg) 39 | 40 | 上圖就是這邊的示意圖,左邊的藍色框框,就是找到的四邊形,而強制把他轉換成正方形的影像後,就會像右邊的圖一樣;之後就是透過這個轉換後的影像,來進行比對的。而針對不同類型的 marker,其實也有不同的比對方法,這個就等之後講到這部分再來說了~ 41 | 而這樣找出來的結果,可能會有不同的四邊形,都對應到同一個 template marker;而為了避免這樣的問題,這裡則會再去針對每一個 template marker、都去做一次比較,來找出相似度最高的四變形,並以此為最終的結果,存入 Recognized Markers 中。 42 | 如果找完後,完全沒有找到四邊形的話,他會去修改進行 binary threshold 時的 threshold 值(他是用亂數產生),來試著讓程式可以在下一個畫面,找到更多四邊形進行測試。 43 | 最後,下面就是最後結果的示意圖。左邊是用來偵錯的畫面,裡面的藍色和紅色框框,就是有偵測到的四邊形;由於只是在偵測四邊形,所以可以看到,裡面的手指間,也被認為是一個四邊形了~=而其中藍色是代表有偵測到,但是沒有找到對應的 template marker,而紅色則是有找到對應的,所以可以看到在右邊的結果畫面裡,「Tw」這個 Maker 已經被一個台灣的 3D 物件取代了~ 44 | 45 | ![](../img/OpenCV_ar-debug.jpg) 46 | 47 | 這篇基本上是概念和架構性的文章,大概就先到這邊了。之後有時間,再來寫實作的內容介紹吧~ 48 | 49 | 【本文來自 Heresy's Space 的網誌,原文網址為: ,由 Heresy 捐出網誌給程式人雜誌,經陳鍾誠編輯後納入雜誌】 50 | -------------------------------------------------------------------------------- /code/js/as.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs"); // 引用檔案函式庫 2 | var c = require("./ccc"); // 引用基本函式庫 ccc.js 3 | var Memory = require("./memory"); // 引用記憶體物件 memory.js 4 | 5 | var as = function(opTable) { // 抽象組譯器物件 6 | this.opTable = opTable; // 取得指令表 opTable 7 | 8 | this.assemble = function(asmFile, objFile) { // 組譯器的主要函數 9 | this.lines = []; this.codes = []; // 設定程式碼行 (lines),指令陣列 (codes) 10 | this.symTable = {}; // 建立空的符號表 (symTable) 11 | c.log("Assembler:asmFile=%s objFile=%s", asmFile, objFile); // 輸入組合語言、輸出目的檔 12 | c.log("===============Assemble============="); 13 | var text = fs.readFileSync(asmFile, "utf8"); // 讀取檔案到 text 字串中 14 | this.lines = text.split(/[\r\n]+/); // 將組合語言分割成一行一行 15 | c.log(this.lines); // 印出組合語言以便觀察 16 | this.pass1(); // 第一階段:計算位址 17 | c.log("===============SYMBOL TABLE========="); 18 | for (s in this.symTable) { // 印出符號表以便觀察 19 | c.log("%s %s", c.fill(' ',s,8), c.hex(this.symTable[s].address, 4)); 20 | } 21 | this.pass2(); // 第二階段:建構目的碼 22 | this.saveObjFile(objFile); // 輸出目的檔 23 | } 24 | 25 | this.pass1 = function() { // 第一階段的組譯 26 | var address = 0; // 程式計數器 PC 的起始位址為 0 27 | c.log("=================PASS1================"); 28 | for (var i in this.lines) { // 對於每一行 29 | try { 30 | var code = this.parse(this.lines[i]); // 剖析並建立 code 物件 31 | code.address = address; // 設定該行的位址 32 | if (code.label.length != 0) { // 如果有標記符號 33 | this.symTable[code.label] = code; // 加入符號表中 34 | } 35 | this.codes.push(code); // 將剖析完成的指令放入陣列中 36 | c.log("%s", code); // 印出指令物件 37 | address += this.size(code); // 計算下一個指令位址 38 | } catch (err) { // 語法有錯,印出錯誤的行號與內容 39 | c.error(c.format("line %d : %s", i, this.lines[i]), err); 40 | } 41 | } 42 | } 43 | 44 | this.pass2 = function(codes) { // 組譯器的第二階段 45 | c.log("=============PASS2=============="); 46 | for (var i in this.codes) { // 對每一個指令 47 | try { 48 | this.translate(this.codes[i]); // 將組合語言指令翻譯成機器碼 49 | c.log("%s", this.codes[i]); // 印出指令物件 (含組合語言與機器碼) 50 | } catch (err) { // 語法有錯,印出錯誤的行號與內容 51 | c.error(c.format("line %d : %s", i, this.lines[i]), err); 52 | } 53 | } 54 | } 55 | 56 | this.saveObjFile = function(objFile) { // 儲存目的檔 57 | c.log("=================SAVE OBJ FILE================"); 58 | var obj = ""; // obj 為目的檔的 16 進位字串,初始化為空字串 59 | for (var i in this.codes) // 對於每個指令 60 | obj += this.codes[i].obj; // 都將目的碼加入 obj 字串中。 61 | var m = new Memory(1); // Memory 物件,用來將 16 進位目的碼轉為 2 進位儲存。 62 | m.loadhex(obj); // 將 16 進位目的碼載入記憶體 63 | m.dump(); // 輸出記憶體內容 64 | m.save(objFile); // 將記憶體內容除存到目的檔 objFile 中。 65 | } 66 | 67 | this.size = function(code) { // 計算指令所佔空間大小,在 pass1() 當中會呼叫此函數 68 | var len = 0, unitSize = 1; // len: 指令大小 , unitSize:每單位大小 (BYTE=1, WORD=4) 69 | switch (code.op.name) { // 根據運算碼 op 70 | case "RESW" : return 4 * parseInt(code.args[0]); // 如果是 RESW, 大小為 4*保留量(參數 0) 71 | case "RESB" : return 1 * parseInt(code.args[0]); // 如果是 RESB, 大小為 1*保留量(參數 0) 72 | case "WORD" : unitSize = 4; // 沒有 break,繼續執行到 BYTE 部分的程式 (共用) 73 | case "BYTE" : // 如果是BYTE, 大小是 1*參數個數 74 | for (i in code.args) { // 對於 BYTE 或 WORD 中的每個元素 75 | if (code.args[i].match(/^\".*?\"$/)) // 如果是字串,像 "Hello!" 76 | len += (code.args[i].length - 2) * unitSize; // 則大小為 unitSize*字串長度 77 | else // 否則 大小就是 unitSize (BYTE=1, WORD=4) 78 | len += unitSize; 79 | } 80 | return len; 81 | case "" : return 0; // 如果只是標記, 大小為 0 82 | default : return 4; // 其他情形 (指令), 大小為 4 83 | } 84 | } 85 | } 86 | 87 | module.exports = as; // 匯出「抽象組譯器物件 as 」 -------------------------------------------------------------------------------- /code/js/bak/jc1.js: -------------------------------------------------------------------------------- 1 | // STMTS = {} | STMT {; STMT_LIST}? 2 | // STMT = ASSIGN | FOR | WHILE | IF | return E; 3 | // ASSIGN = (ID[++|--]?)?(=E)? 4 | // E=T ([+|-|*|/|%|&|^|||&&||||>>|<<|==|<=|>=|<|>] F)? 5 | // T=STRING | INTEGER | FLOAT | '(' E ')' | ID ('('ARGS')')? 6 | // ARGS = {} | E {, ARGS}? 7 | 8 | require("./ccc"); 9 | 10 | var symTable = {}; 11 | 12 | var tempIdx = 0; 13 | var nextTemp=function() { return "T"+tempIdx++; } 14 | 15 | var stack = []; 16 | var push=function(o) { stack.push(o); } 17 | var pop=function() { return stack.pop(); } 18 | 19 | var tokens = []; 20 | var tokenIdx = 0; 21 | // 本來應該用 .*? 來比對 /*...*/ 註解的,但 javascript 的 . 並不包含 \n, 因此用 \s\S 代替 . 就可以了。 22 | var retok = /(\/\*[\s\S]*?\*\/)|(\/\/[^\r\n])|(\d+)|([a-zA-Z]\w*)|(\r?\n)|(.)/gm; // *?, +? non greedy, m for multiline 23 | 24 | var source = ""; 25 | var scan=function(text) { 26 | tokenIdx = 0; 27 | tokens = text.match(retok); 28 | return tokens; 29 | } 30 | 31 | var error=function(o) { printf("Error: %j\n", o); } 32 | var pcode = function(op, t, t1, t2) { printf("%s %s %s %s\n", op, t, t1, t2); } 33 | 34 | var next=function(o) { 35 | if (o==null || isNext(o)) 36 | return tokens[tokenIdx++]; 37 | error(token); 38 | } 39 | 40 | var isNext=function(o) { 41 | if (tokenIdx >= tokens.length) 42 | return false; 43 | var token = tokens[tokenIdx]; 44 | if (o instanceof RegExp) { 45 | return token.match(o); 46 | } else { 47 | return (token == o); 48 | } 49 | } 50 | 51 | var compile=function(text) { 52 | scan(text); 53 | printf("text=%s\n", text); 54 | printf("tokens=%j\n", tokens); 55 | E(); 56 | printf("symTable=%j\n", symTable); 57 | } 58 | 59 | // STMT_LIST = {} | STMT {; STMT_LIST}? 60 | var STMT_LIST=function() { 61 | STMT(); 62 | if (isNext(';')) STMTS_LIST(); 63 | return t1; 64 | } 65 | 66 | // STMT = FOR | WHILE | IF | return E | ASSIGN 67 | var STMT=function() { 68 | if (isNext("for")) { 69 | FOR(); 70 | } else if (isNext("while")) { 71 | WHILE(); 72 | } else if (isNext("if")) { 73 | IF(); 74 | } else if (isNext("return")) { 75 | next("return"); 76 | E(); 77 | } else { 78 | ASSIGN(); 79 | } 80 | } 81 | 82 | // ASSIGN = (ID[++|--]?)?(=E)? 83 | var ASSIGN=function() { 84 | var id="", op=""; 85 | if (isNextType("ID")) { 86 | id = next(null); 87 | if (isNext("++") || isNext("--")) 88 | op = next(null); 89 | } 90 | if (isNext("=")) { 91 | E(); 92 | } 93 | } 94 | 95 | // E=T ([+|-|*|/|%|&|^|||&&||||>>|<<|==|<=|>=|<|>] T)? 96 | var E=function() { 97 | var op = ""; 98 | t1 = T(); 99 | if (isNext(/([\+\-\*\/%&^|<>])|(&&)|(||)|(>>)|(<<)|(<=)|(>=)/)) 100 | op = next(null); 101 | t2 = T(); 102 | t = nextTemp(); 103 | pcode(op, t, t1, t2); 104 | } 105 | } 106 | 107 | // T=STRING | INTEGER | FLOAT | '(' E ')' | ID ('('ARGS')')? 108 | var T=function() { 109 | if (isNextType("STRING|INTEGER|FLOAT")) { 110 | return next(null); 111 | } else if (isNext("(")) { 112 | next("("); E(); next(")"); 113 | } else if (isNextType("ID")) { 114 | id=next(null); next("("); ARGS(); next(")"); 115 | pcode("call", nextTemp(), id, ""); 116 | } 117 | } 118 | 119 | printf("=== EBNF Grammar =====\n"); 120 | printf("E=T ([+-] T)*\n"); 121 | printf("T=F ([*/] F)*\n"); 122 | printf("F=NUMBER | ID | '(' E ')'\n"); 123 | compile("32+5*(182+degree*4-20)"); 124 | // printf("typeof(/\w+/)=%s /\w+/ instanceof RegExp=%s\n", typeof(/\w+/), /\w+/ instanceof RegExp); 125 | // printf("typeof(str)=%s\n", typeof("str")); 126 | 127 | -------------------------------------------------------------------------------- /code/js/bak/jexpc.js: -------------------------------------------------------------------------------- 1 | // STMTS = {} | STMT {; STMT_LIST}? 2 | // STMT = ASSIGN | FOR | WHILE | IF | return E; 3 | // ASSIGN = (ID[++|--]?)?(=E)? 4 | // E=T ([+|-|*|/|%|&|^|||&&||||>>|<<|==|<=|>=|<|>] F)? 5 | // T=STRING | INTEGER | FLOAT | '(' E ')' | ID ('('ARGS')')? 6 | // ARGS = {} | E {, ARGS}? 7 | 8 | var c = require("./ccc"); 9 | 10 | var symTable = {}; 11 | 12 | var tempIdx = 0; 13 | var nextTemp=function() { return "T"+tempIdx++; } 14 | 15 | var stack = []; 16 | var push=function(o) { stack.push(o); } 17 | var pop=function() { return stack.pop(); } 18 | 19 | var tokens = []; 20 | var tokenIdx = 0; 21 | // 本來應該用 .*? 來比對 /*...*/ 註解的,但 javascript 的 . 並不包含 \n, 因此用 \s\S 代替 . 就可以了。 22 | var retok = /(\/\*[\s\S]*?\*\/)|(\/\/[^\r\n])|(\d+)|([a-zA-Z]\w*)|(\r?\n)|(.)/gm; // *?, +? non greedy, m for multiline 23 | 24 | var source = ""; 25 | var scan=function(text) { 26 | tokenIdx = 0; 27 | tokens = text.match(retok); 28 | return tokens; 29 | } 30 | 31 | var error=function(o) { printf("Error: %j\n", o); } 32 | var pcode = function(op, t, t1, t2) { printf("%s %s %s %s\n", op, t, t1, t2); } 33 | 34 | var next=function(o) { 35 | if (o==null || isNext(o)) 36 | return tokens[tokenIdx++]; 37 | error(token); 38 | } 39 | 40 | var isNext=function(o) { 41 | if (tokenIdx >= tokens.length) 42 | return false; 43 | var token = tokens[tokenIdx]; 44 | if (o instanceof RegExp) { 45 | return token.match(o); 46 | } else { 47 | return (token == o); 48 | } 49 | } 50 | 51 | var compile=function(text) { 52 | scan(text); 53 | printf("text=%s\n", text); 54 | printf("tokens=%j\n", tokens); 55 | E(); 56 | printf("symTable=%j\n", symTable); 57 | } 58 | 59 | // STMT_LIST = {} | STMT {; STMT_LIST}? 60 | var STMT_LIST=function() { 61 | STMT(); 62 | if (isNext(';')) STMTS_LIST(); 63 | return t1; 64 | } 65 | 66 | // STMT = FOR | WHILE | IF | return E | ASSIGN 67 | var STMT=function() { 68 | if (isNext("for")) { 69 | FOR(); 70 | } else if (isNext("while")) { 71 | WHILE(); 72 | } else if (isNext("if")) { 73 | IF(); 74 | } else if (isNext("return")) { 75 | next("return"); 76 | E(); 77 | } else { 78 | ASSIGN(); 79 | } 80 | } 81 | 82 | // ASSIGN = (ID[++|--]?)?(=E)? 83 | var ASSIGN=function() { 84 | var id="", op=""; 85 | if (isNextType("ID")) { 86 | id = next(null); 87 | if (isNext("++") || isNext("--")) 88 | op = next(null); 89 | } 90 | if (isNext("=")) { 91 | E(); 92 | } 93 | } 94 | 95 | // E=T ([+|-|*|/|%|&|^|||&&||||>>|<<|==|<=|>=|<|>] T)? 96 | var E=function() { 97 | var op = ""; 98 | t1 = T(); 99 | if (isNext(/([\+\-\*\/%&^|<>])|(&&)|(||)|(>>)|(<<)|(<=)|(>=)/)) 100 | op = next(null); 101 | t2 = T(); 102 | t = nextTemp(); 103 | pcode(op, t, t1, t2); 104 | } 105 | } 106 | 107 | // T=STRING | INTEGER | FLOAT | '(' E ')' | ID ('('ARGS')')? 108 | var T=function() { 109 | if (isNextType("STRING|INTEGER|FLOAT")) { 110 | return next(null); 111 | } else if (isNext("(")) { 112 | next("("); E(); next(")"); 113 | } else if (isNextType("ID")) { 114 | id=next(null); next("("); ARGS(); next(")"); 115 | pcode("call", nextTemp(), id, ""); 116 | } 117 | } 118 | 119 | printf("=== EBNF Grammar =====\n"); 120 | printf("E=T ([+-] T)*\n"); 121 | printf("T=F ([*/] F)*\n"); 122 | printf("F=NUMBER | ID | '(' E ')'\n"); 123 | compile("32+5*(182+degree*4-20)"); 124 | // printf("typeof(/\w+/)=%s /\w+/ instanceof RegExp=%s\n", typeof(/\w+/), /\w+/ instanceof RegExp); 125 | // printf("typeof(str)=%s\n", typeof("str")); 126 | 127 | -------------------------------------------------------------------------------- /htm/video1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

程式人雜誌 -- 2014 年 5 月號 (開放公益出版品)

14 |
15 |
16 |
17 | 20 |
21 |

看影片學 Lua 程式設計

22 |

Lua 的語法很簡單,執行環境也很簡單,如果您用的是 Linux,應該從 Lua 官網上下載 建置一下就可以了,以下是官網所提供的建置方法:

23 |
curl -R -O http://www.lua.org/ftp/lua-5.2.3.tar.gz
24 | tar zxf lua-5.2.3.tar.gz
25 | cd lua-5.2.3
26 | make linux test
27 |

如果您用 MS. Windows ,那麼就可以安裝 Lua for Windows,以下是其網址:

28 | 31 |

下載安裝後,您會發現在「開始/所有程式」裏有個 Lua 的資料夾,裏面有「iLua, Lua Command Line, Lua Examples, QuickLuaTour」等項目,建議您看看「QuickLuaTour」,它會帶領你快速的熟悉 Lua 的語法與範例。

32 |

接著您可以直接起動命令列,然後用任何的編輯器,像是「Notepad++」等,開始寫一些簡單的程式,然後直接用 lua <程式名稱> 去執行您的程式即可。以下是筆者執行幾個 Lua 程式的過程:

33 |
D:\Dropbox\Public\pmag\201402\code>lua hello.lua
34 | Hello World!
35 | 
36 | D:\Dropbox\Public\pmag\201402\code>lua fact.lua
37 | factorial(5)=120
38 | 
39 | D:\Dropbox\Public\pmag\201402\code>lua obj.lua
40 | 10,20
41 | 30,40
42 | 50,60
43 | 
44 | D:\Dropbox\Public\pmag\201402\code>lua obj.lua
45 | point(10,20)
46 | point(30,40)
47 | point(50,60)
48 |

您可以以看看下列 Lua 的影片,以便瞭解 Lua 的程式寫法:

49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 |
影片連結
Lua Tutorial #1: Introduction and Setuphttp://youtu.be/dHURyRLMOK0
Lua Tutorial #2: Hello Worldhttp://youtu.be/aSxoOCn6Y4E
Lua Tutorial #3: Variables and User Inputhttp://youtu.be/ClThmOGuMi4
Lua Tutorial #4: Basic Mathematicshttp://youtu.be/jQ40M1DObl4
Lua Tutorial #5: If and Elsehttp://youtu.be/vlJftHgeByg
79 |

當然、多寫多看,應該是學習程式的不二法門。學程式與學習游泳一樣,只有下水開始扭動身體,才有機會真正學會游泳,也只有真正開始上機寫程式,才有可能真正學會寫程式。對於 Lua 、當然也是如此!

80 |
81 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /code/js/bak/vm.js.bak: -------------------------------------------------------------------------------- 1 | /* 2 | var bits = function(word, from, to) { return word << (31-to) >>> (31-to+from); } // 取得 from 到 to 之間的位元 3 | var signbits = function(word, from, to) { return word << (31-to) >> (31-to+from); } // 取得 from 到 to 之間的位元 4 | var ID = function(op) { 5 | // printf("op=%s\n", op); 6 | return cpu1.opTable[op].id; 7 | } 8 | 9 | var vm = function() { 10 | this.memory = new Memory(1); 11 | 12 | this.run = function(objFile) { 13 | m = this.memory; 14 | m.load(objFile); 15 | IR = 16; 16 | PC = 15; 17 | LR = 14; 18 | SP = 13; 19 | SW = 12; 20 | stop = false; 21 | printf("run\n"); 22 | while (!stop) { // 如果尚未結束 23 | tpc = R[PC]; 24 | R[0] = 0; // R[0] 永遠為 0 25 | R[IR] = m.geti(R[PC]); // 指令擷取,IR=[PC..PC+3] 26 | R[PC] += 4; // 擷取完將 PC 加 4,指向下一個指令 27 | op = bits(R[IR], 24, 31); // 取得 op 欄位,IR[24..31] 28 | ra = bits(R[IR], 20, 23); // 取得 ra 欄位,IR[20..23] 29 | rb = bits(R[IR], 16, 19); // 取得 rb 欄位,IR[16..19] 30 | c16= signbits(R[IR], 0, 15); // 取得 16 位元的 cx 31 | c5 = bits(R[IR], 0, 4); 32 | addr = R[rb]+c16; 33 | // printf("IR=%s op=%s ra=%s rb=%s c16=%s N=%d Z=%d\n", hex(R[IR],8), hex(op,2), hex(ra,1), hex(rb,1), hex(c16,4), N, Z); 34 | // printf("addr=%s\n", hex(R[rb]+c16, 4)); 35 | 36 | switch (op) { // 根據op執行動作 37 | case ID("LD") : R[ra] = m.geti(addr); break; // 處理 LD 指令 38 | case ID("ST") : m.seti(addr, R[ra]); printf("m[%s]=%s\n", hex(addr,4), m.geti(addr)); break; // 處理 ST 指令 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("ADDI"):R[ra] = R[rb] + c16; break; // 處理 ADDI 指令 42 | case ID("ADD"): R[ra] += m.geti(addr); break; // 處理ADD指令 43 | case ID("SUB"): R[ra] -= m.geti(addr); break; // 處理SUB指令 44 | case ID("MUL"): R[ra] *= m.geti(addr); break; // 處理MUL指令 45 | case ID("DIV"): R[ra] /= m.geti(addr); break; // 處理DIV指令 46 | case ID("AND"): R[ra] &= m.geti(addr); break; // 處理AND指令 47 | case ID("OR") : R[ra] |= m.geti(addr); ; break; // 處理OR指令 48 | case ID("XOR"): R[ra] ^= m.geti(addr); ; break; // 處理XOR指令 49 | // case ID("ROL"): R[ra] = ROL(R[rb],c5); break; // 處理ROL指令 50 | // case ID("ROR"): R[ra] = ROR(R[rb],c5); break; // 處理ROR指令 51 | case ID("SHL"): R[ra] = R[rb]<>c5; break; // 處理SHR指令 53 | case ID("JZ") : if (R[ra]==0) R[PC] = R[rb]+c16; break; // 處理JZ指令 54 | case ID("PUSH"):R[SP]-=4; R[ra]=m.geti(addr); m.seti(R[SP], R[ra]); break; // 處理PUSH指令 55 | case ID("POP"): R[ra] = m.geti(R[SP]); R[SP]+=4; break; // 處理POP指令 56 | case ID("PUSHB"):R[SP]--; R[ra]=m.getb(addr); m.setb(R[SP], R[ra]); break; // 處理PUSH指令 57 | case ID("POPB"):R[ra] = m.getb(R[SP]); R[SP]++; break; // 處理POPB指令 58 | case ID("RET"): stop=true; R[PC]=R[LR]; break; // 處理RET指令 59 | // default: printf("Error:invalid op (%s)\n", hex(op)); 60 | } // switch 61 | printf("PC=%s IR=%s SW=%s R[%s]=0x%s=%d\n", // 印出 PC, IR, R[ra]暫存器的值,以利觀察 62 | hex(tpc,4), hex(R[IR],8), hex(R[SW],8), hex(ra,2), hex(R[ra], 8), R[ra]); 63 | } // while 64 | } 65 | } 66 | 67 | // #define ROR(i, k) (((UINT32)i>>k)|(bits(i,32-k, 31)<<(32-k)))// 向右旋轉k位元 68 | // #define ROL(i, k) (((UINT32)i< 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

程式人雜誌 -- 2014 年 5 月號 (開放公益出版品)

14 |
15 |
16 |

關於程式人雜誌

17 |

程式人雜誌是一個結合「開放原始碼與公益捐款活動」的雜誌,簡稱「開放公益雜誌」。開放公益雜誌本著「讀書做善事、寫書做公益」的精神,我們非常歡迎程式人認養專欄、或者捐出您的網誌。

18 |

雜誌下載

19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 |
出刊年月epubipad:PDFA4:PDF單頁 HTM原始碼全部下載
2014年5月epubipad.pdfA4.pdfpmag.htmlcode.zipgithub
43 |

本期內容 (焦點:爬山演算法)

44 | 68 |

雜誌取得

69 |

程式人雜誌預定於每個月 1 日出刊,您可以從下列網址取得程式人雜誌的所有內容 (包含當月最新出刊的雜誌)。

70 | 73 |

連絡我們

74 |

竭誠歡迎程式人投稿,或者成為本雜誌的專欄作家,現在就可以加入 程式人雜誌社團 一同共襄盛舉。

75 |

本雜誌編輯為「陳鍾誠 (@ccckmit)」,若要聯絡編輯,請寄信到

81 |
82 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /code/js/cc1.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs"); 2 | require("./ccc"); 3 | var cpu = require("./cpu1"); 4 | var Memory = require("./memory"); 5 | var cpu1 = new cpu(); 6 | var R = cpu1.R; 7 | 8 | var bits = function(word, from, to) { return word << (31-to) >>> (31-to+from); } // 取得 from 到 to 之間的位元 9 | var signbits = function(word, from, to) { return word << (31-to) >> (31-to+from); } // 取得 from 到 to 之間的位元 10 | var ID = function(op) { 11 | // printf("op=%s\n", op); 12 | return cpu1.opTable[op].id; 13 | } 14 | 15 | var vm = function() { 16 | this.memory = new Memory(1); 17 | 18 | this.run = function(objFile) { 19 | m = this.memory; 20 | m.load(objFile); 21 | IR = 16; 22 | PC = 15; 23 | LR = 14; 24 | SP = 13; 25 | SW = 12; 26 | stop = false; 27 | printf("run\n"); 28 | while (!stop) { // 如果尚未結束 29 | tpc = R[PC]; 30 | R[0] = 0; // R[0] 永遠為 0 31 | R[IR] = m.geti(R[PC]); // 指令擷取,IR=[PC..PC+3] 32 | R[PC] += 4; // 擷取完將 PC 加 4,指向下一個指令 33 | op = bits(R[IR], 24, 31); // 取得 op 欄位,IR[24..31] 34 | ra = bits(R[IR], 20, 23); // 取得 ra 欄位,IR[20..23] 35 | rb = bits(R[IR], 16, 19); // 取得 rb 欄位,IR[16..19] 36 | c16= signbits(R[IR], 0, 15); // 取得 16 位元的 cx 37 | c5 = bits(R[IR], 0, 4); 38 | addr = R[rb]+c16; 39 | // printf("IR=%s op=%s ra=%s rb=%s c16=%s N=%d Z=%d\n", hex(R[IR],8), hex(op,2), hex(ra,1), hex(rb,1), hex(c16,4), N, Z); 40 | // printf("addr=%s\n", hex(R[rb]+c16, 4)); 41 | 42 | switch (op) { // 根據op執行動作 43 | case ID("LD") : R[ra] = m.geti(addr); break; // 處理 LD 指令 44 | case ID("ST") : m.seti(addr, R[ra]); printf("m[%s]=%s\n", hex(addr,4), m.geti(addr)); break; // 處理 ST 指令 45 | case ID("LDB"): R[ra] = m.getb(addr); break; // 處理 LDB 指令 46 | case ID("STB"): m.setb(addr, R[ra]); break; // 處理 STB 指令 47 | case ID("ADDI"):R[ra] = R[rb] + c16; break; // 處理 ADDI 指令 48 | case ID("ADD"): R[ra] += m.geti(addr); break; // 處理ADD指令 49 | case ID("SUB"): R[ra] -= m.geti(addr); break; // 處理SUB指令 50 | case ID("MUL"): R[ra] *= m.geti(addr); break; // 處理MUL指令 51 | case ID("DIV"): R[ra] /= m.geti(addr); break; // 處理DIV指令 52 | case ID("AND"): R[ra] &= m.geti(addr); break; // 處理AND指令 53 | case ID("OR") : R[ra] |= m.geti(addr); ; break; // 處理OR指令 54 | case ID("XOR"): R[ra] ^= m.geti(addr); ; break; // 處理XOR指令 55 | // case ID("ROL"): R[ra] = ROL(R[rb],c5); break; // 處理ROL指令 56 | // case ID("ROR"): R[ra] = ROR(R[rb],c5); break; // 處理ROR指令 57 | case ID("SHL"): R[ra] = R[rb]<>c5; break; // 處理SHR指令 59 | case ID("JZ") : if (R[ra]==0) R[PC] = R[rb]+c16; break; // 處理JZ指令 60 | case ID("PUSH"):R[SP]-=4; R[ra]=m.geti(addr); m.seti(R[SP], R[ra]); break; // 處理PUSH指令 61 | case ID("POP"): R[ra] = m.geti(R[SP]); R[SP]+=4; break; // 處理POP指令 62 | case ID("PUSHB"):R[SP]--; R[ra]=m.getb(addr); m.setb(R[SP], R[ra]); break; // 處理PUSH指令 63 | case ID("POPB"):R[ra] = m.getb(R[SP]); R[SP]++; break; // 處理POPB指令 64 | case ID("RET"): stop=true; R[PC]=R[LR]; break; // 處理RET指令 65 | // default: printf("Error:invalid op (%s)\n", hex(op)); 66 | } // switch 67 | printf("PC=%s IR=%s SW=%s R[%s]=0x%s=%d\n", // 印出 PC, IR, R[ra]暫存器的值,以利觀察 68 | hex(tpc,4), hex(R[IR],8), hex(R[SW],8), hex(ra,2), hex(R[ra], 8), R[ra]); 69 | } // while 70 | } 71 | } 72 | 73 | // #define ROR(i, k) (((UINT32)i>>k)|(bits(i,32-k, 31)<<(32-k)))// 向右旋轉k位元 74 | // #define ROL(i, k) (((UINT32)i<== 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 | STMT_LIST(); 59 | } 60 | 61 | // BLOCK = { STMT_LIST } 62 | var BLOCK=function() { 63 | next("{"); STMT_LIST(); next("}"); 64 | } 65 | 66 | // STMT_LIST = {} | STMT {; STMT_LIST}? 67 | var STMT_LIST=function() { 68 | STMT(); 69 | if (isNext(';')) { next(';'); STMT_LIST(); } 70 | } 71 | 72 | // STMT = FOR | WHILE | IF | return E | ASSIGN 73 | var STMT=function() { 74 | if (isNext("for")) { 75 | FOR(); 76 | } else if (isNext("while")) { 77 | WHILE(); 78 | } else if (isNext("if")) { 79 | IF(); 80 | } else if (isNext("return")) { 81 | next("return"); 82 | E(); 83 | } else { 84 | ASSIGN(); 85 | } 86 | } 87 | 88 | // FOR = for (STMT; E; STMT) BLOCK 89 | var FOR=function() { 90 | next("for"); next("("); STMT(); next(";"); E(); next(";"); STMT(); next(")"); BLOCK(); 91 | } 92 | 93 | // WHILE = while (E) BLOCK 94 | var WHILE=function() { 95 | next("while"); next("("); E(); next(")"); BLOCK(); 96 | } 97 | 98 | // IF = if (E) BLOCK (else if (E) BLOCK)* (else BLOCK)? 99 | var IF=function() { 100 | next("if"); next("("); E(); next(")"); BLOCK(); 101 | while (isNext("else")) { 102 | next("else"); 103 | if (isNext("if")) { 104 | next("if"); next("("); E(); next(")"); BLOCK(); 105 | } else { 106 | BLOCK(); 107 | } 108 | } 109 | } 110 | 111 | // ASSIGN = (ID[++|--]?)?(=E)? 112 | var ASSIGN=function() { 113 | var id="", op=""; 114 | if (isNextType("id")) { 115 | id = next(null); 116 | if (isNext("++") || isNext("--")) 117 | op = next(null); 118 | } 119 | if (isNext("=")) { 120 | next("="); 121 | E(); 122 | } 123 | } 124 | 125 | // E=T ([+|-|*|/|%|&|^|||&&||||>>|<<|==|<=|>=|<|>] T)? 126 | var E=function() { 127 | t1 = T(); 128 | if (isNextType("op2")) { 129 | var op = next(null); 130 | T(); 131 | } 132 | } 133 | 134 | // T=STRING | INTEGER | FLOAT | ( E ) | FUNCTION | ID (ARGS)? 135 | var T=function() { 136 | if (isNextType("string|integer|float")) { 137 | return next(null); 138 | } else if (isNext("(")) { 139 | next("("); E(); next(")"); 140 | } else if (isNext("function")) { 141 | FUNCTION(); 142 | } else if (isNextType("id")) { 143 | id=next(null); 144 | if (isNext("(")) { next("("); ARGS(); next(")"); } 145 | } else error(); 146 | } 147 | 148 | // FUNCTION = function(ARGS) BLOCK 149 | var FUNCTION = function() { 150 | next("function"); next("("); ARGS(); next(")"); BLOCK(); 151 | } 152 | 153 | var source = fs.readFileSync("test.j1", "utf8"); 154 | compile(source); 155 | -------------------------------------------------------------------------------- /code/js/bak/vm0.js.bak: -------------------------------------------------------------------------------- 1 | var fs = require("fs"); 2 | require("./ccc"); 3 | var cpu0 = require("./cpu0"); 4 | var Memory = require("./memory"); 5 | 6 | var bits = function(word, from, to) { return word << (31-to) >>> (31-to+from); } // 取得 from 到 to 之間的位元 7 | var signbits = function(word, from, to) { return word << (31-to) >> (31-to+from); } // 取得 from 到 to 之間的位元 8 | var ID = function(op) { 9 | // printf("op=%s\n", op); 10 | return cpu0.opTable[op].id; 11 | } 12 | 13 | var vm = function() { 14 | this.R = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 13, 14, 0, 16 ]; 15 | this.memory = new Memory(1); 16 | R = this.R; 17 | 18 | this.dump = function() { 19 | for (i=0; i<=16; i++) // 印出 R0..R15 20 | printf("R[%d]=0x%s=%d\n",i, hex(R[i],8), R[i]); 21 | } 22 | 23 | this.run = function(objFile) { 24 | m = this.memory; 25 | m.load(objFile); 26 | IR = 16; 27 | PC = 15; 28 | LR = 14; 29 | SP = 13; 30 | SW = 12; 31 | stop = false; 32 | printf("run\n"); 33 | while (!stop) { // 如果尚未結束 34 | tpc = R[PC]; 35 | R[0] = 0; // R[0] 永遠為 0 36 | R[IR] = m.geti(R[PC]); // 指令擷取,IR=[PC..PC+3] 37 | R[PC] += 4; // 擷取完將 PC 加 4,指向下一個指令 38 | op = bits(R[IR], 24, 31); // 取得 op 欄位,IR[24..31] 39 | ra = bits(R[IR], 20, 23); // 取得 ra 欄位,IR[20..23] 40 | rb = bits(R[IR], 16, 19); // 取得 rb 欄位,IR[16..19] 41 | c24= signbits(R[IR], 0, 23); 42 | c16= signbits(R[IR], 0, 15); // 取得 16 位元的 cx 43 | c5 = bits(R[IR], 0, 4); 44 | addr = R[rb]+c16; 45 | // printf("IR=%s op=%s ra=%s rb=%s c16=%s N=%d Z=%d\n", hex(R[IR],8), hex(op,2), hex(ra,1), hex(rb,1), hex(c16,4), N, Z); 46 | // printf("addr=%s\n", hex(R[rb]+c16, 4)); 47 | 48 | switch (op) { // 根據op執行動作 49 | case ID("LD") : R[ra] = m.geti(addr); break; // 處理 LD 指令 50 | case ID("ST") : m.seti(addr, R[ra]); printf("m[%s]=%s\n", hex(addr,4), m.geti(addr)); break; // 處理 ST 指令 51 | case ID("LDB"): R[ra] = m.getb(addr); break; // 處理 LDB 指令 52 | case ID("STB"): m.setb(addr, R[ra]); break; // 處理 STB 指令 53 | case ID("ADDI"):R[ra] = R[rb] + c16; break; // 處理 ADDI 指令 54 | case ID("ADD"): R[ra] += m.geti(addr); break; // 處理ADD指令 55 | case ID("SUB"): R[ra] -= m.geti(addr); break; // 處理SUB指令 56 | case ID("MUL"): R[ra] *= m.geti(addr); break; // 處理MUL指令 57 | case ID("DIV"): R[ra] /= m.geti(addr); break; // 處理DIV指令 58 | case ID("AND"): R[ra] &= m.geti(addr); break; // 處理AND指令 59 | case ID("OR") : R[ra] |= m.geti(addr); ; break; // 處理OR指令 60 | case ID("XOR"): R[ra] ^= m.geti(addr); ; break; // 處理XOR指令 61 | // case ID("ROL"): R[ra] = ROL(R[rb],c5); break; // 處理ROL指令 62 | // case ID("ROR"): R[ra] = ROR(R[rb],c5); break; // 處理ROR指令 63 | case ID("SHL"): R[ra] = R[rb]<>c5; break; // 處理SHR指令 65 | case ID("JZ") : if (R[ra]==0) R[PC] = R[rb]+c16; break; // 處理JZ指令 66 | case ID("PUSH"):R[SP]-=4; R[ra]=m.geti(addr); m.seti(R[SP], R[ra]); break; // 處理PUSH指令 67 | case ID("POP"): R[ra] = m.geti(R[SP]); R[SP]+=4; break; // 處理POP指令 68 | case ID("PUSHB"):R[SP]--; R[ra]=m.getb(addr); m.setb(R[SP], R[ra]); break; // 處理PUSH指令 69 | case ID("POPB"):R[ra] = m.getb(R[SP]); R[SP]++; break; // 處理POPB指令 70 | case ID("RET"): stop=true; R[PC]=R[LR]; break; // 處理RET指令 71 | // default: printf("Error:invalid op (%s)\n", hex(op)); 72 | } // switch 73 | printf("PC=%s IR=%s SW=%s R[%s]=0x%s=%d\n", // 印出 PC, IR, R[ra]暫存器的值,以利觀察 74 | hex(tpc,4), hex(R[IR],8), hex(R[SW],8), hex(ra,2), hex(R[ra], 8), R[ra]); 75 | } // while 76 | } 77 | } 78 | 79 | // #define ROR(i, k) (((UINT32)i>>k)|(bits(i,32-k, 31)<<(32-k)))// 向右旋轉k位元 80 | // #define ROL(i, k) (((UINT32)i< 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

程式人雜誌 -- 2014 年 5 月號 (開放公益出版品)

14 |
15 |
16 | 21 |

橫跨「數學、哲學、文學」的諾貝爾大師:伯特蘭·羅素 (Bertrand Arthur William Russell)

22 |

羅素出生於1872年的一個貴族家庭,當時大英帝國正值巔峰,逝於1970年,此時英國經歷過兩次世界大戰,其帝國已經沒落。

23 |

羅素的家族相當顯赫,祖父約翰·羅素勛爵在1840年代曾兩次出任英國首相,是「輝格黨」的核心成員。羅素的父母分別在他兩歲與四歲時過世,因此羅素是擔任兩次首相的祖父所扶養長大的。

24 |
25 | 圖、伯特蘭·羅素 (Bertrand Arthur William Russell)

圖、伯特蘭·羅素 (Bertrand Arthur William Russell)

26 |
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 |
13 |

程式人雜誌 -- 2014 年 5 月號 (開放公益出版品)

14 |
15 |
16 |
17 | 25 |
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 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 74 | 75 | 76 | 77 |
公益團體聯絡資訊服務對象捐款帳號
財團法人羅慧夫顱顏基金會http://www.nncf.org/

02-27190408分機 232
顱顏患者 (如唇顎裂、小耳症或其他罕見顱顏缺陷)銀行:009彰化銀行民生分行
帳號:5234-01-41778-800
社團法人台灣省兒童少年成長協會http://www.cyga.org/

04-23058005
單親、隔代教養.弱勢及一般家庭之兒童青少年銀行:新光銀行
戶名:台灣省兒童少年成長協會
帳號:103-0912-10-000212-0
78 |
79 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /htm/article2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

程式人雜誌 -- 2014 年 5 月號 (開放公益出版品)

14 |
15 |
16 | 21 |

使用 OpenCV 實作 AR -- 概念篇 (作者: Heresy Ku )

22 |

擴增實境(Augmented Reality,簡稱 AR、 維基百科 )這個技術,主要的概念就是把攝影機實際拍到的畫面,在顯示出來的同時,根據畫面的內容,加上其他的資訊、畫面,讓虛擬的物體,可以和真實的廠景融合在一起顯示。

23 |

由於把虛擬和現實融合,算是一個相當有趣、而且也相當特別的應用,所以其實在這方面的應用、研究,也都算滿多的;再加上現在許多行動手持裝置,不但配備了小型化的攝影機,也都有足夠的計算能力了,所以在近年來,也算是越來越熱門。不過實際上,這種概念並不算非常地新,在 1994 年就已經有人提出來了~到目前為止,也算是發展好一段時間了。

24 |
25 | 26 |
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 |
33 | 34 |
35 |

上面兩張圖,基本上是 OpenCV AR 能接受的兩種不同形式的 marker。 左邊的圖基本上是一種比較簡單的形式。他基本上是放大過的圖,原圖大小應該要是 10×10 個像素,每個像素都只有黑或白兩種可能;而由於實際上為了確保外圍的邊還是四邊形,所以外面是不能有白色的;也就是資訊都會記錄在裡面的 8×8、共有 64 個像素的矩形裡。(大小可以透過修改程式來調整) 右邊的則是一個黑底、再加上一般圖片,基本上只要確定黑底在外圍可以構成完整的四邊形、同時影像是方形的,就可以了~而雖然他是給彩色的圖片,不過為了減少環境光造成的影響、同時也減低計算量,所以實際上在內部處理的時候,都是轉換成灰階來做計算的。圖片的大小在程式中並沒有做額外的限制,但是由於圖片越大計算量會越多,所以建議不要用太大的圖檔當作 marker。

36 |

前者基本上比較單純,在程式裡面會轉換成編碼過的資料,直接進行比對;基本上不但效率比較好、準確性也比較高;但是相對的,可以變化的幅度就比較小了。

37 |

下方是在進行每一個畫面處理時,比較完整的流程圖:

38 |
39 | 40 |
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 |
45 | 46 |
47 |

上圖就是這邊的示意圖,左邊的藍色框框,就是找到的四邊形,而強制把他轉換成正方形的影像後,就會像右邊的圖一樣;之後就是透過這個轉換後的影像,來進行比對的。而針對不同類型的 marker,其實也有不同的比對方法,這個就等之後講到這部分再來說了~ 而這樣找出來的結果,可能會有不同的四邊形,都對應到同一個 template marker;而為了避免這樣的問題,這裡則會再去針對每一個 template marker、都去做一次比較,來找出相似度最高的四變形,並以此為最終的結果,存入 Recognized Markers 中。 如果找完後,完全沒有找到四邊形的話,他會去修改進行 binary threshold 時的 threshold 值(他是用亂數產生),來試著讓程式可以在下一個畫面,找到更多四邊形進行測試。 最後,下面就是最後結果的示意圖。左邊是用來偵錯的畫面,裡面的藍色和紅色框框,就是有偵測到的四邊形;由於只是在偵測四邊形,所以可以看到,裡面的手指間,也被認為是一個四邊形了~=而其中藍色是代表有偵測到,但是沒有找到對應的 template marker,而紅色則是有找到對應的,所以可以看到在右邊的結果畫面裡,「Tw」這個 Maker 已經被一個台灣的 3D 物件取代了~

48 |
49 | 50 |
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 |
32 |

程式人雜誌 -- 2014 年 5 月號 (開放公益出版品)

33 |
34 |
35 | 40 |

Z > b 還是 Z < b (作者:Wush Wu)

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 |

作者 : Wush Wu ()

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 |
32 |

程式人雜誌 -- 2014 年 5 月號 (開放公益出版品)

33 |
34 |
35 | 40 |

Visual Basic 6.0:奇數魔術方陣(Odd Magic Square) 詳細解法 (作者:廖憲得 0xde)

41 |

什麼是奇數魔術方陣 (?)

42 |
43 |

魔術方陣是許多人想要解決的一個古老的數學問題,您可能在一些雜誌上看過,也可能您的老師有介紹過。一個魔術方陣是在於安排數字在一矩陣[n x n]中,從1到n2, 每一數字僅出現一次, 而且,任一列、任一行或任一對角線的總和都相同。求總和的公式要證明為n [ ( n2 + 1) / 2 ],並不是很困難,若我們利用這個公式,對[5x5]矩陣而言,其總和為5 [ ( 52 + 1 ) / 2 ] = 65,其對應的魔術方陣輸出如下:

44 |
45 |
46 | 47 |
48 |
49 | 50 |
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 |
32 |

程式人雜誌 -- 2014 年 5 月號 (開放公益出版品)

33 |
34 |
35 | 40 |

以爬山演算法尋找函數最高點 - 使用 JavaScript+Node.js 實作

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 | 在上述演算法中,所謂的機率的方式,是採用 ![](../timg/658cc096866d.jpg) 這個機率公式,去判斷是否要從 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 | 在此,我們將以求解 ![](../timg/9205615feebe.jpg) 這個函數的最低點,看看上述演算法對多變數函數是否能正常運作。 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 | | ![](../timg/602ca9944b4f.jpg) | OR 的結合律 | 33 | | ![](../timg/d82c48c778d0.jpg) | OR 的交換律 | 34 | | ![](../timg/fa3ac188a800.jpg) | AND 的結合律 | 35 | | ![](../timg/f01700400c28.jpg) | AND 的交換律 | 36 | | ![](../timg/f5389bc891c5.jpg) | 狄摩根定律(1) | 37 | | ![](../timg/806819bdab01.jpg) | 狄摩根定律(2) | 38 | 39 | 說明:上述規則中的 ![](../timg/b6252c47c600.jpg) 代表邏輯或 (AND) (在程式語言裏常寫為 `&` 或 and), ![](../timg/8668dd090624.jpg) 代表邏輯或 (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 又引入了兩個量詞運算, ![](../timg/8b141f94d437.jpg) (對於所有) 與 ![](../timg/32ff223f4b92.jpg) (存在),透過謂詞的限定作用,以及這兩個量詞,Frege 架構出了這種具有函數的邏輯系統,後來被稱為一階邏輯系統 (First Order Logic)。 53 | 54 | 以下是我們將亞里斯多德的三段論,轉化為一階邏輯後,所寫出的一階邏輯規則。 55 | 56 | | 類型 | 語句 | 說明 | 57 | |--------|----------------------------------------------------|------------------| 58 | | 大前提 | ![](../timg/cfe6e9a974af.jpg) | 所有人都終會死亡 | 59 | | 小前提 | ![](../timg/f8c868b454f3.jpg) | 蘇格拉底是人 | 60 | | 結論  | ![](../timg/cae1b722691a.jpg) | 蘇格拉底終會死亡 | 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 | ![](../timg/21fa3adc303f.jpg) 105 | 106 | 假如我們將上述算式中的 ![](../timg/b4ffcfd79743.jpg) 寫為 A,將 ![](../timg/9018c1f2e831.jpg) 寫為 B,則上述算式可以改寫如下: 107 | 108 | ![](../timg/940130743f3d.jpg) 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 | --------------------------------------------------------------------------------