为什么排列函数的时间复杂度为O(n!)

时间:2016-08-24 14:01:12

标签: 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!的顺序增长时,我发现了不同的结果 对于计数++的2长度字符串(在for循环中)运行4次,对于3长度的字符串,计数值为15,对于4和5长度的字符串,其为64和325.
这意味着它比n!那么为什么它说这(以及产生permuatations的类似算法)就运行时而言是O(n!)。

2 个答案:

答案 0 :(得分:14)

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

  • str.length() == n时,您执行了n次呼叫;
  • 对于nstr.length() == n - 1n - 1次来电,您需要n * (n - 1)次来电;
  • 对于str.length() == n - 2n - 2n!/k!次来电,您需要str.length() == k次来电;
  • ...

您使用str.length() 1 进行0次呼叫,并且由于ne转到1,所以总数电话是:

  

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

但是你可能知道:

  

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

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

运行时复杂性通常与理论复杂性不同 - 在理论上的复杂性,人们想要知道排列的数量,因为算法可能会检查每个排列(因此有效str.length() == n检查完成),但实际上还有很多事情要发生。

1 这个公式实际上会给你一个与你得到的值相比较,因为你没有计算List(Of T)时的第一个函数调用。

答案 1 :(得分:1)

这个答案适用于像我这样不记得e = 1/0!+1/1!+1/2!+1/3!

我可以用一个简单的例子来解释,说我们想要"abc"的所有排列

        /    /   \     <--- for first position, there are 3 choices
       /\   /\   /\    <--- for second position, there are 2 choices
      /  \ /  \ /  \   <--- for third position, there is only 1 choice

上面是递归树,我们知道有3! 叶节点,它代表"abc"的所有可能排列(这也是我们的位置)对结果执行操作,即print() ),但由于您正在计算所有函数调用,我们需要知道总共有多少个树节点(叶+内部)

如果它是一个完整的二叉树,我们知道有2^n叶节点......有多少个内部节点?

x = |__________leaf_____________|------------------------|  
let this represent 2^n leaf nodes, |----| represents the max number of
nodes in the level above, since each node has 1 parent, 2nd last level
cannot have more nodes than leaf
since its binary, we know second last level = (1/2)leaf 
x = |__________leaf_____________|____2nd_____|-----------|
same for the third last level...which is (1/2)sec
x = |__________leaf_____________|____2nd_____|__3rd_|----|

x可以用来表示树节点的总数,因为我们总是在初始|-----|上减半,我们知道总计&lt; = 2 * leaf

现在用于置换树

x = |____leaf____|------------|
let this represent n! leaf nodes
since its second last level has 1 branch, we know second last level = x 
x = |____leaf____|____2nd_____|-------------|
but third last level has 2 branches for each node, thus = (1/2)second
x = |____leaf____|____2nd_____|_3rd_|-------|
fourth last level has 3 branches for each node, thus = (1/3)third
x = |____leaf____|____2nd_____|_3rd_|_4|--| |
| | means we will no longer consider it

这里我们看到总计&lt; 3 *叶,这是预期的(e = 2.718)