跳到主要內容

發表文章

目前顯示的是 2008的文章

UVa 110 Meta-Loopless Sorts

相當有趣的一題....寫程式產生器
用自創的Modified-Permutation Algo ( ̄▽ ̄#)﹏﹏
第一次寫有點痛苦,過了幾天醞釀過想法後,寫起來很順很快就AC了
距離連號解題剩下最後一題107了..

UVa 109 SCUD Busters

解題策略典型的凸包 (Convex Hull ) 問題。我是採用 Graham's Scan 來解。作法可以參考維基條目 Graham's Scan,我就是照著條目裡的算法原封不動實做的。

本題要找出所有被飛彈摧毀的王國的國土面積總和。過程中一共有兩件事要解決,第一個要知道每個王國的國土面積,第二個要能判斷飛彈落點有沒有打中王國。

求國土面積,要先對王國內的所有的發電廠位置求凸包,然後由凸包去計算多邊形面積 (題目頁有給多邊形面積公式) 。 判斷飛彈落點是否落在國土內,也是以凸包為基礎,沿著凸包的每個邊判斷,如果飛彈落點都在邊的左側,那就表示打中了!
注意使用Graham's Scan 時要特別注意共線問題,尤其是起始點三點共線,例如 (0,0) (1,1) (2,2) (1,3) ,求出來的凸包應該是 (0,0) (2,2) (1,3)。
閒聊@2011.10.07
有點囉嗦的題目。2008年底我第一次解這題,因為過程有點曲折,所以花了好幾天才解出來,而且一直搞不定 std::sort( ) 的判斷函數,現在重寫一遍,感覺比較清爽一些了!

線性代數的世界作者 Dr. Strang 妙語如珠

"you can't add apples and oranges."「你不能把蘋果和橘子加在一起」
--談為什麼需要向量

The central porblem of linear algebra is to solve a system of equations.
線性代數的核心問題就是要解方程組!

It is also common sense: If you put on socks and then shoes, the first to be taken off are the ___.
常識也告訴我們:假如你先穿襪子再穿鞋,那麼脫的時候,首先該脫掉___。
--談為何AB的反矩陣是B-1A-1

Ax combines the columns of A while xTAT combines the rows of AT
Ax線性組合了A的諸行,而xTAT線性組合了AT的諸列。
--談為何(AB)T = BTAT
最近看Gilbert Strang寫的『Introduction to Linear Algebra』,Strang教授講法生動,不論是趣味的例子,或者是一句話直搗觀念核心,都常讓我拍案叫絕。
比起補習班名師的講義,研讀起來樂趣大多了。有機會再補上更多句子。

UVa 104 Arbitrage

解題策略Some Hints about uVA 104 
gits大大說得很清楚,我就不囉唆作法了。

閒聊苦思良久的UVa 104終於在 gits 幾乎手把手的帶領下寫出來了,了結了多天以來的心中懸念。XDDD稍微變化題,馬上就把我土法煉鋼學會的Floyd-Warshall 打回原型。連F-W要用二維還是三維陣列來建表都搞不清楚,唉,由這題可以知道我的DP/Graph有待磨練。

只有與問題面對面的對決,在測資跟Bug中打滾一番後,才能對Algorithm有更深刻的體會吧。這也是我寫ACM的目的與樂趣所在。不過...這題還是有點心虛阿.....orz 只能說敗給你了Modified Floyd-Warshall,還未夠班....總有一天我要能秒殺這題。


[ACM] Some hints about uva 104 (Modified Floyd-Warshall)

by gits
Original Thread http://online-judge.uva.es/board/viewtopic.php?t=7292

Well, I'll assume you understand what the problem asks. You have to find the shortest sequence that yelds a profit (not the one with the greatest profit!). If there is more then one sequence with the same length, any of those is valid.

Now, you can't just try with brute force (trying all combinations) because it'll be too slow and you'll get Time Limit Exceeded. However, there's a well known algorithm, Floyd-Warshall, which will find all the shortest paths between every node to the others in just O(n^3) time. You can find more info about F-W in the net. In my previous post I said how you have to change the general F-W algorithm to work for this particular problem...

As for floating point errors, most numbers representation isn't totally acurate; for instance, 0.1 is usually stored as 0.10000000000000001. After some operations, the error may influence the final result. Again, search th…

UVa 112 Tree Summing

終於AC了..這次最大的心得就是,腦袋不清楚的時候不要寫程式。如果沒辦法理清思路,找出邏輯上的漏洞,那麼debug也只是亂試一通而已,一點幫助都沒有(只有浪費時間)。寫到後來連錯誤的測資也在試,現在想起來真是笨阿

我用recursive的方法去追蹤整顆樹。測資是pre-order sequence
所以遞回順序是root ,left-child , right-child

很不清楚核心Algo:
int traversal(int sum, int target){ 抓左括號 抓root lc = traversal(int sum+root, int target) //向left-child遞回 rc = traversal(int sum+root, int target) //向right-child遞回 抓右括號 如果是Leaf? 判斷總和相不相等? 如果是Internal Node,將子樹的結果繼續往上回傳。 }
另外一提,這次我參考了某份AC Code,用non-recursive(Stack)作法,但是因為用了STL stack;,最後執行時間0.510s,慢了我五倍之多。我用Recursive反而執行時間只有0.100s(Ranking 90,再進百大XDDDD)有點意外。證明一件事:Recursive不一定慢,但是STL=慢。

UVa 108 Maximum Sum

解題策略這題AC的關鍵是時間,用暴力法O(n6) 六層迴圈去解鐵定超時。已知最佳的解法是 O(n3) ,不過一般用O(n4) 就可以通過了。

我也是用 O(n4) 的解法,首先用四層迴圈窮舉子矩陣的左上角(x1,y1) 跟右下角 (x2,y2) 的位置,一旦給定了子矩陣後,就要 O(1) 時間得到子矩陣的和。

要達成這個目標可以透過 sum table 來達成,我說明一下我的方法, sum table ( i, j ) 每個位置儲存的值的是原矩陣 (1,1) ~ (i, j) 之間的元素總和。像下圖的 sum table ( 2, 3 ) 的值就是 原矩陣 (1, 1) ~ (2, 3) 之間的元素和。


一旦建好 sum table,那接下來就可以簡單透過這 D - B -C + A 這四個sum table的元素直接得到子矩陣的和了。
閒聊最後執行時間0.052 過關....改天再將作法補完,我好睏....晚安。原本用暴力法去跑 100x100 的矩陣,竟然要253秒(大汗)。

UVa 10751 Chess Board v.2

解題策略
注意修正了兩個問題,終於AC了
1. 雖然題目頁說只要印小數點後三位,但其實要印四位。
2. 各個output case"之間",要隔一個blank line。
但只有case間要空行,最後一個case印完後不能有多餘的空行。 (很嚴格的的格式要求...)

新版的AC Code

UVa 111 History Grading

這題說穿就是LIS,對於已經精通LIS的我自然是小菜一碟(咦?)
不過這題有個陷阱,如果沒仔細讀懂題目的話,會以為只要做一次轉換,其實上要做兩次轉換。
第一次把rank轉成真正的event sequence,(rank[] => seq[])
第二次才轉成可以做LIS的形式。(seq[] => seq2[])
剛開始送了兩個WA,後來看事有悉蹺,(sample output怪怪的),改了一下就AC了。

UVa 103 Stacking Boxes

題目講解 這題給你一堆多維的箱子,要找出最長的裝箱序列,這個最常裝箱序列就是像俄羅斯娃娃那樣,大箱子裡面放入小箱子,一層包一層,必須要可以裝最多層這樣。

這題的麻煩就是各個箱子之間不一定有的大小關係 (數學上打這叫偏序集 Partial Order Set ,不過這兒我們先不管 ),比方說有兩個三維箱子 BoxA(1,4,5) 、BoxB(2,4,6) ,B箱比較大,所以 BoxA 可以裝進 BoxB 裡,那麼我們說 BoxA < BoxB 。

但來看 BoxC(1,5,6) 跟 BoxD (2,4,5) 呢? 我們既不能說BoxC < BoxD ,也不能說 BoxC > BoxD,BoxC 與 BoxD 兩者之間就沒有大小關係。

一個簡單的表示法就是有向圖(Directed Graph)
比如說 A(1,2,3) B(2,4,6) C(2,3,7) D(50,50,50) E(10,10,100) 的有向圖表示法:



到此為止,本題的最長裝箱序列,已經有兩種解法,第一個方法就是找圖的最長路徑,稍微修改 Warsahll algorithm 來解,第二個方法是跑 LIS 。

我這裡用 LIS(最長上昇子串列) 解法。

//先定義一個box struct struct Box { int no; int edge[10]; }; Box box[30];
LIS 法的關鍵是要先對箱子排序,確保小箱子一定在大箱子前,至於沒有大小關係箱子的就隨便放沒關係。這個排序以圖的角度是拓樸排序,以字串的角度是字典排序,兩者都行。

sort(box[i].edge) for all i //先對箱子內部的邊長排序 int lexico_less(const Box& a, const Box& b) { for(int i=0;i<dimension;++i) if( !(a.edge[i]<=b.edge[i]) ) return false; return true; } sort(box) by lexico_less(); //再用字典序排箱子 一切都準備好後,就可以直接套用LIS解了。
// LIS int length[MAX_BOX]; int …

[Algo] String Matching - KMP Algorithm

看了兩次,可是每次都忘記,所以做個筆記
s: input string
p: pattern

Failure Function的值k,就是下次要從p(k)開始matching

舉例

Pattern A B C A B C D
failure -1-1-1 0 1 2-1

Case I:
Input string
ABCDADFGACEDAABBBB
ABCABCD

紅色=not matching
step1: 看最後一個matching字元 (綠色的字) 的failure function value= f
step2: 將p(f)對齊綠色的位置 ( p(i)代表p的第i個字元 )

ex. f(C) = -1 ,所以p(-1)對齊上面那排的C
ABCDADFGACEDAABB >>>ABCABCD
Case II:
ABCABCEACAADDBBBABCABC
ABCABCD

f(C)=2 ,所以p(2)對齊上面那排的綠色C

ABCABCEACAADDBBBABCABC
>>>ABCABCD


講的不是很有條理,不過我先給自己看懂就好= =
改天再來整理的清楚一些

[MASM] IO Patterns

讀取一串由空格分隔的整數 ex. 10 20 30 40 50

.data buffer BYTE 512 DUP(0) array DWORD 30 DUP(0) len DOWRD 0 ;//陣列長度 .code mov edx,OFFSET buffer mov ecx, (SIZEOF buffer) call ReadString ;//Read string to buffer mov esi,OFFSET buffer mov edi,OFFSET intArray ;//set addr, esi=buffer, edi=array mov eax,0 ;//init eax start: movzx edx,BYTE PTR[esi] cmp edx,0 ;//end of string je over cmp edx,32 ;//compare to space je stateB jmp stateA stateA: ;//is digit sub edx,48 ;//ascii edx-'0' mov ebx,edx ;//because mul use edx, back up edx mov ecx,10 mul ecx ;//eax=eax*10 mov edx,ebx add eax,edx inc esi jmp start stateB: ;//is space mov [edi],eax ;//把eax存進array裡 mov eax,0 add edi,4 inc esi inc len jmp start over: mov [edi],eax inc len 觀念很簡單
edx是Input String的位址,結果則是存在eax裡
每次從edx抓一個byte進來,判斷是1.數字 或2.空白
1.如果是數字就跳到 StateA,把eax原來的數字x10,然後加上新的數
2.如果是空白,代表一個整數結束,跳到StateB,將eax存進intArray裡,重新開始另一個循環直到edx讀到0,也就是字串的結尾為止。

Assembly Language Patterns

計算陣列長度list Byte 10,20,30,40 //list 是array的起始位址 ListSize = ($ - list) //ListSize是一個Constant //$代表Current Location counter list DWORD 1000,2000,3000 ListSize = ($-list)/4 //DWORD大小是4Byte,所以除以四
雙層迴圈mov ecx,5 //外圈次數 L1: mov count,ecx mov ecx, 7 //內圈次數 L2: //do something here loop L2 mov ecx,count loop L1

if statementif (op1==op2){ statement1; }
else {statement2; }

cmp op1,op2 // op1==op2 je _true // jump if equal 相等的話就跳TRUE label jmp _false // 否則就無條件跳到FALSE label _true: //statement1 jmp next //跳過False部份 _false: //statement2 next:
While loopwhile( var1>var2 ){
//statement
}
mov eax,var1 _while: cmp eax,var2 jle endwhile //jmp if less and equl //原本是var1>var2,條件成立就進while //這裡改成var1<=var2,條件成立就離開while //statement jmp _while _endwhile:
for loopfor(int i=0;i<9;i++){ statement; }
mov ecx,0 //把ecx當作計數器i _for: cmp ecx,9 jge _endfor //jump if greater & equal //statement; inc ecx…

[ACM] 自己整理一些有用的網站

=解題提示=UVa官方論壇:全世界UVa討論的集散地,當你題目解不出來時,第一件事就該來這裡搜尋該題目的討論串,通常會找到很多好心人提供的測資跟提示。Methods to Solve全世界最大的UVa解題提示網站,收錄的題目數量很多,品質也不錯。Algorismist這是一個關於演算法的維基網站,也有收錄 UVa 的解題提示,如果你有能力可以參與編輯條目,讓它變得更好。UVa Toolkit一般來講UVa題目頁上的範例測資都太簡單,聊勝於無。而 UVa Toolkit 這工具恰好補足了這方面的缺憾,你可以餵給它任何測資,並取得正確的對應輸出,對釐清題意很有幫助。uHunt非常非常好用的 UVa 解題工具網頁,可以很輕鬆的搜尋題目,安排自己解題的方向,瀏覽自己過去的解題紀錄等等。 =題目中譯=Lucky貓著名的ACM題目中譯網,及解題提示。Lucky貓Mirror站同上,有難度提示。ACM中譯少量題目中譯。ZeroJudgeZeroJudge是非常優秀的國產程式解題網,裡面有一區專門收錄UVa題目中譯。 =算法教學=DJWS的網路日誌資源豐富的網站,整理了很多的算法教學,以及各種ACM競賽的資料。 C語言考古題 & C的解題大量題目教學 Art of Programming Contest for uva淺顯的ACM入門書,免費線上版。NACOW對岸關於程式設計與解題的wiki Infinite Loop提供許多教學及ACM Tips, Sources。 =API Reference=Cplusplus.com簡潔清楚的C/C++標準函式參考手冊,範例碼清楚實用,值得看看,我自己把這兒當後花園逛了。 =題目列表=ACM熱題排行榜芭樂題排行榜,有哪些熱門題你還沒解過呢XD =討論論壇=NPSC補完計畫針對NPSC的解題網站OIBH對岸關於資訊奧林匹亞競賽討論的論壇algogeeks Google group about algorithms. =解題強者= 這部分網站就請各位審慎參觀了,大部分ACMer的程式碼都寫得不太好讀。
心如止水350+題目解答,有基本的題目分類。Nothing is Everything少量UVa Source,品質不錯小璋丸UVa解答 Java版JAVA解題的強者。摸索C語言英雄出少年,解題高手,以Zero Judge為主。朱色虫居

[ACM] 加速法

觀念

有修過OS應該都知道,因為執行IO動作會牽涉到interupt、system call 等等機制,花掉非常多的時間。所以對大部分的ACM題目來說,減少IO的次數是加速的好方法。

關鍵點 : 「減少IO次數」


Buffered Input: 不要用scnaf() 或者cin,用fgets一次讀進來,再parse字串。Buffered Outout: 先用StringStream輸出至memory,最後再一次印出。Buffered大小約在5000Byte左右,不要太小,也不要太大,導致Memory要做Swapping或paging。
其他小方法:

1. CPU通常會對4Bytes運算做最佳化(現在的32bit系統,應該也是對4Byte資料做運算最快),所以處理資料盡量用4Bytes當一個單位。

2. 但是超過一定的資料量大小,通常長度超過十萬的陣列,那麼整個陣列的體積反而影響比較大,這時能用char, short 比較快。

讀書心得: 世紀末軟體革命復刻版

本書作者之一賴名宗曾說過一句話,我覺得非常棒:「(學習) 什麼時候有了正確的觀念和目標,就什麼時候入門了。」那麼本書最大特點,就是建立正確物件導向觀念。

我早些年曾經在網路上看過一篇文章叫「不要從程式語言學習物件導向」,第一次讀時我看不懂。對我來說,物件導向不就是那些 class、new、public  等等的語法嗎?不從程式語言學還能從哪裡學呢?大多數坊間書籍只從「程式語法」的角度切入物件導向,受此影響的人(包括我)自然也曾經以為學會了 JAVA 就已經摸透物件導向了。

這個困惑直到我讀完了「世紀末軟體革命」後才稍微解開,物件導向之所以長青不倒的原因,是因為背後有一套組織程式的世界觀: 「程式是為了模擬世界」,「程式由物件組成,物件之間互相發送訊息」,而語法不過是實現整個思考架構的最末節。這樣來看,本書的撰寫順序「先OO,後C++」,這個次序才真的能抓到物件導向思考的脈絡。

本書從背景遠因─軟體危機的歷史背景開始,引出物件導向思想發芽,背後的理念精神「電腦運算的是為了模擬真實世界」,然後介紹比較完整的物件導向理論,最後,才以這樣的知識基礎下介紹C++ OOP。此時來看 C++ 的OO部份,眼光高度就有差異了,也可以知道C++並沒有完美的實踐所有的OO精神。

從思想起源再到程式實踐方法,一路娓娓道來,深入淺出,故事跟插圖很多,程式碼卻很少,架構起一個堅實又不艱澀的的地基。大學寫程式寫了四年,但是要問我何時真正開始瞭解物件導向? 我會說從讀過這本書開始。

另外這本書給我的感動並不只單純在技術上,這三位作者寫書的時候,都還只有20歲,文字語氣中或多或少都還帶著 BBS 上的那種大學生的說話口吻,就像賀元說,他們當初寫書的動機,也只是看了一些好書想要把想法分享給大家而已,但是這本書暢銷到隔了十年還能再出「復刻版」,讓我思考,也許我自己能做的更多,年輕人就該帶有一些瘋狂的想法然後瘋狂的行動。

侯捷的二版序八個字道盡我心中的感覺 『鷹揚年少、氣吞牛斗』。

本書有一些小缺點,像涵蓋的議題很大,想要包山包海,結果貪多嚼不爛,有些細節就模糊帶過。作者之一賴明宗現在是PTT CSSE版主,可以去朝聖一下。

C & C++ 字串兩三事

strlen( ) 不算'\0' ,但是會算'\n'。所以strlen("hello") 結果是5,strlen("hello\n") 結果是6。換行符號也算一個char,很容易被遺忘。宣告string的時候,通常用的格式 char[MAXLINE+1],最後那個1,就是拿來放字串的結尾'\0'的。從stdin讀字串的方法:
cin>>s; 讀一個單字,遇到空格or換行停止,空格or換行不會存進字串s。
cin.getline(s, length) 讀一行,遇到換行才停。
fgets(s, sizeof(s), stdin) 最後那個'\n'也會被包含在輸入字串裡。
cin.getline(s,length)則不會包含'\n'

JOGL - Java與OpenGl(四)

準備實戰

當你熟悉了前面的例子以後,我們來畫一張漂亮的圖。

這就是你接下來的程式。請確保你輸入了所有的code到你的編輯器中。調校這些程式可以使你快速地明白它們的工作原理。

import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.media.opengl.*; /** * This is a basic JOGL app. Feel free to reuse this code or modify it. * 這是個基礎的JOGL程序,你可以隨意重用該代碼或者修改它。 */ public class SecondJoglApp extends JFrame { public static void main(String[] args) { final SecondJoglApp app = new SecondJoglApp(); // show what we've done // 看一下我們做了什麼 SwingUtilities.invokeLater(new Runnable() { public void run() { app.setVisible(true); } }); } public SecondJoglApp() { // set the JFrame title // 設置JFrame標題 super("Second JOGL Application"); // kill the process when the JFrame is closed // 當JFrame關閉的時候,結束程式 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // only three JOGL lines of code ... and here they are //…

JOGL - Java與OpenGl(三)

一個好用的程式樣板

讓我們繼續來看兩個class,當你對JOGL感到有點頭昏腦脹時,你也許會發現這個程式樣板相當好用。我已經不止一次把它們當成樣板用了。你可以隨意地使用它們。

這個樣板由兩個class組成。第一個是如下所示的SimpleJoglApp,在簡短說明之後,第二個是SimpleGLEventListener。你必須建立兩個class來編譯樣板。主程式如下:

import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.media.opengl.*; /** * This is a basic JOGL app. * Feel free to reuse this code or modify it. * 這是個基本的JOGL程式,你可以隨意重用或修改它。 */ public class SimpleJoglApp extends JFrame { public static void main(String[] args) { final SimpleJoglApp app = new SimpleJoglApp(); // show what we've done // 看一下我們做了什麼 SwingUtilities.invokeLater(new Runnable() { public void run() { app.setVisible(true); } }); } public SimpleJoglApp() { // set the JFrame title // 設置JFrame標題 super("Simple JOGL Application"); // kill the process when the JFrame is closed // 當JFrame關閉的時候,結束程式 setDefaultCloseOperation(J…

JOGL - Java與OpenGl(二)

Hello World!

我是一個傳統的人,所以我們理所當然從「Hello World」開始。這個「Hello World」程式將檢驗我們的安裝是否全部或只有一部分安裝正確。回憶一下安裝JOGL的2個部分,分別是jar文件裡的Java Library以及其它的Native code。

以下就是我們的程式:
import javax.media.opengl.*; public class HelloWorld { public static void main (String args[]) { try { System.loadLibrary("jogl"); System.out.println("Hello World! (The native libraries are installed.)"); GLCapabilities caps = new GLCapabilities(); System.out.println("Hello JOGL! (The jar appears to be available.)"); } catch (Exception e) { System.out.println(e); } } }
首先,這個程式測試 Native code 和 Java library 是否已經安裝正確了。只有當jogl.jar和Native code(諸如gluegen-rt.dll或者 jogl.dll)兩者都安裝好了的時候,JOGL才算安裝完全。如果native code不可用,程式會拋出 java.lang.UnsatisfiedLinkError例外。如果classpath裡沒有安裝JAR,程序則根本編譯都過不了。Javac編譯器會報諸如此類的錯「javax.media.opengl Package不存在」。當這個程序編譯通過且運行起來沒有異常的話,你可以繼續學習JOGL了。

(按:已修改為相容JOGL 2008的code)

JOGL - Java與OpenGl(一)

在這篇文章裡,摘錄了《學習Java對於OpenGL的綁定》。作者Gene Davis解釋了如何開始用Java對於OpenGl的綁定開發圖形增強的程式。

這些年來,為了創建一個圖形增強的程式,從而出售給使用各種不同操作系統的用戶,程式員有一個選擇——OpenGL。GL代表圖形庫(graphics library)。OpenGL是SGI(美國圖形工作站生產廠商)的註冊商標。OpenGL顯示了它是一個跨平台的C語言編程API。但是事實上,在程式介面上,它是一個獨立於硬體的規格。

OpenGL用於繪圖,速度非常快。大多數場合下,它使用硬體加速。似乎OpenGL可以實現一切你想要完成的圖形界面。

不幸的是,OpenGL是為C語言而寫的。不得不承認,C語言不是用來編寫複雜應用程式的流行語言。關於OpenGL一個最大的缺點就是:如果你不創建一個視窗(用來把你的圖形放入其中),你就什麼都做不了。但是OpenGL沒有提供給你創建視窗的方法。這使得OpenGL對於初學者來說顯得比較難。

幸運地是,出現了GLUT (OpenGL Utility Toolkit)(OpenGL工具包)。它被用來輕鬆應對視窗、按鈕以及使用者事件。儘管如此,對於想要使用物件導向的程式員來說,學習用C或者C++來寫OpenGL程式仍然是一件痛苦的事。


然後出現了JOGL

Java 也許是最流行的真正物件導向程式語言。有許多用Java去結合OpenGL的嘗試,但是第一個被大家認可並注意的OpenGL的Java綁定 (Java Bindings for OpenGL), 或者稱為JOGL。因為它得到Sun(Java的創建者)和SGI(OpenGL的創建者)的支持。

如今,Sun的遊戲開發小組正在開發JOGL。它以肯·拉塞爾和克里斯·克蘭開發的Jungle開始。拉塞爾是Sun的員工,研發「HotSpot虛擬機器」,擁有多年的3D經驗。克蘭則研發「荒謬的遊戲」,對3D圖學也相當有經驗。

我個人對他們以及所有其它在JOGL上工作的人表示感謝。曾經有許多人嘗試通過友好的Java API來使用OpenGL——其中包括Java 3D, OpenGL for Java Technology (gl4java)(給Java技術的OpenGL),Lightweight Java Game Library (LWJGL)(輕量級的Ja…