Почему временная сложность функции перестановки равна 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!). но когда я использовал переменную count, чтобы увидеть, что wheather algo действительно растет в порядке n! я нашел разные результаты.
для строки длиной 2 для count++(внутри для цикла) выполняется 4 раза, для 3-длины строки значение count приходит 15, а для 4 и 5-длины строки его 64 и 325.
Это значит, что он растет хуже n!. тогда почему он сказал,что это(и подобные algos, которые генерируют перестановки) являются O (n!) с точки зрения времени выполнения.

2 ответов


люди говорят, что этот алгоритм O(n!) что есть n! перестановки, но то, что вы подсчитываете здесь, - это (в некотором смысле) вызовы функций-и есть больше вызовов функций, чем n!:

  • , когда str.length() == n, ты n звонков;
  • для каждого n звонки str.length() == n - 1, ты n - 1 звонков;
  • для каждого n * (n - 1) звонки str.length() == n - 2 ты n - 2 зовет;
  • ...

ты n!/k! звонки str.length() == k1, и с str.length() сочетается с 0 to n, общее количество звонков:

sum k = 0 ... n (n!Кей!) = n! sum k = 0 ... n (1 / k!)

но как вы знаете:

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

так в принципе, эта сумма всегда меньше константы e (и больше 1), поэтому вы можете сказать, что количество вызовов O(e.n!) что это O(n!).

сложность выполнения часто отличается от теоретической сложности - в теоретической сложности люди хотят знать количество перестановок, потому что алгоритм, вероятно, будет проверять каждую из этих перестановок (так что эффективно n! check done), но на самом деле происходит гораздо больше на.

1 эта формула фактически даст вам один по сравнению со значениями, которые вы получили, потому что вы не посчитали первый вызов функции, когда str.length() == n.


этот ответ для таких людей, как я, кто не помнит 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 можно использовать для представления общего количества узлов дерева, и так как мы всегда режем половину на начальном |-----| известно, что всего

теперь для дерева перестановок

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

здесь мы видим, что всего , это, как и ожидалось (e = 2.718)