谈起java,给人的第一映像是什么?除了简单方便易上手,跨平台可移植,各种函数库支持类应有尽有不用重复造轮子这些特性之外,恐怕就是速度慢,占用内存高了吧。不可否认,java运行速度和内存占用方便确实无法比肩C/C++。随手举一个很直观的栗子,在ACM等其他算法竞赛中,普通给java的代码运行的时间往往都是C和C++ 2~~3倍,由此可见一斑。不过我们今天要说的就是,算法效率一样的情况下,如何去优化java的速度,让他在提供编码方便的同时,速度和内存上比肩一般的C和C++。
(/**博客记录了小菜寻找优化解法的过程 === 嫌拖拉的同学可以直接拉到最末看总结出的模板 */)
为了方便直观对运行时间和占用内存有直观的感受,我们拿 hdu 2602这个题目举栗子。因为在OJ上能直接给出程序的运行时间和占用内存。
hdu 2602 是一个没有任何变化的经典01背包问题,对于01背包问题,一维的dp做法在算法上已经到极致不能再优化了(参考dd巨巨的背包九讲)。在大家算法效率都是一样的情况下,我们看看速度和内存占用有多大差别。(提交语言都是java的情况下)
最开始我连续提交了好几次,虽然AC了,但是代码运行内存开销始终在1万3千多k,我继续往后翻了一下java的提交记录
大都是在1万多k 以上,虽然这数字看着有点大,但是既然那么多人都是这么大的内存占用,那就应该是java本身的问题了。 ========我继续往后翻=========可是当我继续往后翻的时候,就感觉不对了,同样是java,同样的算法,怎么还能有这么不可思议,整整少了一个数量级的内存占用,整整快了一倍的速度
快个几十毫秒,内存多个几十几百k 还能理解,但是这不是一个几十几百k了,这是上万的差别了,同样是用java同样的算法(应该是),居然出现了上万的差别。这就让人不能忍受了。
下面是我自己第一次提交的代码,用的是直观二维的做法。耗时468ms,占用内存24948k
在发现自己内存占用比人家高了那么多之后,我又换成一维的解法。 耗时546ms,占用内存 13908k。
速度还是不尽人意,我试着直接写在main 函数里面,省去函数调用看会不会快一点。这次是546ms, 13940k,并没有多大的变化。甚至还多了一点,我想应该是随机的吧。
后来丧心病狂的我又尝试把 i,j 还有数组的定义都放在循环外,然而时间还是微乎其微的变化,尝试着像以前一样固定数组,然和时间也还是没有变化。于是意识到这不是普通小优化能解决的了。
========================纠结纠结尝试中====================
由于在hdu 上不能发邮件问别人代码怎么写的也看不到别人代码,只能靠自己一步步的摸索了。百度了好久也没找到可行的解决方案,最后还是上codeforces看的别人的模板才发现的。
来看一下效果吧,参看cf上排名第二的peter的java代码之后, 运行时间降到了234ms,内存降到了 9588k
可是9k多k 还是比人家2k多有很大的差距,继续在 cf上找代码尝试 ======>
这次看的是排名第一的tourist巨巨的代码,运行时间降到了 249ms,内存占用降到了7504k,与之前相比整整降低了一倍,下面给出代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Scanner;
public class Main {
static InputReader in;
static PrintWriter out;
public static void main(String[] args) throws IOException {
in = new InputReader(System.in);
out = new PrintWriter(System.out);
int Case;
Case = in.nextInt();
//int i,j;
// int []v = new int[1010];
// int []w = new int[1010];
// int []m = new int[1010];
while(Case-- > 0){
int n,c;
n = in.nextInt();
c = in.nextInt();
int []v = new int[n];
int []w = new int[n];
int []m = new int[c+1];
for(int i=0;i=w[i];j--)
m[j]=Math.max(m[j], m[j-w[i]]+v[i]);
}
System.out.println(m[c]);
}
out.close();
}
static class InputReader {
BufferedReader br;
public InputReader(InputStream stream) {
br = new BufferedReader(new InputStreamReader(stream));
}
public int nextInt() throws IOException {
int c = br.read();
while (c <= 32) {
c = br.read();
}
boolean negative = false;
if (c == '-') {
negative = true;
c = br.read();
}
int x = 0;
while (c > 32) {
x = x * 10 + c - '0';
c = br.read();
}
return negative ? -x : x;
}
public long nextLong() throws IOException {
int c = br.read();
while (c <= 32) {
c = br.read();
}
boolean negative = false;
if (c == '-') {
negative = true;
c = br.read();
}
long x = 0;
while (c > 32) {
x = x * 10 + c - '0';
c = br.read();
}
return negative ? -x : x;
}
public String next() throws IOException {
int c = br.read();
while (c <= 32) {
c = br.read();
}
StringBuilder sb = new StringBuilder();
while (c > 32) {
sb.append((char) c);
c = br.read();
}
return sb.toString();
}
public double nextDouble() throws IOException {
return Double.parseDouble(next());
}
}
}
200多的速度和7000多的内存和极致的142ms度与2712的内存使用相比还有差距,不死心,还想继续降低下去来着,可是无奈cf上前面两页巨巨的java代码几乎都尝试过了一遍,还是没能降低下来,那就只好先暂时这样吧。更加好奇142ms 的极致速度是怎么出来的,真的堪比C/C++
要是哪位亲有代码一定要贴出来学习一下啊.......
下面给出IO读取加速的普遍模板
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Scanner;
public class Main {
static InputReader in;
static PrintWriter out;
public static void main(String[] args) throws IOException {
in = new InputReader(System.in);
out = new PrintWriter(System.out);
/*
*
* 这里补充你的实际代码 读取输入采用 in.nextInt();的形式
*
* */
out.close();
}
static class InputReader {
BufferedReader br;
public InputReader(InputStream stream) {
br = new BufferedReader(new InputStreamReader(stream));
}
public int nextInt() throws IOException {
int c = br.read();
while (c <= 32) {
c = br.read();
}
boolean negative = false;
if (c == '-') {
negative = true;
c = br.read();
}
int x = 0;
while (c > 32) {
x = x * 10 + c - '0';
c = br.read();
}
return negative ? -x : x;
}
public long nextLong() throws IOException {
int c = br.read();
while (c <= 32) {
c = br.read();
}
boolean negative = false;
if (c == '-') {
negative = true;
c = br.read();
}
long x = 0;
while (c > 32) {
x = x * 10 + c - '0';
c = br.read();
}
return negative ? -x : x;
}
public String next() throws IOException {
int c = br.read();
while (c <= 32) {
c = br.read();
}
StringBuilder sb = new StringBuilder();
while (c > 32) {
sb.append((char) c);
c = br.read();
}
return sb.toString();
}
public double nextDouble() throws IOException {
return Double.parseDouble(next());
}
}
}