繁体   English   中英

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

[英]Why Time complexity of permutation function is O(n!)

考虑以下代码。

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!)。 但是当我使用一个计数变量来查看 wheather 算法真的按照 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 - 1的这n调用中的每一个,您执行n - 1调用;
  • 对于这些n * (n - 1)使用str.length() == n - 2调用中的每一个,您都执行n - 2调用;
  • ...

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

总和k = 0 ... n (n!/k!) = n! 总和k = 0 ... n (1 / k!)

但你可能知道:

总和k = 0 ... +oo 1 / k! = e 1 = e

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

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

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 可以用来表示树节点的总数,因为我们总是在初始|-----|上减半我们知道总数 <= 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

在这里我们看到total < 3*leaf ,这是预期的(e = 2.718)

暂无
暂无

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

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