Am i calculating the big-o correctly? - time-complexity

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)

Related

3 Nested for loops where third loop is dependent on first 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).

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.

Finding the time complexity of a recursive algorithm with a double for loop

I am trying to find the tightest upper bound for the following upper bound. However, I am not able to get the correct answer. The algorithm is as follows:
public staticintrecursiveloopy(int n){
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
System.out.println("Hello.");
}
} if(n <= 2) {
return 1;
} else if(n % 2 == 0) {
return(staticintrecursiveloopy(n+1));
} else{
return(staticintrecursiveloopy(n-2));
}
}
I tried to draw out the recursion tree for this. I know that for each run of the algorithm the time complexity will be O(n2) plus the time taken for each of the recursive calls. Also, the recursion tree will have n levels. I then calculated the total time taken for each level:
For the first level, the time taken will be n2. For the second level, since there are two recursive calls, the time taken will be 2n2. For the third level, the time taken will be 4n 2 and so on until n becomes <= 2.
Thus, the time complexity should be n2 * (1 + 2 + 4 + .... + 2n). 1 + 2 + 4 + .... + 2n is a geometric sequence and its sum is equal to 2n - 1.Thus, the total time complexity should be O(2nn2). However, the answer says O(n3). What am I doing wrong?
Consider the below fragment
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
System.out.println("Hello.");
}
}
This doesn't need any introduction and is O(n2)
Now consider the below fragment
if(n <= 2) {
return 1;
} else if(n % 2 == 0) {
return(staticintrecursiveloopy(n+1));
} else {
return(staticintrecursiveloopy(n-2));
}
How many times do you think this fragment will be executed?
If n%2 == 0 then the method staticintrecursiveloopy will be executed 1 extra time. Otherwise it goes about decresing it by 2, thus it'll be executed n/2 times (or (n+1)/2 if you include the other condition).
Thus the total number of times the method staticintrecursiveloopy will be executed is roughly n/2 which when expressed in terms of complexity becomes O(n).
And the method staticintrecursiveloopy calls a part with complexity O(n2) in each iteration, thus the total time complexity becomes
O(n) * O(n2) = O(n3).

Performance analysis of 3 sum

I have a method that finds 3 numbers in an array that add up to a desired number.
code:
public static void threeSum(int[] arr, int sum) {
quicksort(arr, 0, arr.length - 1);
for (int i = 0; i < arr.length - 2; i++) {
for (int j = 1; j < arr.length - 1; j++) {
for (int k = arr.length - 1; k > j; k--) {
if ((arr[i] + arr[j] + arr[k]) == sum) {
System.out.println(Integer.toString(i) + "+" + Integer.toString(j) + "+" + Integer.toString(k) + "=" + sum);
}
}
}
}
}
I'm not sure about the big O of this method. I have a hard time wrapping my head around this right now. My guess is O(n^2) or O(n^2logn). But these are complete guesses. I can't prove this. Could someone help me wrap my head around this?
You have three runs over the array (the i, j and k loops), in sizes that depend primarily on n, the size of the array. Hence, this is an O(n3) operation.
Even though your quicksort is O(nlogn), it is overshadowed by the fact that you have 3 nested for loops. So the time complexity w.r.t number of elements (n) is O(n^3)
It's an O(n^3) complexity because there are three nested forloops. The inner forloop only runs if k>j so you can think of n^2*(n/2) but in big O notation you can ignore this.
Methodically speaking, the order of growth complexity can be accurately inferred like the following:

Number of possible combinations

