Finding Big O notation - big-o

I have the following code and I want to find the Big O. I wrote my answers as comments and would like to check my answers for each sentence and the final result.
public static void findBigO(int [] x, int n)
{
//1 time
for (int i = 0; i < n; i += 2) //n time
x[i] += 2; //n+1 time
int i = 1; //1 time
while (i <= n/2) // n time
{
x[i] += x[i+1]; // n+1 time
i++; //n+1 time
}
} //0
//result: 1 + n + n+1 + n + n+1 + n+1 = O(n)

First of all: simple sums and increments are O(1), they are made in constant time so x[i] += 2; is constant since array indexing is also O(1) the same is true for i++ and the like.
Second: The complexity of a function is relative to its input size, so in fact this function's time complexity is only pseudo-polynomial
Since n is an integer, the loop takes about n/2 interactions which is linear on the value of n but not on the size of n (4 bytes or log(n)).
So this algorithm is in fact exponential on the size of n.

for (int i = 0; i < n; i += 2) // O(n)
x[i] += 2;
int i = 1;
while (i <= n/2) // O(n/2)
{
x[i] += x[i+1];
i++;
}
O(n) + O(n/2) = O(n) in terms of Big O.
You have to watch out for nested loops that depend on n, if (as I first thought thanks to double usage of i) you would've had that O(n) * O(n/2), which is O(n^2). In the first case it is in fact about O(1,5n) + C However that is never ever used to describe an Ordo.
With Big O you push the values towards infinity, no matter how large C you have it will in the end be obsolete, just as if it is 1.000.000n or n. The prefix will eventually be obsolete.
That being said, the modifiers of n as well as the constants do matter, just not in Ordo context.

Related

Better understanding of (log n) time complexity

I'm a bit confused on (log n). Given this code
public static boolean IsPalindrome(String s) {
char[] chars = s.toCharArray();
for (int i = 0; i < (chars.length / 2); i++) {
if (chars[i] != chars[(chars.length - i - 1)])
return false;
}
return true;
}
}
I am looping n/2 times. So, as length of n increase, my time is increasing half the time of n. In my opinion, I thought that's exactly what log n was? But the person who wrote this code said this is still O(N).
In what case of a loop, can something be (log n)? For example this code:
1. for (int i = 0; i < (n * .8); i++)
Is this log n? I'm looping 80% of n length.
What about this one?
2. for (int i = 1; i < n; i += (i * 1.2))
Is that log n? If so, why.
1. for (int i = 0; i < (n * .8); i++)
In the first case basically you can replace 0.8n with another variable, let's call it m.
for (int i = 0; i < m; i++) You're looping m number of times. You're increasing value of i one unit in each iteration. Since m and n are just variable names, the Big-O complexity of the above loop is O(n).
2. for (int i = 0; i < n; i += (i * 1.2))
In the second scenario, you're not incrementing the value of i, the value of i is always going to be 0. And it is a classic case of an for-infinite loop.
What you're looking for is 2. for (int i = 1; i <= n; i += (i * 1.2)) Here, you're incrementing the value of i logarithmically(but not to the base 2).
Consider for (int i = 1; i <= n; i += i) The value of i doubles after every iteration. value of i is going to be 1, 2, 4, 8, 16, 32, 64.. Let's say n value is 64, your loop is going to terminate in 7 iterations, which is (log(64) to the base 2) + 1(+1 because we are starting the loop from 1) number of operations. Hence it becomes a logarithmic operation.
2. for (int i = 1; i <= n; i += (i * 1.2)) In your case as well the solution is a logarithmic solution, but it is not to the base 2. The base of your logarithmic operation is 2.2, But in big-O notation it boils down to a O(log(n))
I think you miss what is time complexity and how the big O notation work.
The Big O notation is used to describe the asymptotic behavior of the algorithm as the size of the problem growth (to infinity). Particular coefficients do not matter.
As a simple intuition, if when you increase n by a factor of 2, the number of steps you need to perform also increases by about 2 times, it is a linear time complexity or what is called O(n).
So let's get back to your examples #1 and #2:
yes, you do only chars.length/2 loop iterations but if the length of the s is doubled, you also double the number of iterations. This is exactly the linear time complexity
similarly to the previous case you do 0.8*n iterations but if n is doubled, you do twice as many iterations. Again this is linear
The last example is different. The coefficient 1.2 doesn't really matter. What matters is that you add i to itself. Let's re-write that statement a bit
i += (i * 1.2)
is the same as
i = i + (i * 1.2)
which is the same as
i = 2.2 * i
Now you clearly see that each iteration you more than double i. So if you double n you'll only need one more iteration (or even the same). This is a sign of a fundamentally sub-linear time complexity. And yes this is an example of O(log(n)) because for a big n you need only about log(n, base=2.2) iterations and it is true that
log(n, base=a) = log(n, base=b) / log(n, base=b) = constant * log(x, base=b)
where constant is 1/log(a, base=b)

