Can loops affect other loops' complexity without being inside it? - performance

Can a loop affect any other loop without being inside it?
Will the total time complexity for the code be changed?
I found this code in the internet as an example for what I'm talking about:
int i, j, k, val=0, c=0;
for (int i=n; i>1; i=i/2)
val++;
for (j=1; k<val; j=j*2)
c++;
I thuoght that the time complexity for this code is n^2 but it seems I was wrong
Sorry for my English.

Yes, it can, and in your example, it does. The first loop calculates some value used to determine the number of iterations the second loop will execute. The number of iterations is (related closely to) the complexity.
Currently, the first loop does ~log(n) iterations and the second does ~log(log(n)) iterations. If the first loop were changed to do ~n iterations, the second would do ~log(n). If the first were changed to calculate val in a way that made it ~2^n, the second loop would do ~n iterations.
There's nothing special about this other code being a loop: any code before a loop can affect the complexity of the loop.

Related

What is the worst-case time complexity for this code?

I had a quiz in my class and didn't do so well on it. I'm looking to find out if someone can explain to me what I did wrong here - our professor is overwhelmed with office hours as we moved online so I thought I'd post here.
def functionA(n):
level = n
total = 0
while level > 1:
for i in range(0,n):
level = level // 2
total = total + i
return total
My answer: The above function is O(log n) because the for loop divides the level variable in half on each iteration.
I got 5/10 points but it doesn't really have an explanation as to what was wrong or correct about it. What did I get wrong with this and why?
Image for proof that the quiz was already graded and returned. Just trying to figure it out.
The problem is this line:
for i in range(0,n):
Since n and level are two totally independent variables that are copies of n and n never changes, this loop is always O(n).
Once we've established that the inner loop is O(n), we need to figure out the complexity of the outer loop.
On the first iteration of the outer loop, the inner loop repeatedly sets level = level // 2. Since this assignment will quickly reduce level down to 1, the outer loop is guaranteed to terminate after its first iteration, making it constant time.
We're left with an overall complexity of O(n) for the single iteration of the inner for loop.

What is the time complexity of triple nested for loops of the following?

I have some problems with time complexity determination. Please help me to find out the time complexity of the following problem in detail.
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
for(k=1;k<=p;k++)
print(arr[i][j][k]);
To get the time complexity of a nested loop you need to understand how often every loop executes its body.
In your example for(i=1;i<=n;i++) is a loop running its body n times.
for(j=1;j<=m;j++) will run its body m times and for(k=1;k<=p;k++) will run its body p times.
Since the two inner loops will run each per iteration of their respective outer loop, you can make the following statements.
The first loop will run the second loop n times
The second loop will run the third loop m times, thus the third loop will run n*m times
The third loop will run the print statement p times, thus the print statement is run n*m*p times.
Thus, the time complexity of this code is ϴ(n*m*p).
In general, when talking about nested loops the time complexity of the code will always be in terms of the loop conditions. There are indeed special cases, like dependent loop conditions or loops containing code with break statements for a certainly occurring condition.
Now here I would like to mention that what we actually count here are the comparisons made by the for loops, not really how often the print statement is executed. Still, counting with the print statement and the loop bodies as a base makes it easier and the time complexity is still the same.

running time of pseudocode algorithm

This is an algorithm that I have been given to find the run time of. I know how to do this fairly well except he has not explained what to do for while loops and he said he is not going to. I also do not know what the syntax of begin and end is about. He doesn't normally have that after a for loop, so since it is there now I am confused.
procedure f(n)
s=0;
for i=1 to 5n do
begin
j=4i;
while j<i^3 do
begin
s=s+i-j
j=5j
end
end
Looking at the second loop we can see that the while loop starts from 4i and ends after k iterations, where k is such that 4*i*5^k=i^3, that is k = log_5{i^2/4}. So your run-time is:
In the second to last equation we used Stirling's approximation.

Time complexity of an algorithm with nested loops

I've read a ton of questions on here already about finding the time complexity of different algorithms which I THINK I understand until I go to apply it to the outer loop of an algorithm which states:
for i=1 step i←n∗i while i < n^4 do
I can post the full algorithm if necessary but I'd prefer not to as it is for a piece of homework that i'd like to otherwise complete by myself if possible.
I just can't figure out what the complexity of that loop is. I think its just 4 unless n=1 but I am blank as to how to express that formally. Its that or im totally wrong anyway!
Is anyone able to help with this?
Translating your loop into C (just to make sure I understand your pseudo code):
for (i=1; i < n^4; i = i * n ) {
...
}
The key question is what is i after xth iteration? Answer: i^x (you can prove by induction). So when x is 4, then i is n^4 and the loop exits. So it runs in 4 iterations and is constant time.

