简体   繁体   English

Java:测试数组求和算法的效率

[英]Java : Testing Array Sum Algorithm Efficiency

I am taking a Java course in university and my notes give me 3 methods for calculating the sum of an ArrayList. 我正在大学上一门Java课程,我的笔记给了我3种计算ArrayList之和的方法。 First using iteration, second using recursion, and third using array split combine with recursion. 首先使用迭代,其次使用递归,第三次使用数组拆分与递归结合。

My question is how do I test the efficiency of these algorithms? 我的问题是如何测试这些算法的效率? As it is, I think the number of steps it takes for the algorithm to compute the value is what tells you the efficiency of the algorithm. 实际上,我认为算法计算值所需的步骤数告诉了您算法的效率。

My Code for the 3 algorithms: 我的3种算法的代码:

import java.util.ArrayList;
public class ArraySumTester {

    static int steps = 1;

    public static void main(String[] args) {

        ArrayList<Integer> numList = new ArrayList<Integer>();

        numList.add(1);
        numList.add(2);
        numList.add(3);
        numList.add(4);
        numList.add(5);


        System.out.println("------------------------------------------");
        System.out.println("Recursive array sum = " + ArraySum(numList));

        System.out.println("------------------------------------------");
        steps = 1;
        System.out.println("Iterative array sum = " + iterativeSum(numList));

        System.out.println("------------------------------------------");
        steps = 1;
        System.out.println("Array sum using recursive array split : " + sumArraySplit(numList));

    }

    static int ArraySum(ArrayList<Integer> list) {
        return sumHelper(list, 0);
    }

    static int sumHelper(ArrayList<Integer> list, int start) {
        // System.out.println("Start : " + start);
        System.out.println("Rescursive step : " + steps++);
        if (start >= list.size())
            return 0;
        else
            return list.get(start) + sumHelper(list, start + 1);

    }

    static int iterativeSum(ArrayList<Integer> list) {
        int sum = 0;
        for (Integer item : list) {
            System.out.println("Iterative step : " + steps++);
            sum += item;
        }
        return sum;
    }

    static int sumArraySplit(ArrayList<Integer> list) {

        int start = 0;
        int end = list.size();
        int mid = (start + end) / 2;

        System.out.println("Rescursive step : " + steps++);
        //System.out.println("Start : " + start + ", End : " + end + ", Mid : " + mid);
        //System.out.println(list);

        if (list.size() <= 1)
            return list.get(0);
        else
            return sumArraySplit(new ArrayList<Integer>(list.subList(0, mid)))
                    + sumArraySplit(new ArrayList<Integer>(list.subList(mid,
                            end)));

    }
}

Output: 输出:

------------------------------------------
Rescursive step : 1
Rescursive step : 2
Rescursive step : 3
Rescursive step : 4
Rescursive step : 5
Rescursive step : 6
Recursive array sum = 15
------------------------------------------
Iterative step : 1
Iterative step : 2
Iterative step : 3
Iterative step : 4
Iterative step : 5
Iterative array sum = 15
------------------------------------------
Rescursive step : 1
Rescursive step : 2
Rescursive step : 3
Rescursive step : 4
Rescursive step : 5
Rescursive step : 6
Rescursive step : 7
Rescursive step : 8
Rescursive step : 9
Array sum using recursive array split : 15

Now from the above output the recursive array split algorithm takes the most steps, however according to my notes, it is as efficient as the iterative algorithm. 现在,从上面的输出中,递归数组拆分算法采取了最多的步骤,但是根据我的笔记,它与迭代算法一样有效。 So which is incorrect my code or my notes? 那么我的代码或注释中哪个不正确?

Using System.currentTimeMillis() is the way to go. 使用System.currentTimeMillis()是方法。 Define a start variable before your code and an end variable after it completes. 在代码之前定义一个开始变量,在代码完成之后定义一个结束变量。 The difference of these will be the time elapsed for your program to execute. 这些差异将是程序执行所花费的时间。 The shortest time will be the most efficient. 最短的时间将是最有效的。

long start = System.currentTimeMillis(); 长期开始= System.currentTimeMillis();

// Program to test //程序进行测试

long end = System.currentTimeMillis(); 长端= System.currentTimeMillis(); long diff = end - start; long diff =结束-开始;

Do you just want to look at speed of execution? 您是否只想看看执行速度? If so, you'll want to look at microbenchmarking: How do I write a correct micro-benchmark in Java? 如果是这样,您将需要研究微基准测试如何用Java编写正确的微基准测试?