Time Complexity for Sieve of Eratosthenes: why is it not linear? [duplicate]

From Wikipedia:
The complexity of the algorithm is
O(n(logn)(loglogn)) bit operations.
How do you arrive at that?
That the complexity includes the loglogn term tells me that there is a sqrt(n) somewhere.
Suppose I am running the sieve on the first 100 numbers (n = 100), assuming that marking the numbers as composite takes constant time (array implementation), the number of times we use mark_composite() would be something like
n/2 + n/3 + n/5 + n/7 + ... + n/97 = O(n^2)
And to find the next prime number (for example to jump to 7 after crossing out all the numbers that are multiples of 5), the number of operations would be O(n).
So, the complexity would be O(n^3). Do you agree?
Your n/2 + n/3 + n/5 + … n/97 is not O(n), because the number of terms is not constant. [Edit after your edit: O(n2) is too loose an upper bound.] A loose upper-bound is n(1+1/2+1/3+1/4+1/5+1/6+…1/n) (sum of reciprocals of all numbers up to n), which is O(n log n): see Harmonic number. A more proper upper-bound is n(1/2 + 1/3 + 1/5 + 1/7 + …), that is sum of reciprocals of primes up to n, which is O(n log log n). (See here or here.)
The "find the next prime number" bit is only O(n) overall, amortized — you will move ahead to find the next number only n times in total, not per step. So this whole part of the algorithm takes only O(n).
So using these two you get an upper bound of O(n log log n) + O(n) = O(n log log n) arithmetic operations. If you count bit operations, since you're dealing with numbers up to n, they have about log n bits, which is where the factor of log n comes in, giving O(n log n log log n) bit operations.
That the complexity includes the loglogn term tells me that there is a sqrt(n) somewhere.
Keep in mind that when you find a prime number P while sieving, you don't start crossing off numbers at your current position + P; you actually start crossing off numbers at P^2. All multiples of P less than P^2 will have been crossed off by previous prime numbers.
The inner loop does n/i steps, where i is prime => the whole
complexity is sum(n/i) = n * sum(1/i). According to prime harmonic
series, the sum (1/i) where i is prime is log (log n). In
total, O(n*log(log n)).
I think the upper loop can be optimized by replacing n with sqrt(n) so overall time complexity will O(sqrt(n)loglog(n)):
void isPrime(int n){
int prime[n],i,j,count1=0;
for(i=0; i < n; i++){
prime[i] = 1;
}
prime[0] = prime[1] = 0;
for(i=2; i <= n; i++){
if(prime[i] == 1){
printf("%d ",i);
for(j=2; (i*j) <= n; j++)
prime[i*j] = 0;
}
}
}
int n = 100;
int[] arr = new int[n+1];
for(int i=2;i<Math.sqrt(n)+1;i++) {
if(arr[i] == 0) {
int maxJ = (n/i) + 1;
for(int j=2;j<maxJ;j++)
{
arr[i*j]= 1;
}
}
}
for(int i=2;i<=n;i++) {
if(arr[i]==0) {
System.out.println(i);
}
}
For all i>2, Ti = sqrt(i) * (n/i) => Tk = sqrt(k) * (n/k) => Tk = n/sqrt(k)
Loop stops when k=sqrt(n) => n[ 1/sqrt(2) + 1/sqrt(3) + ...] = n * log(log(n)) => O(nloglogn)
see take the above explanation the inner loop is harmonic sum of all prime numbers up to sqrt(n). So, the actual complexity of is O(sqrt(n)*log(log(sqrt(n))))

