【Java】深深跪了,OJ題目Java與C運行效率對比(附帶清華北大OJ內存計算的對比)


看了園友的評論之后,我也好奇清橙OJ是怎么計算內存占用的。重新測試的情況附在原文后邊。

 

-------------------------------------- 這是切割線 --------------------------------------------

 

最近自學Java,試着用Java刷幾道OJ的題來熟悉基本語法。

起初幾道簡單題,沒太留意程序的執行效率。今天做了一道簡單的遞歸題,竟然運行超時了!由於本人算法方面功底太差,不懂的如何優化,想了個投機的辦法應付了過去。但又覺得不可思議,照以前用C語言刷OJ的經驗,不可能這種規模的數據就運行超時。於是我將代碼從eclipse復制黏貼到VC中,改成C程序,重新測試,結果驚人。

以下是詳細情況:

一、題目

題目位於:http://oj.tsinsen.com/ViewGProblem.page?gpid=A1069

問題描述

  幻方(magic square)是一個非常有趣的矩陣,n階的幻方表示一個n階矩陣,它的元素恰好是1~N^2,它的各行,各列,以及對角線之和都相等。下面是一個非常經典的3階幻方:
  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之前
輸入格式
  輸入僅包含一行K。
輸出格式
  4行4列的幻方,數之間用一個空格隔開,行末不要有多余的空格。
樣例輸入
1
樣例輸出
1 2 15 16
12 14 3 5
13 7 10 4
8 11 6 9
 
 
二、Java解答結果
這是我的java解(算法比較渣,風格比較丑,請多指教=。=)
 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 }
Java代碼

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 }
C代碼

這次的評測結果如下:

亮瞎狗眼有木有!!!

 

 

之前在看斯坦福《編程方法學》公開課視頻時,老師有說,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。
  輸出A+B。
輸入格式
  輸入包含兩個整數A,B,用一個空格分隔。
輸出格式
  輸出一個整數,表示A+B的值。
樣例輸入
5 8
樣例輸出
13
數據規模和約定
  -1,000,000,000<=A,B<=1,000,000,000。

我分別寫了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 }
C Code
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 Code

一、清華-清橙

清橙上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

我表示凌亂了。。。不知道這個是怎么計算的。。。

 

求問怎么自己測試。。。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM