看了園友的評論之后,我也好奇清橙OJ是怎么計算內存占用的。重新測試的情況附在原文后邊。
-------------------------------------- 這是切割線 --------------------------------------------
最近自學Java,試着用Java刷幾道OJ的題來熟悉基本語法。
起初幾道簡單題,沒太留意程序的執行效率。今天做了一道簡單的遞歸題,竟然運行超時了!由於本人算法方面功底太差,不懂的如何優化,想了個投機的辦法應付了過去。但又覺得不可思議,照以前用C語言刷OJ的經驗,不可能這種規模的數據就運行超時。於是我將代碼從eclipse復制黏貼到VC中,改成C程序,重新測試,結果驚人。
以下是詳細情況:
一、題目
題目位於:http://oj.tsinsen.com/ViewGProblem.page?gpid=A1069
問題描述
2 7 6
9 5 1
4 3 8
你的任務是找出字典序第K小的4階幻方。
這里的幻方的字典序定義為:把幻方按行優先排成一條N^2的序列后的字典序(如上面這個幻方,排成這樣一條序列:2 7 6 9 5 1 4 3 8)。
其中K<=100。
字典序的定義為:在某一系列字符串中,首先按照第一個字符明確其先后順序,如果第一個字符相同,則根據第二個字符的大小關系明確其先后關系。以此類推 。例如:
1 2 3 4 5 6 7 8 9 10 11在2 1 3 4 5 6 7 8 9 10 11之前
1 2 3 4 5 6 7 8 9 10 11在1 3 2 4 5 6 7 8 9 10 11之前
12 14 3 5
13 7 10 4
8 11 6 9

1 import java.util.Scanner; 2 public class Main { 3 static boolean[] used = new boolean[17]; 4 static int k; 5 static int[][] ary = new int[4][4]; 6 static boolean found = false; 7 8 public static void main(String[] args) { 9 Scanner input = new Scanner(System.in); 10 k = input.nextInt(); 11 for (int i = 1; i <= 16; i++) 12 used[i] = false; 13 if (k < 70) { 14 recursion(0); 15 } 16 else { 17 k -= 69; 18 ary[0][0] = 1; 19 ary[0][1] = 7; 20 used[1] = true; 21 used[7] = true; 22 recursion(2); 23 } 24 } 25 26 public static void recursion(int n) { 27 int x = n / 4, y = n % 4; 28 if (found) return; 29 if (n == 16) { 30 k--; 31 if (k == 0) { 32 printMagicSquare(); 33 found = true; 34 } 35 return; 36 } 37 for (int i = 1; i <= 16; i++) { 38 if (!used[i]) { 39 if (x == 3 && (ary[0][y] + ary[1][y] + ary[2][y] + i) != 34) 40 continue; 41 if (y == 3 && (ary[x][0] + ary[x][1] + ary[x][2] + i) != 34) 42 continue; 43 if (x == 3 && y == 0 && (ary[0][3] + ary[1][2] + ary[2][1] + i) != 34) 44 continue; 45 if (x == 3 && y == 3 && (ary[0][0] + ary[1][1] + ary[2][2] + i) != 34) 46 continue; 47 used[i] = true; 48 ary[x][y] = i; 49 recursion(n + 1); 50 used[i] = false; 51 } 52 } 53 } 54 55 public static void printMagicSquare() { 56 for (int i = 0; i < 4; i++) { 57 for (int j = 0; j < 3; j++) { 58 System.out.print(ary[i][j] + " "); 59 } 60 System.out.println(ary[i][3]); 61 } 62 } 63 }
main中原本是直接調用recursion(0),一運行,尼瑪運行超時!評測結果如下:
遂加入了那個if(k<70)的判斷語句,把上邊兩個較大的節點也通過了。
但這很不靠譜,因為這題簡單,k限制在100以內。再大一些的話,用條件判斷是解決不了問題的(除非有超強毅力寫一大堆if,全體遍歷一遍自動生成if語句倒是個辦法)。
三、C解答
於是我把代碼復制到VC中,刪掉class,把boolean類型改成int類型,去掉了那個if(k<70)的判斷語句,代碼如下:

