我已经以各种方式解决了CodeEval上的一个简单问题,可以找到here的规范(只有几行)。
我已经制作了3个工作版本(其中一个在Scala中)并且我不理解我上一个Java版本的性能差异,我认为这是最好的时间和内存方式。
我还将其与Github上的代码进行了比较。以下是CodeEval返回的性能统计信息:
。版本1是在Github上找到的版本 。版本2是我的Scala解决方案:
object Main extends App {
val p = Pattern.compile("\\d+")
scala.io.Source.fromFile(args(0)).getLines
.filter(!_.isEmpty)
.map(line => {
val dists = new TreeSet[Int]
val m = p.matcher(line)
while (m.find) dists += m.group.toInt
val list = dists.toList
list.zip(0 +: list).map { case (x,y) => x - y }.mkString(",")
})
.foreach(println)
}
。版本3是我的Java解决方案,我期望它是最好的:
public class Main {
public static void main(String[] args) throws IOException {
Pattern p = Pattern.compile("\\d+");
File file = new File(args[0]);
BufferedReader br = new BufferedReader(new FileReader(file));
String line;
while ((line = br.readLine()) != null) {
Set<Integer> dists = new TreeSet<Integer>();
Matcher m = p.matcher(line);
while (m.find()) dists.add(Integer.parseInt(m.group()));
Iterator<Integer> it = dists.iterator();
int prev = 0;
StringBuilder sb = new StringBuilder();
while (it.hasNext()) {
int curr = it.next();
sb.append(curr - prev);
sb.append(it.hasNext() ? "," : "");
prev = curr;
}
System.out.println(sb);
}
br.close();
}
}
StringBuilder
来打印输出,并且在版本1中也是如此以下是我对这些结果的解释:
由于System.out.print
次呼叫数量过多,版本1速度太慢。此外,在非常大的行上使用split
(在执行的测试中就是这种情况)会占用大量内存。
版本2似乎也很慢,但主要是因为&#34;开销&#34;在CodeEval上运行Scala代码,甚至非常高效的代码运行缓慢
版本2使用不必要的内存来构建集合中的列表,这也需要一些时间,但不应该太重要。编写更高效的Scala可能会喜欢用Java编写它,所以我更喜欢优雅的性能
在我看来,版本3不应该使用那么多内存。使用StringBuilder
对内存的影响与在版本2中调用mkString
具有相同的影响
版本4证明对System.out.println
的调用正在减慢程序的速度
有人看到对这些结果的解释吗?
答案 0 :(得分:3)
我进行了一些测试。
每种语言都有一个基线。我用java和javascript编写代码。对于javascript,这是我的测试结果:
您可以看到,无论如何,运行时间至少为200毫秒,内存使用量约为5兆。此基线也取决于服务器的负载!曾经有一段时间代码被严重超载,因此无法在最长时间(10秒)内运行任何东西。
结论:我不会太担心CPU和内存使用情况以及排名。这显然不可靠。
答案 1 :(得分:2)
你的scala解决方案很慢,不是因为“CodeEval上的开销”,而是因为你正在构建一个不可变的TreeSet
,所以逐个添加元素。用
val regex = """\d+""".r // in the beginning, instead of your Pattern.compile
...
.map { line =>
val dists = regex.findAllIn(line).map(_.toInt).toIndexedSeq.sorted
...
应该减少约30-40%的执行时间。
相同的方法(构建列表,然后排序)可能会帮助你在“版本3”中使用内存(java集是真正的内存生成器)。在你的列表中给你的列表一个初始大小也是一个好主意(否则,每当它耗尽容量时它将增长50%,这在内存和性能上都是浪费的)。 600听起来像一个很好的数字,因为这是问题描述中城市数量的上限。
现在,既然我们知道上边界,那么更快更苗条的方法就是取消列表和盒装的Integeres,并且只做int dists[] = new int[600];
。
如果你想变得非常花哨,你还可以利用描述中提到的“路线长度”范围。例如,不是将int注入数组并进行排序(或保留树集),而是创建一个20,000位的数组(甚至是20K字节的速度),并设置您在读取时看到的那些... ...比任何解决方案都更快,内存效率更高。
答案 2 :(得分:1)
我尝试解决这个问题,并认为你不需要城市的名称,只需要排序数组中的距离。
它具有更好的738ms运行时间,以及4513792的内存。
虽然这可能无法帮助您改进代码,但这似乎是解决问题的更好方法。欢迎任何进一步改进代码的建议。
import java.io.*;
import java.util.*;
public class Main {
public static void main (String[] args) throws IOException {
File file = new File(args[0]);
BufferedReader buffer = new BufferedReader(new FileReader(file));
String line;
while ((line = buffer.readLine()) != null) {
line = line.trim();
String out = new Main().getDistances(line);
System.out.println(out);
}
}
public String getDistances(String s){
//split the string
String[] arr = s.split(";");
//create an array to hold the distances as integers
int[] distances = new int[arr.length];
for(int i=0; i<arr.length; i++){
//find the index of , - get the characters after that - convert to integer - add to distances array
distances[i] = Integer.parseInt(arr[i].substring(arr[i].lastIndexOf(",")+1));
}
//sort the array
Arrays.sort(distances);
String output = "";
output += distances[0]; //append the distance to the closest city to the string
for(int i=0; i<arr.length-1; i++){
//get distance between current element(city) and next
int distance_between = distances[i+1] - distances[i];
//append the distance to the string
output += "," + distance_between;
}
return output;
}
}