问题
Consider following code.
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);
}
}
here the logic, that i think is followed is- it considers each character of the string as a possible prefix and permutes the remaining n-1 characters.
so by this logic recurrence relation comes out to be
T(n) = n( c1 + T(n-1) ) // ignoring the print time
which is obviously O(n!). but when i used a count variable to see wheather algo really grows in order of n!, i found different results.
for 2-length string for count++(inside for loop) runs 4 times, for 3-length string value of count comes 15 and for 4 and 5-length string its 64 and 325.
It means it grows worse than n!. then why its said that this(and similar algos which generate permuatations) are O(n!) in terms of run time.
回答1:
People say this algorithm is O(n!)
because there are n!
permutations, but what you are counting here are (in a sense) function calls - And there are more function calls than n!
:
- When
str.length() == n
, you don
calls; - For each of these
n
calls withstr.length() == n - 1
, you don - 1
calls; - For each of these
n * (n - 1)
calls withstr.length() == n - 2
you don - 2
calls; - ...
You do n!/k!
calls with an input str
of length k
1, and since the length goes from n
to 0
, the total number of calls is:
sum k = 0 ... n (n!/k!) = n! sum k = 0 ... n (1/k!)
But as you may know:
sum k = 0 ... +oo 1/k! = e1 = e
So basically, this sum is always less than the constant e
(and greater than 1
), so you can say that the number of calls is O(e.n!)
which is O(n!)
.
Runtime complexity is often different from theoretical complexity. In theoretical complexity, people want to know the number of permutations because the algorithm is probably going to check each of these permutations (so there are effectively n!
check done), but in reality there is much more thing going on.
1 This formula will actually give you one compared to the values you got since you did account for the initial function call.
回答2:
this answer is for people like me who doesn't remember e=1/0!+1/1!+1/2!+1/3!...
I can explain using a simple example, say we want all the permutation of "abc"
/ / \ <--- for first position, there are 3 choices
/\ /\ /\ <--- for second position, there are 2 choices
/ \ / \ / \ <--- for third position, there is only 1 choice
above is the recursion tree, and we know that there are 3!
leaf nodes, which represents all possible permutations of "abc"
(which is also where we perform an action on the result, ie print()
), but since you are counting all function calls, we need to know how many tree nodes in total (leaf + internal)
if it was a complete binary tree, we know there are 2^n
leaf nodes...how many internal nodes?
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 can be used to represent total number of tree nodes, and since we are always cutting half on the initial |-----|
we know that total <= 2*leaf
now for permutation tree
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
here we see that total < 3*leaf, this is as expected (e = 2.718)
来源:https://stackoverflow.com/questions/39125471/why-time-complexity-of-permutation-function-is-on