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

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

我认为这根本没有效率。我正在尝试为此创建一个更快的实现(最好是二进制搜索或使用集合),但现在这就是我所在的位置。 我不确定它是否相关,但我创建了一个计数变量只是为了查看该方法被调用了多少次。来到了577次。

这是 "Subset Sum" 算法,我必须在其中显示添加到目标总和的所有子集,在本例中为 3165。 没有特别说明这个问题是算法,但我意识到这确实是同一个概念。

我的问题是我怎么知道这个程序的效率如何,方法调用指标吗?

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]);
            }
        }
    }
}

这是输出:

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

如果您想通过实验找到趋势,方法的调用次数是一个不错的指标。您需要使用不同的输入多次 运行 函数,收集数据,绘制图表并找到最佳拟合曲线...这将假设对 findCollections 的调用与其时间直接相关需要执行。 然而,在这里找到最坏的情况并不难。您只需要考虑哪些输入变量会导致您的执行时间最长。

在这种情况下,如果 sum 变量大于整个集合的总和,您将看到最差的执行(最多递归)。如果这个条件成立,那么下面的方法就等同于你的方法:

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]);
    }
}

这里很容易看出,这将循环遍历 array 的所有可能子集。这使得您的解决方案 O(2^n) 其中 n 是 array

的大小

My question is how can I know how efficient this program is, and are the method calls an indicator?

推导算法渐近 运行 时间的唯一方法是亲自动手 手动 。话虽如此,当您的算法 运行s 不是 可靠的 合理的 方法时,尝试跟踪方法调用导出算法的运行时间。

要开始尝试弄清楚您的算法 运行s 有多快或多慢,我们可以从分析每行的 运行 时间推导循环开始:

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   -

我们分析了每一行,我们可以推导出递归:

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

因为我们不能使用主定理,并且尝试使用递归树会很快变得非常混乱和混淆这个特定的递归,我们可以使用代入法来解决这个递归问题。我们将初始猜测设置为 2^n,因为在我看来它可能是指数级的:


上限O(2^n)


这意味着

T(n) ≤ c * 2^n

如果这个命题成立,这意味着我们也知道

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

因此,我们现在可以写出我们的递归并尝试证明我们的猜测:

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

这适用于所有 n > 0 | c = 1,因此 T(n) = O(2^n)


下限Ω(2^n)


这意味着

T(n) ≥ c * 2^n

如果这个命题成立,这意味着我们也知道

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

因此,我们现在可以写出我们的递归并尝试证明我们的猜测:

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

这适用于所有 n > 0 | c = 5000,因此 T(n) = Ω(2^n)


结论Θ(2^n)


由于我们已经证明该算法既是 O(2^n) 又是 Ω(2^n),因此根据定义它也是 Θ(2^n) .所以基本上,你的算法的时间复杂度是 Θ(2^n) 这是 非常慢 因为它是指数级的。