Essentially because of how the JVM and modern processors work, you won't get consistent results by running something a million times in a FOR loop and measuring the execution speed with a system timer (EDIT). 本质上是因为JVM和现代处理器的工作方式,通过在FOR循环中运行一百万次并使用系统计时器(EDIT)测量执行速度,您将无法获得一致的结果。

That said, "efficiency" can also mean other things like memory consumption. 话虽如此,“效率”也可能意味着其他事情,例如内存消耗。 For instance, any recursive method runs a risk of a stack overflow, the issue this site is named after :) Try giving that ArrayList tens of thousands of elements and see what happens. 例如,任何递归方法都存在堆栈溢出的风险,此站点以以下名称命名:)尝试给ArrayList上万个元素,看看会发生什么。

I suggest that you look at the running time and space complexity (these are more computer sciencey names for efficiency) of these algorithms in the abstract. 我建议您抽象地看一下这些算法的运行时间和空间复杂度(这些是效率的更多计算机科学名称)。 This is what the so-called Big-Oh notation is for. 这就是所谓的Big-Oh表示法

To be exact, of course, after making the implementations as tight and side-effect-free as possible, you should consider writing microbenchmarks. 确切地说,当然,在使实现尽可能紧密和无副作用之后,您应该考虑编写微基准测试。

Since you have to be able to read the value of every element of the list in order to sum these elements up, no algorithm is going to perform better than a (linear) O(n) time, O(1) space algorithm (which is what your iterative algorithm does) in the general case (ie without any other assumptions). 由于您必须能够读取列表中每个元素的值以将这些元素加起来,因此没有一种算法会比(线性) O(n)时间, O(1)空间算法(是在一般情况下 (即没有任何其他假设)的迭代算法所执行的操作。 Here n is the size of the input (ie the number of elements in the list). 这里n是输入的大小(即列表中元素的数量)。 Such an algorithm is said to have a linear time and constant space complexity meaning its running time increases as the size of the list increases, but it does not need any additional memory; 这样的算法被认为具有一个线性时间恒定的空间复杂性意味着其运行时间随着列表中的尺寸增加,但它不需要任何附加的存储器; in fact it needs some constant memory to do its job. 实际上,它需要一些恒定的内存来完成其工作。

The other two recursive algorithms, can, at best, perform as well as this simple algorithm because the iterative algorithm does not have any complications (additional memory on the stack, for instance) that recursive algorithms suffer with. 由于迭代算法没有递归算法所遇到的任何复杂性(例如,堆栈上的额外内存),因此其他两种递归算法最多只能具有与这种简单算法一样好的性能。

This gets reflected into what are called the constant terms of the algorithms that have the same O(f(n)) running time. 这反映为具有相同O(f(n))运行时间的算法的常数项。 For instance, if you somehow found an algorithm which examines roughly half the elements of a list to solve a problem, whereas another algorithm must see all the elements, then, the first algorithm has better constant terms than the second and is expected to beat it in practice, although both these algorithms have a time complexity of O(n) . 例如,如果您以某种方式找到了一种算法,该算法检查列表中大约一半的元素以解决问题,而另一种算法必须查看所有元素,则第一种算法的常数项要比第二种更好,并且有望击败它实际上,尽管这两种算法的时间复杂度均为O(n)

Now, it is quite possible to parallelize the solution to this problem by splitting the giant list into smaller lists (you can achieve the effect via indexes into a single list) and then use a parallel summing operation which may beat other algorithms if the list is sufficiently long. 现在,很有可能通过将巨型列表拆分为较小的列表来并行化该问题的解决方案(您可以通过将索引分成单个列表来实现效果),然后使用并行求和操作,如果列表是足够长。 This is because each non-overlapping interval can be summed up in parallel (at the same time) and you'd sum the partial sums up in the end. 这是因为每个非重叠间隔都可以并行(同时)求和,最后将部分和求和。 But this is not a possibility we are considering in the current context. 但这不是我们目前考虑的可能性。

I would say to use the Guava Google Core Libraries For Java Stopwatch. 我会说使用JavaGuava Google核心库 Example: 例:

Stopwatch stopwatch = Stopwatch.createStarted();
// TODO: Your tests here
long elapsedTime = stopwatch.stop().elapsed(TimeUnit.MILLISECONDS);

You get the elapsed in whatever unit you need and plus you don't need any extra calculations. 您可以使用所需的任何单位作为耗材,而且不需要任何额外的计算。

If you want to consider efficiency then you really need to look at algorithm structure rather than timing. 如果要考虑效率,那么您确实需要查看算法结构而不是时序。

Load the sources for the methods you are using, dive into the structure and look for looping - that will give you the correct measure of efficiency. 加载正在使用的方法的源代码,深入结构并寻找循环-这将为您提供正确的效率度量。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM