简体   繁体   English

如何计算此递归函数的最坏情况时间复杂度?

[英]How would I calculate the worst case time complexity of this recursive function?

I don't believe this is efficient at all. 我不相信这是有效的。 I'm trying to create a faster implementation of this (preferably binary search or use of Sets perhaps), but for now this is where I'm at. 我正在尝试创建一个更快的实现(最好是二进制搜索或者使用Sets),但是现在这就是我所处的位置。 I'm not sure if it's relevant, but I created a count variable just to see how many times the method was being called. 我不确定它是否相关,但我创建了一个计数变量,只是为了查看调用该方法的次数。 It came to 577 times. 它达到了577次。

This is the "Subset Sum" algorithm, where I have to display all the subsets that add to a target sum, in this case 3165. It wasn't specifically stated that this problem was the algorithm, but I realized it was indeed the same concept. 这是“子集和”算法,其中我必须显示添加到目标总和的所有子集,在本例中为3165.没有具体说明这个问题是算法,但我意识到它确实是相同的概念。

My question is how can I know how efficient this program is, and are the method calls an indicator? 我的问题是我如何知道这个程序的效率如何,方法调用指标是什么?

public class SubsetSumAlgorithm {

    static int count = 0;

    public static void main(String[] args) {

        int[] array = {26, 39, 104, 195, 403, 504, 793, 995, 1156, 1673};

        System.out.println("COLLECTIONS IN ARRAY THAT ADD TO 3165: ");
        findCollections(array, 0, 0, 3165, "");
        System.out.println("Method called " + count + " times.");//method calls
    }

    public static void findCollections(int[] array, int index, int currentPosition, int sum, String collection) {

        count++; //<---COUNTING THE METHOD CALLS HERE

        if (array.length < index || currentPosition > sum) {
            return;
        }
        //if sum is found, add to subset
        for (int i = index; i < array.length; i++) {
            if (currentPosition + array[i] == sum) {
                System.out.println(collection + " " + array[i]);
            }
            //otherwise, call the method again
            else if (currentPosition + array[i] < sum) {//recursive call
                findCollections(array, i + 1, currentPosition + array[i], sum, collection + " " + array[i]);
            }
        }
    }
}

And here is the output: 这是输出:

COLLECTIONS IN ARRAY THAT ADD TO 3165: 
26 195 793 995 1156
195 504 793 1673
Method called 577 times.

My question is how can I know how efficient this program is, and are the method calls an indicator? 我的问题是我如何知道这个程序的效率如何,方法调用指标是什么?

The only way to derive the asymptotic run time of an algorithm is to actually get your hands dirty and do it manually . 导出算法的渐近运行时间的唯一方法是实际上弄脏你的手并手动完成 With this being said, trying to keep track of method calls while your algorithm runs isn't a reliable or reasonable way to derive the runtime of an algorithm. 有了这样说,尝试在算法运行时跟踪方法调用并不是一种可靠合理的方法来推导算法的运行时。

To start trying to figure out how fast or slow your algorithm runs, we can start by deriving the recurrence from analyzing the run time of each line: 要开始尝试计算算法的运行速度或速度,我们可以从分析每行的运行时间得出重现:

1    public static void findCollections(int[] array, int index, int currentPosition, int sum, String collection) {
2        if(array.length < index || currentPosition > sum)
3            return;
4        for(int i = index; i < array.length; i++) {
5            if(currentPosition + array[i] == sum) {
6                System.out.println(collection + " " + array[i]);
7            }
8            else if(currentPosition + array[i] < sum) {
9                findCollections(array, i + 1, currentPosition + array[i], sum, collection + " " + array[i]);
10           }
11       }
12   }

...

1    -
2    1
3    1
4    n+1
5    n
6    n
7    -
8    n
9    n*T(n-1)
10   -
11   -
12   -

One we've analyzed each line, we can derive the recurrence: 我们分析了每一行,我们可以推导出复发:

    T(n) = n*T(n-1) + 4n + 3
 => T(n) = n*T(n-1) + 4n + Θ(1)
 => T(n) = n*T(n-1) + 4n

