Reputation:
It is very difficult for me to analyse the time complexity and output of the following code.I literally was not able to find even the output. I know that
1<< N
is left shift 1 by N bit .
#include <stdio.h>
#define N 3
int main() {
int array[N] = {1,2,3};
int i,j;
for ( i=1; i<(1<<N); i++) {
for( j=0; j<N; j++) {
if((1<<j)&i) {
printf("%d", array[j]);
}
}
printf("\n");
}
return 0;
}
What I have tried
when N=3 ,
1<<N
will be 8 So outer loop execute for 8 times and inner loop execute for 3 time
But how does
if((1<<j)&i)
works ?
When N is n , positive integer I think the complexity will be O(n 2^n)
Please help me analysing the code and complexity of the program
Upvotes: 3
Views: 211
Reputation: 430
Q1) If condition will be tested N * (2^N-1) times.
The body of the if will execute for each set bit in i.
In this instance, i takes values in the range [1, 8]. In this range, 3 values have 1 bit set; 3 values have 2 bits set; 1 value has 3 bits set. You have 3 * 1 + 3 * 2 + 1 * 3 = 12 executions of if body.
For N = n > 0, you can think how many different numbers of length n have exactly k bits set. This is combinations of n elements in groups of k, which is the Newton binomial (n over k).
So, executions (n) = sum (i * (n over i)) for i in [0, n].
This is executions (n) = n * 2^(n-1)
Proof:
sum (i*(n over i)) for i in [0, n] = sum (i*(n over i)+(n-i)*(n over n-i)) for i in [0, n/2]
But (n over i) = (n over n-i)
Therefore, we can write the previous expression as
sum ((i + (n-i))*(n over i)) = n * sum (n over i) for i in [0, n/2]
We know sum (n over i) for all i in [0, n] = 2^n
As it is symmetric, the sum in half the range is 2^n/2 = 2^(n-1)
Therefore,
executions (n) = n * 2^(n-1)
For example, executions (3) = 3 * 2^2 = 12
Q2)
The output is all possible combinations of the elements in array, regardless of the order, excluding the empty set.
The array has size N and the program generates all possible binary numbers of length N, except zero.
It matches bit positions with array positions, and prints out the values of the array in positions corresponding to set bits. Of course, if you do this for all possible N-bit values, you have all possible combinations of set bits and thus all possible combinations of array elements (excluding the empty set).
You could include the empty set by initialising i to zero, but you would only get an empty line.
How does the program match set bits with array positions?
Ok j goes from 0 to N-1, thus scanning all bit/array positions.
To check the value of bit j you compute the bitwise and between the current value of i and a mask consisting of only a one at position j and zeroes at all other positions. Therefore, the bitwise and will result in zero for all positions but j, and the actual value of the bit in position j of i.
1 << j generates the mask; it just shifts a single 1 to its desired position.
Then, (1 << j) & i is the biwise and of the mask with i. If the result is nonzero, jth bit in i is 1. If the result is zero, the bit is 0.
Q3)
Well, N is a constant. So, strictly speaking, complexity is O (1).
But I understand you are interested in complexity considering N the variable size of the problem.
In this case, the outer loop is executed 2^N - 1 times, and the inner loop, N times. This is N * (2^N - 1) in O (N*2^N).
If printf dominates, we know it's executed (ignoring line feeds) N * 2^(N-1) times = (N * 2^N) / 2, also in O (N*2^N).
Upvotes: 0
Reputation: 144695
Your analysis is mostly correct regarding the number of iterations of the outer loop and the if
test. As long as N
is such that 1 << N
does not cause an overflow (thereby invoking undefined behavior), the if
is executed N * (2N - 1) times.
The question Q1 is more subtile: how many times does the if
execute successfully for any positive integer?
if
executes successfully as long as it does not invoke undefined behavior. In other words: as long as n < CHAR_BIT * sizeof(int) - 1
. Indeed for a large N
, (1 << N)
invokes undefined behavior as it causes arithmetic overflow. For example if type int
is 32-bit wide, the maximum value for which 1 << N
is defined is 30
.N
bits or less. The inner loop iterates over all bit numbers, testing every bit once. The total number of if
tests is n * 2n - n. each bit is tested for every possible combination: half the tests evaluate to 0
and half to non-zero, while the n tests for value 0
are omitted. The answer to question Q1 is n * 2n-1 - n, which can be factored as n * (2n-1 - 1).Half the tests succeed and invoke printf()
. Simplifying the negligible values and constant factor 2, the time-complexity of this function for large values of n comes out as O(n * 2n).
Upvotes: 1
Reputation: 178
Q1) I think it is a similar question to Q3
Q2) The code which is inside of for loop will be executed how many integer 'i' has 1 bits. if N = n, 'i' is in the range of 1 to 2^n - 1. It means you can easily think this way
There are n bit slots. 'i' will iterate all cases which n bit slots can make except 0.
For example, In the case of N = 3, there are 3 bits slots OOO and 'i' will iterate all cases except 0. 001, 010, 011, 100, 101, 110, 111.
As I mentioned, the code which is inside of for loop will be executed starting with the lowest index and print a number when '1' bit on.
N = 3,
1
2
12
3
13
23
123
Q3) O(N*2^N). It's too large to calculate I think.
Upvotes: 0