java - 如何减少此程序的执行时间

时间:2011-12-09 10:10:02

标签: java performance algorithm loops

int n, k;
int count = 0, diff;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] input;
input = br.readLine().split(" ");
n = Integer.parseInt(input[0]);
int[] a = new int[n];
k = Integer.parseInt(input[1]);
input = br.readLine().split(" ");
for (int i = 0; i < n; i++) {
  a[i] = Integer.parseInt(input[i]);
     for (int j = 0; j < i; j++) {
        diff = a[j] - a[i];
        if (diff == k || -diff == k) {
           count++;
        }
     }
}
System.out.print(count);

这是我打印特定差异计数的示例程序,其中n范围<= 100000 现在的问题是减少这个程序的执行。如何才能更好地减少运行时间。

提前感谢您的建议

8 个答案:

答案 0 :(得分:4)

从文件中读取数字并将它们放在Map中(数字作为键,其频率作为值)。迭代它们一次,并为每个数字检查地图是否包含添加了k的数字。如果是这样,请增加您的柜台。如果您使用HashMap,那就是O(n),而不是算法的O(n^2)

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int k = Integer.parseInt(br.readLine().split(" ")[1]);
Map<Integer, Integer> readNumbers = new HashMap<Integer, Integer>();

for (String aNumber : br.readLine().split(" ")) {
    Integer num = Integer.parseInt(aNumber);
    Integer freq = readNumbers.get(num);
    readNumbers.put(num, freq == null ? 1 : freq + 1);
}

int count = 0;
for (Integer aNumber : readNumbers.keySet()) {
    int freq = readNumbers.get(aNumber);
    if (k == 0) {
        count += freq * (freq - 1) / 2;
    } else if (readNumbers.containsKey(aNumber + k)) {
        count += freq * readNumbers.get(aNumber + k);
    }
}
System.out.print(count);

编辑已修复重复且k = 0

答案 1 :(得分:3)

以下是使用HashSet,TIntIntHashSet和原始解决方案的@ Socha23解决方案的比较。

对于100,000个数字,我得到以下内容(没有阅读和解析)

对于100个唯一值,k = 10

Set: 89,699,743 took 0.036 ms
Trove Set: 89,699,743 took 0.017 ms
Loops: 89,699,743 took 3623.2 ms

对于1000个唯一值,k = 10

Set: 9,896,049 took 0.187 ms
Trove Set: 9,896,049 took 0.193 ms
Loops: 9,896,049 took 2855.7 ms

代码

import gnu.trove.TIntIntHashMap;
import gnu.trove.TIntIntProcedure;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

class Main {
    public static void main(String... args) throws Exception {
        Random random = new Random(1);
        int[] a = new int[100 * 1000];
        int k = 10;
        for (int i = 0; i < a.length; i++)
            a[i] = random.nextInt(100);

        for (int i = 0; i < 5; i++) {
            testSet(a, k);
            testTroveSet(a, k);
            testLoops(a, k);
        }
    }

    private static void testSet(int[] a, int k) {
        Map<Integer, Integer> readNumbers = new HashMap<Integer, Integer>();
        for (int num : a) {
            Integer freq = readNumbers.get(num);
            readNumbers.put(num, freq == null ? 1 : freq + 1);
        }

        long start = System.nanoTime();
        int count = 0;
        for (Integer aNumber : readNumbers.keySet()) {
            if (readNumbers.containsKey(aNumber + k)) {
                count += (readNumbers.get(aNumber) * readNumbers.get(aNumber + k));
            }
        }
        long time = System.nanoTime() - start;
        System.out.printf("Set: %,d took %.3f ms%n", count, time / 1e6);
    }

    private static void testTroveSet(int[] a, final int k) {
        final TIntIntHashMap readNumbers = new TIntIntHashMap();
        for (int num : a)
            readNumbers.adjustOrPutValue(num, 1,1);

        long start = System.nanoTime();
        final int[] count = { 0 };
        readNumbers.forEachEntry(new TIntIntProcedure() {
            @Override
            public boolean execute(int key, int keyCount) {
                count[0] += readNumbers.get(key + k) * keyCount;
                return true;
            }
        });
        long time = System.nanoTime() - start;
        System.out.printf("Trove Set: %,d took %.3f ms%n", count[0], time / 1e6);
    }

    private static void testLoops(int[] a, int k) {
        long start = System.nanoTime();
        int count = 0;
        for (int i = 0; i < a.length; i++) {
            for (int j = 0; j < i; j++) {
                int diff = a[j] - a[i];
                if (diff == k || -diff == k) {
                    count++;
                }
            }
        }
        long time = System.nanoTime() - start;
        System.out.printf("Loops: %,d took %.1f ms%n", count, time / 1e6);
    }

    private static long free() {
        return Runtime.getRuntime().freeMemory();
    }
}

答案 2 :(得分:1)

由于split()使用正则表达式来分割字符串,因此您应该测量StringTokenizer是否会加快速度。

答案 3 :(得分:1)

您正在尝试查找具有差异k的元素。试试这个:

  • 对数组进行排序。
  • 您可以在排序后通过两个指针进行一次传递,并根据差异是大于还是小于k来调整其中一个

答案 4 :(得分:1)

值的稀疏映射及其出现频率。

SortedMap<Integer, Integer> a = new TreeMap<Integer, Integer>();
for (int i = 0; i < n; ++i) {
    int value = input[i];
    Integer old = a.put(value, 1);
    if (old != null) {
        a.put(value, old.intValue() + 1);
    }
}
for (Map.Entry<Integer, Integer> entry : a.entrySet()) {
    Integer freq = a.get(entry.getKey() + k);
    count += entry.getValue() * freq; // N values x M values further on.
}

这个O(n)。

如果成本过高,您可以对输入数组进行排序并执行类似操作。

答案 5 :(得分:0)

我不明白你为什么在另一个圈内有一个循环。这样O(n^2)

你也可以将这个数组中的读数与得到这个计数混合起来。我将两者分开 - 读完整个内容然后扫过并获得差异计数。

也许我误解了你正在做的事情,但感觉你在内循环中重新做了很多工作。

答案 6 :(得分:0)

为什么不使用java.util.Scanner类而不是BufferReader。

例如: -

Scanner sc = new Scanner(System.in); int number = sc.nextInt();

这可能会更快,因为他们的包装更少...... See this link

答案 7 :(得分:0)

使用集合和地图,正如其他用户已经解释的那样,所以我不会再重申他们的建议。

我会建议别的。 停止使用String.split。它编译并使用正则表达式。 String.split中有一行:Pattern.compile(expr).split(this)。 如果你想沿着单个字符分割,你可以编写自己的函数,它会快得多。我相信Guava(ex-Google collections API)具有String split功能,可以在不使用正则表达式的情况下拆分字符。