How to find the loop invariant and prove correctness?

int i, temp;
a is an array of integers [1...100]
i = 1;
while i < 100
if a[i] > a[i+1]
temp = a[i]
a[i] = a[i+1]
a[i+1] = temp
i = i+1
I'm having trouble understanding how to find loop invariants and writing a formal statement for them. So a loop invariant is just a condition that is true immediately before and after each iteration of a loop. It looks like the code is doing a swap: if the following element is greater in the array than the current one, switch places. I mean from the definition of a loop invariant, it really sounds like it's i < 100 because that must be true for the loop to run, but I don't really understand. Would greatly appreciate some clarification.
Going by your definition, I can see two loop invariant conditions:
1. i < 100
2. a[i] = greater than a[j] for all j < i, where i is the loop variable.
This is in fact one outer loop iteration of bubble sort. At the end of this loop, the highest value in the array bubbles to the top (a[100])
.
Roughly speaking, you are right. A loop invariant is "just a condition that is true immediately before and after each iteration of a loop." However, under this definition, there are literally an infinite number of loop invariants for the code in question and most of them are of no particular interest. For example, i < 101, i < 102, i < 103, ... are all loop invariants for the given code.
However, usually we are not interested in simply finding a loop invariant just for the sake of finding a loop invariant. Instead, we are interested in proving a program correct and if we want to prove a program correct then a well-chosen loop invariant turns out to be very useful.
For example, the code in question is the inner loop of the bubble sort algorithm and its purpose is to make the array "more sorted". Hence, to prove total correctness of this code we must prove three things:
(1) When execution gets to the end of the code, the array is a permutation of the array at the beginning of the code.
(2) When execution gets to the end of the code, the number of inversions in the array is either zero or it is less than the number of inversions in the array at the beginning of the code (this condition helps us prove that the outer loop of the bubble sort algorithm terminates).
(3) The code terminates.
To prove (1) we need to consider three execution paths (and a loop invariant will play a critical role when we consider PATH 2).
(PATH 1) Consider what happens when execution starts at the beginning of the code and arrives at the top of the loop for the first time. Since nothing is done to the array on this execution path, the array is a permutation of the array at the beginning of the code.
(PATH 2) Now consider what happens when execution starts at the top of the loop, goes around the loop, and returns to the top of the loop. If a[i] <= a[i+1] then the swap does not occur and, thus, the array is still a permutation of the array at the beginning of the code (since nothing is done to it). Alternatively, if a[i] > a[i+1] then the swap does occur. However, the array is still a permutation of the array at the beginning of the code (since a swap is a type of permutation). Thus, whenever execution gets to the top of the loop, the array is a permutation of the array at the beginning of the code. Note that the statement "the array is a permutation of the array at the beginning of the code" is the well-chosen loop invariant that we need to help us prove that the code is correct.
(PATH 3) Finally, consider what happens when execution starts at the top of the loop but does not enter the loop and instead it goes to the end of the code. Since nothing is done to the array on this execution path, the array is a permutation of the array at the beginning of the code.
These three paths cover all possible ways that execution can go from the beginning of the code to the end of the code and, hence, we have proved (1) the array at the end of the code is a permutation of the array at the beginning of the code.
A loop invariant is some predicate (condition) that holds for every
iteration of the loop, that is necessarily true immediately before and
immediately after each iteration of a loop.
There can be of course infinitely many loop invariants, but the fact that the loop invariant property is used to prove correctness of the algorithm, restricts us to consider only the so-called "interesting loop invariants".
Your program, whose aim is to sort the given array, is a simple bubble sort.
Goal Statement: The array a is sorted at the end of while loop
Some interesting properties can be like: At the end of ith iteration, a[0:i] is sorted, which when extended to i=100, results in the whole array being sorted.
Loop Invariant for your case: a[100-i: 100-1] is sorted
Note that when i equals 100, the above statement would mean that the complete array is sorted, which is what you want to be true at the end of the algorithm.
PS: Just realized it is an old question, anyway helps in improving my answering skills:)
Your loop is controlled by the test i < 100. Within the body of the loop, i is used in several places but is only assigned in one place. The assignment always happens, and for any value of i which permits entry to the loop the assignment will converge towards the terminating condition. Thus the loop is guaranteed to terminate.
As for correctness of your program, that's a different issue. Depending on whether your arrays use zero-based or one-based indexing, the way you're using i for array accesses could be problematic. If it's zero-based, you never look at the first element and you'll step out of bounds with a[i+1] on the last iteration.

Resources