Consider the following Java method:
public static void f(int n) {
if (n<=1) {
System.out.print(n) ;
return;
}else {
f(n/2) ;
System.out.print(n);
f(n/2);
}
} // end of method
Question 3. Let S(n) denote the space complexity of f(n). Which of the
following statements is correct?
A: S(n) = (2n)
B: S(n) = (n)
C: S(n) = (log n) <- Correct answer, anyone know why?
D: none of the above
Whenever the function calls itself recursively all local variables remain on the stack and a new set of them are pushed to the stack for the new call.
This means that you care how many calls are there at most, in other words what is the maximum depth of the recursion.
It's clear that it's log n, because the successive argumetns are n, n/2, n/4, ..., 1.
There is a constant number of local variables, namely 1 (for which space is needed on the stack) therefore the overall space complexity is O(log n).
Related
I'm currently taking a class in algorithms. The following is a question I got wrong from a quiz: Basically, we have to indicate the worst case running time in Big O notation:
int foo(int n)
{
m = 0;
while (n >=2)
{
n = n/4;
m = m + 1;
}
return m;
}
I don't understand how the worst case time for this just isn't O(n). Would appreciate an explanation. Thanks.
foo calculates log4(n) by dividing n by 4 and counting number of 4's in n using m as a counter. At the end, m is going to be the number of 4's in n. So it is linear in the final value of m, which is equal to log base 4 of n. The algorithm is then O(logn), which is also O(n).
Let's suppose that the worst case is O(n). That implies that the function takes at least n steps.
Now let's see the loop, n is being divided by 4 (or 2²) at each step. So, in the first iteration n is reduced to n/4, in the second, to n/8. It isn't being reduced linearly. It's being reduced by a power of two so, in the worst case, it's running time is O(log n).
The computation can be expressed as a recurrence formula:
f(r) = 4*f(r+1)
The solution is
f(r) = k * 4 ^(1-r)
Where ^ means exponent. In our case we can say f(0) = n
So f(r) = n * 4^(-r)
Solving for r on the end condition we have: 2 = n * 4^(-r)
Using log in both sides, log(2) = log(n) - r* log(4) we can see
r = P * log(n);
Not having more branches or inner loops, and assuming division and addition are O(1) we can confidently say the algorithm, runs P * log(n) steps therefore is a O((log(n)).
http://www.wolframalpha.com/input/?i=f%28r%2B1%29+%3D+f%28r%29%2F4%2C+f%280%29+%3D+n
Nitpickers corner: A C int usually means the largest value is 2^32 - 1 so in practice it means only max 15 iterations, which is of course O(1). But I think your teacher really means O(log(n)).
I have a great Doubt in solving this recursive relation. Can anyone provide me a solution?
The relation:
T(n) = Sumation i=1 to N T(i) +1...,
What is the bigOh order?
Taking the first order difference allows you to get rid of the summation.
T(n) - T(n-1) = (Sum(1<=i<n: T(i)) + 1) - (Sum(1<=i<n-1: T(i)) + 1) = T(n-1)
Hence
T(n) = 2.T(n-1)
A recurrence relation describes a sequence of numbers. Early terms are specified explicitly and later terms are expressed as a function of their predecessors. As a trivial example, this recurrence describes the sequence 1, 2, 3, etc.:
void Sample(int n)
{
if (n > 0) {
Sample(n-1);
System.out.println('here');
} else {
return 1;
}
}
Sample(3);
Here, the first term is defined to be 1 and each subsequent term is one more than its predecessor. In order to analyze a recurrence relationship, we need to know execution time of each line of code, In above example:
void Sample(int n)
{
if (n > 0) {
// T(n-1) Sample(n-1);
// 1 System.out.println('here');
} else {
return 1;
}
}
We define T(n) as a:
For solving T(n)= T(n-1)+1, If we know what is T(n-1), then we can substitute it and get answer, Let's substitute it with n then, we will have:
T(n-1)= T(n-1-1)+1 => T(n-2)+1
//in continue
T(n)=[T(n-2)+1]+1 => T(n-2)+2
//and again
T(n)=[T(n-3)+2]+1 => T(n-3)+3
.
.
.
// if repeat it k times, while it ends
T(n)= T(n-k)+k
As you see it is increasing by 1 in each step, If we go k times, Then T(n)= T(n-k)+k. Now, we need to know the smallest value(when the the function will stop, always must be a point to stop). In this problem zero is end of recursive stack. sine we assume that we go k times to reach zero, the solution will be:
// if n-k is final step
n-k = 0 => n = k
// replace k with n
T(n)= T(n-n)+n => T(n)= T(0)+n;
// we know
T(0) = 1;
T(n) = 1+n => O(n)
The big O is n, It means this recursive algorithms in worst case goes n times.
Say we have a method that takes n steps, but that calls itself linearly at the worst case n times. In such a case would the Big O would be n*n? So is a recursive call generally n^2 similarly to two for loops?
Let's now take a binary recursion algorithm such as binary Fibonacci. 1 call of that algorithm takes n step, but let's say it can reiterate up to n times. Would the run-time of that algorithm be 2^n?
Let f() be a function which calls itself n times. Consider the C code representing the function f().
int f(int n)
{
int i;
if(n==0)
{
printf("\n Recursion Stopped");
}
else
{
for(i=0;i<=n;i++)
{
printf("\n Hello");
}
f(n-1);
}
}
For n = 5, the message Hello will be printed 15 times.
For n = 10, the message Hello will be printed 55 times.
In general the message will be printed n*(n+1)/2 times.
Thus the complexity of the function f() is O(n2). Remember f() is a function which has a complexity n and f() is recursively called n times. The complexity of such a function is equal to the following loop order if the inner loop contains constant time expressions like addition, subtraction etc.
for(i=0;i<=n;i++)
{
for(j=i;j<=n;j++)
{
/* Some constant time operation */
}
}
For a Binary Recursion the time complexity is O(2n).
A Binary Recursive function calls itself twice.
The following function g() is an example for binary recursion, (which is a Fibonacci binary recursion)
int g(int n)
{
int i;
if(n==0)
{
printf("\n Recursion Stopped");
}
else
{
printf("\n Hello");
g(n-1);
g(n-2);
}
}
The recurrence relation is g(n) = g(n-1)+g(n-2), for n>1.
Solving which we get an upper bound of O(2n).
The function g() is also Θ(φn),
where φ is the golden ratio and φ = ((1+ √5)/2)
I'd like to know the Big Oh for the following algorithm
public List<String> getPermutations(String s){
if(s.length()==1){
List<String> base = new ArrayList<String>();
base.add(String.valueOf(s.charAt(0)));
return base;
}
List<String> combos = createPermsForCurrentChar(s.charAt(0),
getPermutations(s.substring(1));
return combos;
}
private List<String> createPermsForCurrentChar(char a,List<String> temp){
List<String> results = new ArrayList<String>();
for(String tempStr : temp){
for(int i=0;i<tempStr.length();i++){
String prefix = tempStr.substring(0, i);
String suffix = tempStr.substring(i);
results.add(prefix + a + suffix);
}
}
return results;
}
Heres what I think it is getPermutations is called n times , where n is length of the string.
My understanding is that
createPermutations is O(l * m) where l is the length of list temp and m is the length of each string in temp.
However since we are looking at worst case analysis, m<=n and l<= n!.
The length of the temp list keeps growing in each recursive call and so does the number of characters in each string in temp.
Does this mean that the time complexity of this algorithm is O(n * n! *n). Or is it O(n * n * n) ?
Well, I will just write this up as an answer instead of having a long list of comments.
Denote the run time of getPerm on string of length n as T(n). Observe that inside getPerm, it calls getPerm(string length n-1), so clearly
T(n)=T(n-1) + [run time of createPerm]
Note that createPerm has 2 loops that are nested. The outer loop iterates through the size of the result of getperm(string of length n-1) and the inner loop iterates through n-1 (length of individual strings). The result of getPerm(string of length n-1) is a list of T(n-1) strings. From this, we get that
[run time of createPerm] = (n-1) T(n-1)
Substituting this into the previous equation gives
T(n) = T(n-1) + (n-1) T(n-1) = n T(n-1)
T(1) = 1 from the exit condition. We can just expand to find the solution (or, alternatively, use Z-transform: Can not figure out complexity of this recurrence). Since it is a simple equation, expanding is faster:
T(n) = n T(n-1)
= n (n-1) T(n-2)
= n (n-1) (n-2) T(n-3) ....
= n (n-1) ... 1
= n!
So T(n) = n!
Exercise: prove this by induction! :p
Does this make sense? Let's think about it. We are creating permutations of n characters: http://en.wikipedia.org/wiki/Permutation.
EDIT: note that T(n)=n! is O(n!)
I'm not the best with combinatorics, but I think it's O(n^3) where n is the number of characters in your string.
My logic is this:
The number of times that
getPermutations(String)
is called is related to the call:
createPermsForCurrentChar(s.charAt(0),getPermutations(s.substring(1));
On the first call you pass arguements (charAt(0), substring of length s.length-1), then (charAt(1), substring of length s.length-2)... for O(n) calls.
What's more important is the # of elements in List temp each time we enter createPermsForCurrentChar.
First, let's analyze the function as a standalone thing:
Let's say there are k elements in List<String> temp, and they have monotonically increasing lengths, denoted by L=current length, beginning with L=1 and ending with L=k.
The outer-for loop will iterate k times, this is easy.
The inner for loop will iterate L times.
Our complexity is O(k"L"). L is in quotation marks because it changes each time, let's see what it looks like: First outer loop iteration, the inner loop executes once. Second outer loop ieration, the inner loop executes twice, and so on until the inner loop executes k times giving us 1+2+3+4+...k = O(k^2).
So createPermsForCurrentChar is O(k^2) complexity, where k is the number of elements in List<String> temp (and also the size of the longest string in temp). What we want to know now is this - How many elements will be in List<string> temp for each call?
When we finally reach the base case in our recursion, we're passing the second last character of our string, and the last character of our string to createPermsForCurrentChar, so k=1. It will create a single string of length O(k).
This allows the next execution to pop off the stack and call createPermsForCurrentChar again, this time with k=2. Then k=3, k=4, k=5, etc.
We know that createPermsForCurrentChar is being called O(n) times due to our recurrence relation, so k will eventually = n. (1 + 2 + 3 + ... + n) = O(n^2). Taking into account the complexity of createPermsForCurrentChar, we get (1^2 + 2^2 + 3^2 + ... n^2) = (1/3)n^3 + (1/2)n^2 + (1/6)n (from http://math2.org/math/expansion/power.htm).
Since we only care about our dominating value, we can say that the algorithm is O(n^3).
What is the time and space complexity of:
int superFactorial4(int n, int m)
{
if(n <= 1)
{
if(m <= 1)
return 1;
else
n = m -= 1;
}
return n*superFactorial4(n-1, m);
}
It runs recursively by decreasing the value of n by 1 until it equals 1 and then it will either decrease the value of m by 1 or return 1 in case m equals 1.
I think the complexity depends on both n and m so maybe it's O(n*m).
Actually, it looks to be closer to O(N+m^2) to me. n is only used for the first "cycle".
Also, in any language that doesn't do tail call optimization the space complexity is likely to be "fails". In languages that support the optimization, the space complexity is more like O(1).
The time complexity is O(n+m^2), space complexity the same.
Reasoning: with a fixed value of m, the function makes n recursive calls to itself, each does constant work, so the complexity of calls with fixed m is n. Now, when n reaches zero, it becomes m-1 and m becomes m-1 too. So the next fixed-m-phase will take m-1, the next m-2 and so on. So you get a sum (m-1)+(m-2)+...+1 which is O(m^2).
The space complexity is equal, because for each recursive call, the recursion takes constant space and you never return from the recursion except at the end, and there is no tail recursion.
The time complexity of a Factorial function using recursion
pseudo code:
int fact(n)
{
if(n==0)
{
return 1;
}
else if(n==1)
{
return 1;
}
else if
{
return n*f(n-1);
}
}
time complexity;
let T(n) be the number of steps taken to compute fact(n).
we know in each step F(n)= n*F(n-1)+C
F(n-1)= (n-1)*F(n-2)+c
substitute this in F(n), we get
F(n)= n*(n-1)*F(n-2)+(n+1)c
using big o notation now we can say that
F(n)>= n*F(n-1)
F(n)>= n*(n-1)*F(n-2)
.
.
.
.
.
F(n)>=n!F(n-k)
T(n)>=n!T(n-k)
n-k=1;
k=n-1;
T(n)>=n!T(n-(n-1))
T(n)>=n!T(1)
since T(1)=1
T(n)>=1*n!
now it is in the form of
F(n)>=c(g(n))
so we can say that time complexity of factorial using recursion is
T(n)= O(n!)