3 Nested for loops where third loop is dependent on first time complexity - time-complexity

I'm trying to find the time complexity for 3 nested for loops. I'm a little lost on how to do this because the the first and third are dependent. From what I did I found that the pattern is n(1 + 2 + 3) so O(n^2) but I'm unsure if that's right. I'm also unsure if this includes the j loop or would I have to multiply a n to my current answer. Any help is much appreciated.
for (int i = 0; i < n*n; i++) {
for (int j = 0; j < n; j++) {
for (int k = 0; k < i; k++) {
// print some statement here
}
}
}

Short Answer:
Assuming the innermost loop operation is O(1), the time compexity of your code is O(n^5).
Longer Answer:
Let's start with a simpler example of 2 dependent loops:
for (int i=0; i<n; ++i) {
for (int j=0; j<i; ++j) {
// Some O(1) operation
}
}
The outer loop will run n times and the inner loop will run 1...n times, and on average:
(1 + 2 + ... + n)/n = n(n+1)/2/n = O(n)
So the overall complexity for this simpler example is O(n^2).
Now to your case:
Note that I assumed the operation in the innermost loop is done in O(1).
for (int i=0; i< n*n; i++){
for (int j=0; j<n; j++){
for (int k=0; k<i; k++){
// Some O(1) operation
}
}
}
The 1st outer loop will run n^2 times.
The 2nd outer loop (i.e. the middle loop) will run n times.
So the 2 outer loop together will run in O(n^3).
The number of times the inner loop will run on average is now O(n^2) because the number of iterations will now be 1..n^2 (instead of 1..n):
(1 + 2 + ... n^2)/n^2 = (n^2)(n^2+1)/2/(n^2) = O(n^2).
Therefore the overall time complexity is O(n^5).
Addendum:
The code below is not in any case a proof regarding the complexity, since measuring for specific values of n does not prove anything about the asymptotic behavior of the time function, but it can give you a "feel" about the number of operations that are done.
#include <iostream>
#include <ctype.h>
void test(int n)
{
int64_t counter = 0;
for (int i = 0; i < n * n; i++) {
for (int j = 0; j < n; j++) {
for (int k = 0; k < i; k++) {
counter++;
}
}
}
std::cout << "n:" << n << ", counter:" << counter << std::endl;
}
int main()
{
test(10);
test(100);
test(1000);
}
Output:
n:10, counter:49500
n:100, counter:4999500000
n:1000, counter:499999500000000
I believe it is quite clear that the number of operations is close to n^5/2, and since constants like 1/2 do not apply: O(n^5).

Related

How to calculate complexity of such loop

What is time complexity of such loop?
for (int i = 0; i < array.length; i++){
for(int j = i + 1; j < arrray.length; j++){
// Some O(1) operation
}
}
Two nested loops O(n^2)
The time complexity of a nested loop is equal to the number of times the innermost statement is executed.
More info on time complexity and a fun read refer to:
https://www.enjoyalgorithms.com/blog/time-complexity-analysis-of-loop-in-programming

Calculating Big O and time complexity of code as a linked list vs array based

Hi I'm trying to calculate the big O notation of this code, assuming the list that is being used is a linked list
public static void update(List<Star> list) {
// compute and apply acceleration
for (int i = 0; i < list.size(); i++) {
Star s1 = list.get(i);
for (int j = i + 1; j < list.size(); j++) {
Star s2 = list.get(j);
Vector acceleration = attractionAcceleration(s1, s2);
s1.velocity.add(acceleration);
acceleration.negate();
s2.velocity.add(acceleration);
}
}
// apply velocity
for (int i = 0; i < list.size(); i++) {
Star s = list.get(i);
s.location.add(s.velocity);
}
}
}
I was also asked to calculate the big O assuming the list is an array based list and I got O(N^2) due to it being 2 nested loops. I have been told that the answer for it as a linked list is O(N^4), i'm just not sure how I explain either of these calculations completely
When calculating the linked list, remember that list.get is O(N). So for the first two lines of your code, you have O(N^2) for lists and O(N) for arrays.
for (int i = 0; i < list.size(); i++) { O(N) for both
Star s1 = list.get(i); O(N) for list, O(1) for array
for (int j = i + 1; j < list.size(); j++) { O(N) for both
Star s2 = list.get(j); O(N) for list, O(1) for array
}
}
So you have two extra O(N) for lists. The nested for loops multiply, and Star s1... adds, since it is not in the internal loop, giving O(N^3) for the acceleration phase.