Since we cannot use the Master Theorem , and attempting a recursion tree would get very messy and confusing very fast with this particular recurrence, we can solve this recurrence using the substitution method . 由于我们不能使用主定理 ,并且尝试递归树会非常混乱并且非常快速地混淆这种特定的重复,我们可以使用替换方法来解决这种重复。 We will set our initial guess to be 2^n since it looks to me like it'll probably be exponential: 我们将初始猜测设置为2^n因为它看起来像它可能是指数的:


Upper Bound O(2^n) 上界O(2^n)


This would imply that 这意味着

T(n) ≤ c * 2^n

if this proposition holds true, this means we also know that 如果这个命题成立,这意味着我们也知道这一点

T(n-1) ≤ c * 2^(n-1)

Therefore, we can now write our recurrence and attempt to prove our guess: 因此,我们现在可以写下我们的重复并尝试证明我们的猜测:

    c * 2^n ≥ n * (c * 2^(n-1)) + 4n
 => c * 2^n ≥ c * n * 2^(n-1) + 4n

this holds true for all n > 0 | c = 1 这适用于所有n > 0 | c = 1 n > 0 | c = 1 , therefore T(n) = O(2^n) n > 0 | c = 1 ,因此T(n) = O(2^n)


Lower Bound Ω(2^n) 下界Ω(2^n)


This would imply that 这意味着

T(n) ≥ c * 2^n

if this proposition holds true, this means we also know that 如果这个命题成立,这意味着我们也知道这一点

T(n-1) ≥ c * 2^(n-1)

Therefore, we can now write our recurrence and attempt to prove our guess: 因此,我们现在可以写下我们的重复并尝试证明我们的猜测:

    c * 2^n ≤ n * (c * 2^(n-1)) + 4n
 => c * 2^n ≤ c * n * 2^(n-1) + 4n

this holds true for all n > 0 | c = 5000 这适用于所有n > 0 | c = 5000 n > 0 | c = 5000 , therefore T(n) = Ω(2^n) n > 0 | c = 5000 ,因此T(n) = Ω(2^n)


Conclusion Θ(2^n) 结论Θ(2^n)


Since we've prove that the algorithm is both O(2^n) and Ω(2^n) , therefore it is also, by definition, Θ(2^n) . 由于我们已经证明该算法是O(2^n) Ω(2^n) ,因此根据定义它也是Θ(2^n) So basically, your algorithm's time complexity is Θ(2^n) which is very slow as it is exponential. 所以基本上,你的算法的时间复杂度是Θ(2^n) ,这是非常慢的,因为它是指数的。

The number of calls to a Method are a decent indicator if you want to find the trend experimentally. 如果您想通过实验找到趋势,则调用Method的次数是一个不错的指标。 You would need to run the function many times with varying inputs, collect the data, graph it, and find a best fit curve... This would assume that calls to findCollections directly correlate with the time it takes to execute. 您需要使用不同的输入多次运行该函数,收集数据,绘制图形并找到最佳拟合曲线......这将假设对findCollections调用与执行所需的时间直接相关。 However, finding the worst case here isn't too hard. 然而,在这里找到最坏的情况并不太难。 You just have to think about what input variables would cause your execution to go on the longest. 您只需要考虑哪些输入变量会导致执行时间最长。

In this case if the sum variable is larger than the sum of the whole set you will see the worst execution (the most recursion). 在这种情况下,如果sum变量大于整个集合的总和,您将看到最差的执行(最多的递归)。 If this condition is true, then the method bellow would be equivalent to yours: 如果这个条件为真,那么下面的方法将等同于你的方法:

public static void findCollections(int[] array, int index, int currentPosition, int sum, String collection) {
    for(int i = index; i < array.length; i++) {
        findCollections(array, i + 1, currentPosition + array[i], sum, collection + " " + array[i]);
    }
}

Its easy to see here that this would cycle through all possible sub-sets of array . 这里很容易看出,这将循环遍历所有可能的array子集。 This makes your solution O(2^n) where n is the size of array 这使得您的解决方案为O(2^n) ,其中n是array的大小

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

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