algorithm - 为什么置换函数的时间复杂度是 O(n!)

标签 algorithm time-complexity permutation

考虑以下代码。

public class Permutations {
    static int count=0;
    static void permutations(String str, String prefix){
        if(str.length()==0){
            System.out.println(prefix);
        }
        else{
            for(int i=0;i<str.length();i++){
                count++;
                String rem = str.substring(0,i) + str.substring(i+1);
                permutations(rem, prefix+str.charAt(i));
            }
        }

    }
    public static void main(String[] args) {
        permutations("abc", "");
        System.out.println(count);
    }

}

这里的逻辑,我认为遵循的是——它将字符串的每个字符视为一个可能的前缀,并排列剩余的 n-1 个字符。
所以按照这个逻辑递归关系就是

T(n) = n( c1 + T(n-1) )          // ignoring the print time

这显然是 O(n!)。但是当我使用一个计数变量来查看小麦算法真的按照 n! 的顺序增长时,我发现了不同的结果。
对于 count++ 的 2 长度字符串(在 for 循环内)运行 4 次,对于 3 长度的字符串,count 的值是 15,对于 4 和 5 长度的字符串,其值为 64 和 325。
这意味着它变得比 n! 更糟。那么为什么它说这个(以及生成排列的类似算法)在运行时间方面是 O(n!)。

最佳答案

人们说这个算法是 O(n!) 因为有 n! 排列,但你在这里计算的是(在某种意义上)函数调用 - 并且有比 n! 更多的函数调用:

  • str.length() == n 时,您执行 n 次调用;
  • 对于每个使用 str.length() == n - 1n 调用,你执行 n - 1 调用;
  • 对于每个 n * (n - 1) 调用 str.length() == n - 2 你做 n - 2调用;
  • ...

你做 n!/k! 使用长度为 k1 的输入 str 调用,并且由于长度从 n0,总调用次数为:

sum k = 0 ... n (n! / k!) = n! sum k = 0 ... n (1 / k!)

但是你可能知道:

sum k = 0 ... +oo 1 / k! = e1 = e

所以基本上,这个总和总是小于常量 e(并且大于 1),所以你可以说调用次数是 O (e.n!)O(n!)

运行时复杂度通常不同于理论复杂度。在理论上的复杂性中,人们想知道排列的数量,因为算法可能会检查这些排列中的每一个(因此实际上有 n! 检查完成),但实际上还有更多的东西继续。

1 由于您没有考虑初始函数调用,因此与您获得的值相比,这个公式实际上会给您一个。

关于algorithm - 为什么置换函数的时间复杂度是 O(n!),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39125471/

相关文章:

algorithm - 计算哈希表中键比较的次数

algorithm - 计算大 O 时间复杂度

算法 - 将嵌套数据转换为普通数据

java - 遍历由边表示的树

php - PHP 中的更改组合/排列算法

matlab - 如何获得矩阵中所有可能的元素组合,但不允许在列之间交换元素?

python - 过滤A010784序列的最快方法

algorithm - 如何找出合并排序实现的时间复杂度?

algorithm - 寻找一种有效的算法来获得一条线

Javascript - 排列和可能性