how i can find the time complexity of the above code

for(i=0; i<n; i++) // time complexity n+1
{
k=1; // time complexity n
while(k<=n) // time complexity n*(n+1)
{
for(j=0; j<k; j++) // time complexity ??
printf("the sum of %d and %d is: %d\n",j,k,j+k); time complexity ??
k++;
}
What is the time complexity of the above code? I stuck in the second (for) and i don't know how to find the time complexity because j is less than k and not less than n.
I always having problems related to time complexity, do you guys got some good article on it?
especially about the step count and loops.
From the question :
because j is less than k and not less than n.
This is just plain wrong, and I guess that's the assumption that got you stuck. We know what values k can take. In your code, it ranges from 1 to n (included). Thus, if j is less than k, it is also less than n.
From the comments :
i know the the only input is n but in the second for depends on k an not in n .
If a variable depends on anything, it's on the input. j depends on k that itself depends on n, which means j depends on n.
However, this is not enough to deduce the complexity. In the end, what you need to know is how many times printf is called.
The outer for loop is executed n times no matter what. We can factor this out.
The number of executions of the inner for loop depends on k, which is modified within the while loop. We know k takes every value from 1 to n exactly once. That means the inner for loop will first be executed once, then twice, then three times and so on, up until n times.
Thus, discarding the outer for loop, printf is called 1+2+3+...+n times. That sum is very well known and easy to calculate : 1+2+3+...+n = n*(n+1)/2 = (n^2 + n)/2.
Finally, the total number of calls to printf is n * (n^2 + n)/2 = n^3/2 + n^2/2 = O(n^3). That's your time complexity.
A final note about this kind of codes. Once you see the same patterns a few times, you quickly start to recognize the kind of complexity involved. Then, when you see that kind of nested loops with dependent variables, you immediately know that the complexity for each loop is linear.
For instance, in the following, f is called n*(n+1)*(n+2)/6 = O(n^3) times.
for (i = 1; i <= n; ++i) {
for (j = 1; j <= i; ++j) {
for (k = 1; k <= j; ++k) {
f();
}
}
}
First, simplify the code to show the main loops. So, we have a structure of:
for(int i = 0; i < n; i++) {
for(int k = 1; k <= n; k++) {
for(int j = 0; j < k; j++) {
}
}
}
The outer-loops run n * n times but there's not much you can do with this information because the complexity of the inner-loop changes based on which iteration of the outer-loop you're on, so it's not as simple as calculating the number of times the outer loops run and multiplying by some other value.
Instead, I would find it easier to start with the inner-loop, and then add the outer-loops from the inner-most to outer-most.
The complexity of the inner-most loop is k.
With the middle loop, it's the sum of k (the complexity above) where k = 1 to n. So 1 + 2 + ... + n = (n^2 + n) / 2.
With the outer loop, it's done n times so another multiplication by n. So n * (n^2 + n) / 2.
After simplifying, we get a total of O(n^3)
The time complexity for the above code is : n x n x n = n^3 + 1+ 1 = n^3 + 2 for the 3 loops plus the two constants. Since n^3 carries the heaviest growing rate the constant values can be ignored, so the Time complexity would be n^3.
Note: Take each loop as (n) and to obtained the total time, multiple the (n) values in each loop.
Hope this will help !

What is the time complexity of these loops 1 and 2

I was going through an article on analysis of time complexity of loops at a very popular website(link given below) and according to that article the time complexity of below loops 1st and 2nd are O(1) and O(n) respectively.
But i think the time complexity of both loop is same O(n)
for (int i = 1; i <= c; i++) {
// some O(1) expressions
}
My reasoning : `c*n=O(n)
after going through answers below , My reasoning is wrong as there is no varying input n. the loop run is fixed- c times. hence irrespective of the input value n , the loop will run constant time. so O(1) complexity
for (int i = 1; i <= n; i += c) {
// some O(1) expressions
}
My reasoning : c*n=O(n)
Am i missing something ?I would be grateful if someone can help and explain
This is the link of the article : http://www.geeksforgeeks.org/analysis-of-algorithms-set-4-analysis-of-loops/
A loop or recursion that runs a constant number of times is also
considered as O(1).
Here: C is a constant value. So basically, you are performing constant number of operation irrespective of the value of n
// Here c is a constant
for (int i = 1; i <= c; i++) {
// some O(1) expressions
}
Also in Second Loop:
for (int i = 1; i <= n; i += c) {
// some O(1) expressions
}
Your reason c*n = O(n) is not correct. Here
Increment by C. For n elements, loops occur n/c which is asymptotically O(n/c) ~ O(n)
for (int i = 1; i <= c; i++) { // some O(1) expressions }
Here c is a constant. So basically, you are performing constant number of operation irrespective of the value of n. That is why is it considered as constant complexity, O(1).
for (int i = 1; i <= n; i += c) { // some O(1) expressions }
You are looping with a input value n, which is essentially variable with the given input to the program or algorithm. Now the c is again a constant, which will remain same for all the different values of n. The complexity is considered as O(n).
for (int i = 1; i <= n; i++) { // some O(1) expressions }
This is same as the above only, just that value of the c is 1.
All the complexities are represented in asymptotic notation format. Constant factors are removed because they will be same irrespective of the value of n.
1) There is no n in the picture, i dunno why you think it O(n). The loop is going from 1 to c, so its O(c), and as c is a constant, the complexity is O(1).
2) The loop starts from 1 and goes till n, incrementing c at every step. Clearly the complexity is O(n/c), which asymptotically is O(n).
O(1) : The complexity of this loop is O(1) as it runs a constant amount of time c.
// Here c is a constant
for (int i = 1; i <= c; i++) {
// some O(1) expressions
}
O(n): The complexity of the loop is O(n) if it is incremented or decremented by constant amount. For example, these loops have O(n) time complexity.
// Here c is a positive integer constant
for (int i = 1; i <= n; i += c) {
// some O(1) expressions
}
for (int i = n; i > 0; i -= c) {
// some O(1) expressions
}

Complexity of algorithm

What is the complexity given for the following problem is O(n). Shouldn't it be
O(n^2)? That is because the outer loop is O(n) and inner is also O(n), therefore n*n = O(n^2)?
The answer sheet of this question states that the answer is O(n). How is that possible?
public static void q1d(int n) {
int count = 0;
for (int i = 0; i < n; i++) {
count++;
for (int j = 0; j < n; j++) {
count++;
}
}
}
The complexity for the following problem is O(n^2), how can you obtain that? Can someone please elaborate?
public static void q1E(int n) {
int count = 0;
for (int i = 0; i < n; i++) {
count++;
for (int j = 0; j < n/2; j++) {
count++;
}
}
}
Thanks
The first example is O(n^2), so it seems they've made a mistake. To calculate (informally) the second example, we can do n * (n/2) = (n^2)/2 = O(n^2). If this doesn't make sense, you need to go and brush up what the meaning of something being O(n^k) is.
The complexity of both code is O(n*n)
FIRST
The outer loop runs n times and the inner loop varies from 0 to n-1 times
so
total = 1 + 2 + 3 + 4 ... + n
which if you add the arithmetic progression is n * ( n + 1 ) / 2 is O(n*n)
SECOND
The outer loop runs n times and the inner loop varies from 0 to n-1/2 times
so
total = 1 + 1/2 + 3/2 + 4/2 ... + n/2
which if you add the arithmetic progression is n * ( n + 1 ) / 4 is also O(n*n)
First case is definitely O(n^2)
The second is O(n^2) as well because you omit constants when calculate big O
Your answer sheet is wrong, the first algorithm is clearly O(n^2).
Big-Oh notation is "worst case" so when calculating the Big-Oh value, we generally ignore multiplications / divisions by constants.
That being said, your second example is also O(n^2) in the worst case because, although the inner loop is "only" 1/2 n, the n is the clear bounding factor. In practice the second algorithm will be less than O(n^2) operations -- but Big-Oh is intended to be a "worst case" (ie. maximal bounding) measurement, so the exact number of operations is ignored in favor of focusing on how the algorithm behaves as n approaches infinity.
Both are O(n^2). Your answer is wrong. Or you may have written the question incorrectly.

Resources