Am i calculating the big-o correctly?

Following loops:
for(var i = 0; i < A; i++) {
for(var j = 0; j < B; j++) {
for(var k = 0; k < C; k++) {
//not concerned with instructions here
}
}
}
As I understand each loop complexity is 2n+2 so based on that I calculate the complexity of above nested loops to be (2A+2)*((2B+2)*(2C+2)). Is this correct? if so, how do I get the big-o out of it?
Edit 1
I've learned so much about big-o since this question was asked and have found an interesting visualization that I'd like to place here in case others come across this thread. For detailed reference (way better than student textbooks) and original drawing check out Wikipedia. There are a variety of time complexities explained there.
Since the original question involves three nested loops each with a different n, then the big-o is O(A * B * C) as mentioned in the answers. The difficulty arises when we try to determine the big-o for something like this where A is an array of objects (aka hash in some languages). The algorithm itself non-sense and is for demonstration only (although I've been asked non-sense in interviews before):
var cache = {}
for(var i = 0; i < A.length; i++) {
var obj = A[i]
if(!obj.someProperty) {
continue;
}
else if(cache[obj.someProperty]) {
return obj;
}
else if(obj.someProperty === 'some value') {
for(var j = 1; j < A.length; j++) {
if(A[j].someProperty === obj.someProperty) {
cache[obj.someProperty] = obj.someProperty
break
}
}
}
else {
for(var j = i; j < A.length; j++) {
//do something linear here
}
}
}
The outer loop is O(A.length). For inner loops:
obj.someProperty does not exist, we have no complexity per theory.
obj.someProperty is in the cache, we have no complexity per theory.
obj.someProperty is equal to some value either of:
we have O(A.length - 1) where there are no duplicates
We have O(A.length - x) where A.length - x refers to a duplicate's index within A.
Everything else, we have O(log A.length)
At best performance this algorithm gives us O(3) when A[0] and A[1] are considered duplicates and A[0].someProperty === 'some value' as we'll have one iteration for outer loop and one iteration of inner loop (3.2 A.length - x = index 1, finally 3rd iteration returns cached value breaking out of the outer loop entirely. At worse we'll have O(A.length log A.length) as the outer loop and inner loop at 4 are exhausted when no object has someProperty === 'some value'.
To "optimize" this algorithm we can simply write as follows:
for(var i = 0; i < A.length; i++) {
if(A[i].someProperty === 'some value') {
return obj
}
else {
for(var j = i; j < A.length; j++) {
//do something linear here
}
}
}
The outermost for-loop runs a total of A times. For each iteration, the second-level for-loop runs B times, each time triggering C iterations of the omitted instructions.
Thus, the time complexity is O(A * B * C).
Constants are ignored while calculating the time complexity
O((2A+2)*((2B+2)*(2C+2)))
=> O((2A)(2B)(2C))
=> O(8*ABC)
=> O(ABC)

Big-O Complexity of Two Problems

I was practicing a few Big-O complexity problems for one of my classes and these two problems seem to stump me the most.
For both of these, I need to determine the best and worst-case complexity.
Q1
function FUNC3(int array[n], int n, int key)
int i = 1;
while (i < n) do {
if (key == array[0]) then
i = i + n^0.25;
else
i = i + n^0.5;
}
The best-case I got was: O(n / n^0.5) while my worst-case was: O(n / n^0.25)
Q2
function FUNC4(int array[n], int n, int key)
for (int i=1; i<n; i = i * 2) do
for (int j=0; j<sqrt(n); j++) do {
if(array[0] == key) then {
int k = 1;
while (k < n) do
k = k * sqrt(n);
}
}
For this one, I got best-case: O(logn x sqrt(n)), with a worst-case of: O(logn x n)
Although, I am not very confident in these answers, do any of these look about right?
Let's visit each of these individually. Here's your first function:
function FUNC3(int array[n], int n, int key)
int i = 1;
while (i < n) do {
if (key == array[0]) then
i = i + n^0.25;
else
i = i + n^0.5;
}
You are correct that the best-case runtime is Θ(n / n0.5) and that the worst case is Θ(n / n0.25). It might help to rewrite these by simplifying the exponents; the first runtime is
Θ(n / n0.5) = Θ(n0.5) = Θ(√n)
and the second runtime is
Θ(n / n0.25) = Θ(n3/4).
Now, let's look at the second function:
function FUNC4(int array[n], int n, int key)
for (int i=1; i<n; i = i * 2) do
for (int j=0; j<sqrt(n); j++) do {
if(array[0] == key) then {
int k = 1;
while (k < n) do
k = k * sqrt(n);
}
}
To determine the runtime, let's use the time-honored maxim
"When in doubt, work inside out!"
Let's begin with the innermost loop:
int k = 1;
while (k < n) do
k = k * sqrt(n);
This loop is sneaky - it never runs more than three times because the values of k will be 1, then √n, then n. This means that the loop does O(1) total work. As a result, we can rewrite the overall code as
function FUNC4(int array[n], int n, int key)
for (int i=1; i<n; i = i * 2) do
for (int j=0; j<sqrt(n); j++) do {
if(array[0] == key) then {
do O(1) work;
}
}
Since the if statement does O(1) work regardless of whether it executes, we're left with
function FUNC4(int array[n], int n, int key)
for (int i=1; i<n; i = i * 2) do
for (int j=0; j<sqrt(n); j++) do {
do O(1) work;
}
If we do O(1) work √n times, then the runtime is Θ(√n), so the inner loop becomes
function FUNC4(int array[n], int n, int key)
for (int i=1; i<n; i = i * 2) do
do sqrt(n) work
Since the work done in the inner loop is independent of the value of i, the work done here is simply the product of the number of outer loop iterations and the work done by one iteration. The outer loop runs Θ(log n) times, so the work here is Θ(√n log n), regardless of the array contents. So that makes the best- and worst-case runtimes for the function the same, since the total work done is (asymptotically) always the same.
Hope this helps!

time complexity ( studying for exam)

I am currently studying to an examen in algorithms and I am trying to solve a question about time complexity in java, but can't really figure out how to do it. I am suppose to calculate the expected time complexity. N is a positive integer.
for (int i=0; i < N; i++)
for (int j=i+1; j < N; i++) {
int x=j+1; int h=N-1; int k;
while(x<h) {
k=(x+h)/2;
if (a[i]+a[j]+a[k] == 0) { cnt++; break;}
if (a[i]+a[j]+a[k] < 0) x=k+1;
else h=k-1;
}}
The first for loop should run N times and the second should run N-1. Since x is j+1 I guessed that x= N-2. I dont know how to think after that with the while loop or if I have done anything right. Would really appreciate help!
Create your time complexity function in parts.
for (int i=0; i < N; i++) //Takes linear O(n)
for (int j=i+1; j < N; i++) { //Takes linear O(n) and in computer science we can safely assume -1 is irrelevant at N-1 in big O notation
int x=j+1; int h=N-1; int k; // 3 x O(1)
while( x < h ) { // Worst case is when j equals i + 1 where i = 0 so x is at lowest 2 and h equals to N-1 so h depends on N. So again loop takes linear O(n) time.
k=(x+h)/2; // Takes O(1) time
if (a[i]+a[j]+a[k] == 0) { // Takes O(1) time and if this gives true we do break from the while loop
cnt++; // Takes O(1) time
break; // Takes O(1) time
}
if ( a[i]+a[j]+a[k] < 0 ) { // Takes O(1) time
x=k+1; // Takes O(1) time
} else {
h=k-1; // Takes O(1) time
}
}
}
}
So in summary T(N) equals to O(N^3) and Ω(N^2)
More specific T(N) = N * N-1 * N-2 + 10 and this last while loop in avarage takes O(N/2) time but still in computer science it is same as O(N).
We are only interested in worst and best cases.
To confuse even more big O notation actually
T(N)=O(g(N)) means this:
I hope this answer helps even little bit...