How many possible combinations of the variables a,b,c,d,e are possible if I know that:
a+b+c+d+e = 500
and that they are all integers and >= 0, so I know they are finite.
#Torlack, #Jason Cohen: Recursion is a bad idea here, because there are "overlapping subproblems." I.e., If you choose a as 1 and b as 2, then you have 3 variables left that should add up to 497; you arrive at the same subproblem by choosing a as 2 and b as 1. (The number of such coincidences explodes as the numbers grow.)
The traditional way to attack such a problem is dynamic programming: build a table bottom-up of the solutions to the sub-problems (starting with "how many combinations of 1 variable add up to 0?") then building up through iteration (the solution to "how many combinations of n variables add up to k?" is the sum of the solutions to "how many combinations of n-1 variables add up to j?" with 0 <= j <= k).
public static long getCombos( int n, int sum ) {
// tab[i][j] is how many combinations of (i+1) vars add up to j
long[][] tab = new long[n][sum+1];
// # of combos of 1 var for any sum is 1
for( int j=0; j < tab[0].length; ++j ) {
tab[0][j] = 1;
}
for( int i=1; i < tab.length; ++i ) {
for( int j=0; j < tab[i].length; ++j ) {
// # combos of (i+1) vars adding up to j is the sum of the #
// of combos of i vars adding up to k, for all 0 <= k <= j
// (choosing i vars forces the choice of the (i+1)st).
tab[i][j] = 0;
for( int k=0; k <= j; ++k ) {
tab[i][j] += tab[i-1][k];
}
}
}
return tab[n-1][sum];
}
$ time java Combos
2656615626
real 0m0.151s
user 0m0.120s
sys 0m0.012s
The answer to your question is 2656615626.
Here's the code that generates the answer:
public static long getNumCombinations( int summands, int sum )
{
if ( summands <= 1 )
return 1;
long combos = 0;
for ( int a = 0 ; a <= sum ; a++ )
combos += getNumCombinations( summands-1, sum-a );
return combos;
}
In your case, summands is 5 and sum is 500.
Note that this code is slow. If you need speed, cache the results from summand,sum pairs.
I'm assuming you want numbers >=0. If you want >0, replace the loop initialization with a = 1 and the loop condition with a < sum. I'm also assuming you want permutations (e.g. 1+2+3+4+5 plus 2+1+3+4+5 etc). You could change the for-loop if you wanted a >= b >= c >= d >= e.
I solved this problem for my dad a couple months ago...extend for your use. These tend to be one time problems so I didn't go for the most reusable...
a+b+c+d = sum
i = number of combinations
for (a=0;a<=sum;a++)
{
for (b = 0; b <= (sum - a); b++)
{
for (c = 0; c <= (sum - a - b); c++)
{
//d = sum - a - b - c;
i++
}
}
}
This would actually be a good question to ask on an interview as it is simple enough that you could write up on a white board, but complex enough that it might trip someone up if they don't think carefully enough about it. Also, you can also for two different answers which cause the implementation to be quite different.
Order Matters
If the order matters then any solution needs to allow for zero to appear for any of the variables; thus, the most straight forward solution would be as follows:
public class Combos {
public static void main() {
long counter = 0;
for (int a = 0; a <= 500; a++) {
for (int b = 0; b <= (500 - a); b++) {
for (int c = 0; c <= (500 - a - b); c++) {
for (int d = 0; d <= (500 - a - b - c); d++) {
counter++;
}
}
}
}
System.out.println(counter);
}
}
Which returns 2656615626.
Order Does Not Matter
If the order does not matter then the solution is not that much harder as you just need to make sure that zero isn't possible unless sum has already been found.
public class Combos {
public static void main() {
long counter = 0;
for (int a = 1; a <= 500; a++) {
for (int b = (a != 500) ? 1 : 0; b <= (500 - a); b++) {
for (int c = (a + b != 500) ? 1 : 0; c <= (500 - a - b); c++) {
for (int d = (a + b + c != 500) ? 1 : 0; d <= (500 - a - b - c); d++) {
counter++;
}
}
}
}
System.out.println(counter);
}
}
Which returns 2573155876.
One way of looking at the problem is as follows:
First, a can be any value from 0 to 500. Then if follows that b+c+d+e = 500-a. This reduces the problem by one variable. Recurse until done.
For example, if a is 500, then b+c+d+e=0 which means that for the case of a = 500, there is only one combination of values for b,c,d and e.
If a is 300, then b+c+d+e=200, which is in fact the same problem as the original problem, just reduced by one variable.
Note: As Chris points out, this is a horrible way of actually trying to solve the problem.
link text
If they are a real numbers then infinite ... otherwise it is a bit trickier.
(OK, for any computer representation of a real number there would be a finite count ... but it would be big!)
It has general formulae, if
a + b + c + d = N
Then number of non-negative integral solution will be C(N + number_of_variable - 1, N)
#Chris Conway answer is correct. I have tested with a simple code that is suitable for smaller sums.
long counter = 0;
int sum=25;
for (int a = 0; a <= sum; a++) {
for (int b = 0; b <= sum ; b++) {
for (int c = 0; c <= sum; c++) {
for (int d = 0; d <= sum; d++) {
for (int e = 0; e <= sum; e++) {
if ((a+b+c+d+e)==sum) counter=counter+1L;
}
}
}
}
}
System.out.println("counter e "+counter);
The answer in math is 504!/(500! * 4!).
Formally, for x1+x2+...xk=n, the number of combination of nonnegative number x1,...xk is the binomial coefficient: (k-1)-combination out of a set containing (n+k-1) elements.
The intuition is to choose (k-1) points from (n+k-1) points and use the number of points between two chosen points to represent a number in x1,..xk.
Sorry about the poor math edition for my fist time answering Stack Overflow.
Just a test for code block
Just a test for code block
Just a test for code block
Including negatives? Infinite.
Including only positives? In this case they wouldn't be called "integers", but "naturals", instead. In this case... I can't really solve this, I wish I could, but my math is too rusty. There is probably some crazy integral way to solve this. I can give some pointers for the math skilled around.
being x the end result,
the range of a would be from 0 to x,
the range of b would be from 0 to (x - a),
the range of c would be from 0 to (x - a - b),
and so forth until the e.
The answer is the sum of all those possibilities.
I am trying to find some more direct formula on Google, but I am really low on my Google-Fu today...