1 #include <stdio.h> 2 3 int used[17]; //0-false 1-true 4 int k; 5 int ary[4][4]; 6 int found = 0; 7 8 void recursion(int n); 9 void printMagicSquare(); 10 11 int main() { 12 int i; 13 scanf("%d", &k); 14 for (i = 1; i <= 16; i++) 15 used[i] = 0; 16 // if (k < 70) { 17 recursion(0); 18 /* } 19 else { 20 k -= 69; 21 ary[0][0] = 1; 22 ary[0][1] = 7; 23 used[1] = 1; 24 used[7] = 1; 25 recursion(2); 26 }*/ 27 return 0; 28 } 29 30 void recursion(int n) { 31 int x = n / 4, y = n % 4; 32 int i; 33 if (found) return; 34 if (n == 16) { 35 k--; 36 if (k == 0) { 37 printMagicSquare(); 38 found = 1; 39 } 40 return; 41 } 42 for (i = 1; i <= 16; i++) { 43 if (!used[i]) { 44 if (x == 3 && (ary[0][y] + ary[1][y] + ary[2][y] + i) != 34) 45 continue; 46 if (y == 3 && (ary[x][0] + ary[x][1] + ary[x][2] + i) != 34) 47 continue; 48 if (x == 3 && y == 0 && (ary[0][3] + ary[1][2] + ary[2][1] + i) != 34) 49 continue; 50 if (x == 3 && y == 3 && (ary[0][0] + ary[1][1] + ary[2][2] + i) != 34) 51 continue; 52 used[i] = 1; 53 ary[x][y] = i; 54 recursion(n + 1); 55 used[i] = 0; 56 } 57 } 58 } 59 60 void printMagicSquare() { 61 int i, j; 62 for (i = 0; i < 4; i++) { 63 for (j = 0; j < 3; j++) { 64 printf("%d ", ary[i][j]); 65 } 66 printf("%d\n", ary[i][3]); 67 } 68 }
這次的評測結果如下:
亮瞎狗眼有木有!!!
之前在看斯坦福《編程方法學》公開課視頻時,老師有說,Java的運行效率比普通語言慢3倍左右。從上面的運行時間看的確差不多。但是在同一數量級上的。
但本題中,最深17層的遞歸,Java用了34MB內存,C語言只用了808KB,差距非常大。
以我目前所學的,有以下猜測:
1、Java運行於Java虛擬機上,裝載字節文件等本身需要消耗內存空間。
2、Java中的數組都是類,封裝了很多數據域和方法,占用一定內存空間;C中數組就是一段連續的內存空間,相對小巧。
3、Java中的棧中,遞歸時壓棧的信息豐富很多,除了跟C一樣的返回地址、參數信息,Java中還會保存異常處理鏈等地址、參數,所以每次遞歸會占用不少內存。
也不知上述猜測是否是主要原因,還請Java方面的前輩指教~
---------------------------------------------------這也是切割線--------------------------------------------------
鑒於想知道清橙OJ到底有沒有把虛擬機的內存也算進程序運行內存,我去提交了A+B Problem.
清華的清橙網:http://oj.tsinsen.com
北大的POJ:http://poj.org
A+B Problem是絕大多數在線評測網站的第一題,內容為:
問題描述
輸出A+B。
我分別寫了Java和C的代碼,如下:

1 #include <stdio.h> 2 int main() { 3 int a, b; 4 scanf("%d %d", &a, &b); 5 printf("%d\n", a+b); 6 return 0; 7 }

1 import java.util.Scanner; 2 public class Main { 3 public static void main(String[] args) { 4 Scanner input = new Scanner(System.in); 5 int a = input.nextInt(), b = input.nextInt(); 6 System.out.println(a+b); 7 } 8 }
一、清華-清橙
清橙上JAVA的評測結果為:
評測點編號 | 評測結果 | 得分 | 運行時間 | 內存使用 |
---|---|---|---|---|
1
|
正確
|
10.00
|
109ms
|
34.21MB
|
2
|
正確
|
10.00
|
93ms
|
34.23MB
|
3
|
正確
|
10.00
|
109ms
|
34.24MB
|
4
|
正確
|
10.00
|
125ms
|
34.20MB
|
5
|
正確
|
10.00
|
109ms
|
34.24MB
|
6
|
正確
|
10.00
|
125ms
|
34.24MB
|
7
|
正確
|
10.00
|
93ms
|
34.24MB
|
8
|
正確
|
10.00
|
109ms
|
34.24MB
|
9
|
正確
|
10.00
|
109ms
|
34.21MB
|
10
|
正確
|
10.00
|
93ms
|
34.21MB
|
C的評測結果為:
評測點編號 | 評測結果 | 得分 | 運行時間 | 內存使用 |
---|---|---|---|---|
1
|
正確
|
10.00
|
0ms
|
784.0KB
|
2
|
正確
|
10.00
|
0ms
|
784.0KB
|
3
|
正確
|
10.00
|
0ms
|
784.0KB
|
4
|
正確
|
10.00
|
0ms
|
784.0KB
|
5
|
正確
|
10.00
|
0ms
|
784.0KB
|
6
|
正確
|
10.00
|
0ms
|
784.0KB
|
7
|
正確
|
10.00
|
0ms
|
784.0KB
|
8
|
正確
|
10.00
|
0ms
|
784.0KB
|
9
|
正確
|
10.00
|
0ms
|
784.0KB
|
10
|
正確
|
10.00
|
0ms
|
784.0KB
|
跟上邊的遞歸題目比較,發現,java的內存占用始終在34MB左右,C的內存占用從808K降低至784K。
結論:清橙不靠譜。。。把java虛擬機也算進去了。。。
以前上程設導論課有些題目規定占用內存不許超過1MB,用java妥妥死翹。。。Otz。。。
二、北大-POJ
既然清華不靠譜,我就打開北大的也試試,程序還是上面兩個,結果如下:
Java: 3024K 735MS
C: 164K 16MS
我表示凌亂了。。。不知道這個是怎么計算的。。。
求問怎么自己測試。。。