We've got 2 pieces of code:
int a = 3;
while (a <= n) {
a = a * a;
}
And:
public void foo(int n, int m) {
int i = m;
while (i > 100)
i = i / 3;
for (int k = i ; k >= 0; k--) {
for (int j = 1; j < n; j*=2)
System.out.print(k + "\t" + j);
System.out.println();
}
}
What is the time complexity of them?
I think that the first one is: O(logn), because it's progressing to N with power of 2.
So maybe it's O(log2n) ?
And the second one I believe is: O(nlog2n), because it's progressing with jumps of 2, and also running on the outer loop.
Am I right?
I believe, that first code will run in O(Log(LogN)) time. It's simple to understand in this way
Before first iteration you have 3 in power 1
After first iteration you have 3 in power 2
After second iteration you have 3 in power 4
After third iteration you have 3 in power 8
After fourth iteration you have 3 in power 16
and so on.
In the second code first piece of code will work in O(LogM) time, because you divide i by 3 every time. The second piece of code C times (C equals 100 in your case) will perform O(LogN) operations, because you multiply j by 2 every time, so it runs in O(CLogN), and you have complexity O(LogM + CLogN)
For the first one, it is indeed O(log(log(n))). Thanks to #MarounMaroun for the hint, I could find this:
l(k) = l(k-1)^2
l(0) = 3
Solving this system yields:
l(k) = 3^(2^k)
So, we are looking for such a k that satisfies l(k) = n. So simply solve that:
This means we found:
The second code is seems misleading. It looks like O(nlog(n)), but the outer loop limited to 100. So, if m < 100, then it obviously is O(mlog(n)). Otherwise, it kind of depends on where exactly m is. Consider these two:
m: 305 -> 101 -> 33
m: 300 -> 100
In the first case, the outer loop would run 33 times. Whereas the second case would cause 100 iterations. I'm not sure, but I think you can write this as being O(log(n)).
Related
You are given two strings S and T. An infinitely long string is formed in the following manner:
Take an empty string,
Append S one time,
Append T two times,
Append S three times,
Append T four times,
and so on, appending the strings alternately and increasing the number of repetitions by 1 each time.
You will also be given an integer K.
You need to tell the Kth Character of this infinitely long string.
Sample Input (S, T, K):
a
bc
4
Sample Output:
b
Sample Explanation:
The string formed will be "abcbcaaabcbcbcbcaaaaa...". So the 4th character is "b".
My attempt:
public class FindKthCharacter {
public char find(String S, String T, int K) {
// lengths of S and T
int s = S.length();
int t = T.length();
// Counters for S and T
int sCounter = 1;
int tCounter = 2;
// To store final chunks of string
StringBuilder sb = new StringBuilder();
// Loop until K is greater than zero
while (K > 0) {
if (K > sCounter * s) {
K -= sCounter * s;
sCounter += 2;
if (K > tCounter * t) {
K -= tCounter * t;
tCounter += 2;
} else {
return sb.append(T.repeat(tCounter)).charAt(K - 1);
}
} else {
return sb.append(S.repeat(sCounter)).charAt(K - 1);
}
}
return '\u0000';
}
}
But is there any better way to reduce its time complexity?
I've tried to give a guide here, rather than just give the solution.
If s and t are the lengths of the strings S and T, then you need to find the largest odd n such that
(1+3+5+...+n)s + (2+4+6+...+(n+1))t < K.
You can simplify these expressions to get a quadratic equation in n.
Let N be (1+3+..+n)s + (2+4+6+...+(n+1))t. You know that K will lie either in the next (n+2) copies of S, or the (n+3) copies of T that follow. Compare K to N+(n+2)s, and take the appropriate letter of either S or T using a modulo.
The only difficult step here is solving the large quadratic, but you can do it in O(log K) arithmetic operations easily enough by doubling n until it's too large, and then using a binary search on the remaining range. (If K is not too large so that floating point is viable, you can do it in O(1) time using the well-known quadratic formula).
Here my quick attempt, there probably is a better solution. Runtime is still O(sqrt n), but memory is O(1).
public static char find(String a, String b, int k) {
int lenA = a.length();
int lenB = b.length();
int rep = 0;
boolean isA = false;
while (k >= 0) {
++rep;
isA = !isA;
k -= (isA ? lenA : lenB) * rep;
}
int len = (isA ? lenA : lenB);
int idx = (len * rep + k) % len;
return (isA ? a : b).charAt(idx);
}
Here's a O(1) solution that took me some time to come up with (read I would have failed an interview on time). Hopefully the process is clear and you can implement it in code.
Our Goal is to return the char that maps to the kth index.
But How? Just 4 easy steps, actually.
Step 1: Find out how many iterations of our two patterns it would take to represent at least k characters.
Step 2: Using this above number of iterations i, return how many characters are present in the previous i-1 iterations.
Step 3: Get the number of characters n into iteration i that our kth character is. (k - result of step 2)
Step 4: Mod n by the length of the pattern to get index into pattern for the specific char. If i is odd, look into s, else look into t.
For step 1, we need to find a formula to give us the iteration i that character k is in. To derive this formula, it may be easier to first derive the formula needed for step 2.
Step 2's formula is basically given an iteration i, return how many characters are present in that iteration. We are solving for 'k' in this equation and are given i, while it's the opposite for step 1 where were are solving for i given k. If we can derive the equation of find k given i, then we can surely reverse it to find i given k.
Now, let's try to derive the formula for step 2 and find k given i. Here it's best to start with the most basic example to see the pattern.
s = "a", t = "b"
i=1 a
i=2 abb
i=3 abbaaa
i=4 abbaaabbbb
i=5 abbaaabbbbaaaaa
i=6 abbaaabbbbaaaaabbbbbb
Counting the total number of combined chars for each pattern during its next iteration gives us:
#iterations of pattern: 1 2 3 4 5 6 7 8 9 10
every new s iteration: 1, 4, 9, 16, 25, 36, 49, 64, 81, 100
every new t iteration: 2, 6, 12, 20, 30, 42, 56, 72, 90, 110
You might notice some nice patterns here. For example, s has a really nice formula to find out how many combined characters it has at any given iteration. It's simply (# of s iterations^2)*s.length. t also has a simple formula. It is (# of t iterations * (# of t iterations + 1))*t.length. You may have noticed that these formulas are the formulas for sum of odd and even numbers (if you did you get a kudos). This makes sense because each pattern's sum for an iteration i is the sum of all of its previous iterations.
Using s,t as length of their respective patterns, we now have the following formula to find the total number of chars at a given iteration.
#chars = s*(# of s iterations)^2 + t * (# of t iterations * (# of t iterations + 1))
Now we just need to do some math to get the number of iterations for each pattern given i.
# of s iterations given i = ceil(i/2.0)
# of t iterations given i = floor(i/2) which / operation gives us by default
Plugging these back into our formula we get:
total # of chars = s*(ceil(i/2.0)^2) + t*((i/2)*((i/2)+1))
We have just completed step 2, and we now know at any given iteration how many total chars there are. We could stop here and start picking random iterations and adjusting accordingly until we get near k, but we can do better than that. Let's use the above formula now to complete step 1 which we skipped. We just need to reorganize our equation to solve for i now.
Doing some simplyfying we get:
// 2
// i i i
// s (-) + t - ( - + 1 ) = k
// 2 2 2
// ----------------------------
// 2
// i t i
// s - + - ( - + 1 )i = k
// 4 2 2
// ----------------------------
// 2 2
// si ti ti
// ---- + ---- + ---- - k = 0
// 4 4 2
// ----------------------------
//
// 2 2
// si + ti + 2ti - 4k = 0
// ----------------------------
// 2
// (s + t)i + 2ti - 4k = 0
// ----------------------------
This looks like a polynomial. Wow! You're right! That means we can solve it using the quadratic formula.
A=(s+t), B=2t, C=-4k
quadratic formula = (-2t + sqrt(2t^2 + 16(s+t)k)) / 2(s+t)
This is our formula for step 1, and it will give us the iteration that the kth character is on. We just need to ceil it. I'm actually not smart enough to know why this works. It just does. Here is a desmos graph that graphs our two polynomials from step 2: s(Siterations)^2 and t(Titerations (Titerations + 1)).
The area under both curves is our total number of chars at an iteration (the vertical line). The formula from step 1 is also graphed, and we can see that for any s, t, k that the x intercept (which represents our xth iteration) is always: previous iteration < x <= current iteration, which is why the ceil works.
We have now completed steps 1 and 2. We have a formula to get the ith iteration that the kth character is on and a formula that gives us how many characters are in an ith iteration. Steps 3 and 4 should follow and we get our answer. This is constant time.
I was wondering if the complexity of a empty for loop like below is still O(n^2)
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
}
}
update : changed height and width variable to n
If it won't get optimized out by the compiler, the complexity will still be O(n^2) (or actually O(N*M)) - even though the loops bodies are empty, the condition checks and incrementation of both counters are still valid operations which have to be performed.
The complexity of any for loop that runs from 1 .. n is O(n), even if it does not do anything inside it. So in your case it is always going to be O(n^2) irrespective of what you are doing inside the loops.
Here in your example i and j are running till n and hence individually depends on the value of n making the the nested for loops having a complexity of O(n^2)
Pay attention, you can do something else than i++, e.g. fun(i).
Based off of my understanding of time-complexity of an algorithm, we assume that there are one or more fundamental operations. Re-writing the code using a while loop and expanding for logic :
int i = 0, j = 0;
while(i < n)
{
while(j < n)
{
; //nop or no-operation
j = j + 1; // let jInc be alias for j + 1
}
i = i + 1; // let iInc be alias for i + 1
}
Now if your objective is to perform a 'nop' n^2 times, then the time complexity is O(0) where 'nop' is the fundamental operation. However, if the objective is to iterate 2 counters ('i' and 'j') from 0 to n -1 or count n^2 times then the fundamental operations can be addition (j + 1 and i + 1), comparison (i < n and j < n) or assignment (i = iInc and j = jInc) i.e. O(n^2).
Big O is just an approximation for evaluating count of steps in algorithm.
We could have formulas for exact count of steps in algorithm, but they are complex and difficult to realise the actual complexity.
1) O(0.000 000 001*n^2 - 1 000 000 000) = n^2
2) O(1 000 000 000*n ) = n
Despite of Big O first case is less e.g. for N = 0..1 000 000
More than, it doesn't take into account how fast particular step.
So, your loop is a case when O(n^2) could be less than O(1)
The nested loop performs constant work O(1) n times, so nO(1)=O(n)O(1)=O(n).
The external loop performs the above mentioned O(n) work n times so nO(n)=O(n)O(n) =O(n^2).
In general:``
f(n) ∈ O(f(n))
cf(n) ∈ O(f(n)) if c is constant
f(n)g(n) ∈ O(f(n)g(n))
It depends on the compiler.
Theoretically, it's O(n), where n is the number of loops, even if there's no task inside the loop.
But, in case of some compiler, the compiler optimizes the loop and doesn't iterates n times. In this situation, the complexity is O(1).
For the loop mentioned above, it's both O(n) and O(n^2). But, it's good practice to write O(n^2) as Big O covers upper bound.
I have this simple code sample that I don't understand.
// Compute integer powers of 2.
class Power {
public static void main (String args[]) {
int e, result;
for (int i = 0; i < 10; i++) {
result = 1;
e = i;
while (e > 0) {
result *= 2;
e--;
}
System.out.println("2 to the " + i + " power is " + result);
}
}
}
Producing this output.
2 to the 0 power is 1
2 to the 1 power is 2
2 to the 2 power is 4
2 to the 3 power is 8
2 to the 4 power is 16
2 to the 5 power is 32
2 to the 6 power is 64
2 to the 7 power is 128
2 to the 8 power is 256
2 to the 9 power is 512
Questions:
How come result is not always 2, since it's being re-initialized every time the for loop is entered?
The e-- decrement doesn't do a thing either, does it? Since, again, e is being set equal to i afresh on every iteration.
Thanks.
How come result is not always 2, since it's being re-initialized every
time the for loop is entered?
Yes, it is being re-initialized but only in the first loop. Your inner loop is looping while(e > 0) and double result at each iteration. Then once you are done looping, you pring the result and restart. The value of result will depend on e which define the number of times result is doubled.
The e-- decrement doesn't do a thing either, does it? Since, again, e
is being set equal to i afresh on every iteration.
Again, yes it is being set back to i at each iteration but that doesn't mean it is useless. At each iteration, e is set back to the new value of i, and then you use it to create an inner loop while e > 0 where at each iteration you decrement e of 1 and double the result.
The two questions kind of go together.
You see, e is set to i which is actually INCREASED each iteration.
And the higher e is, the more often the inner while will be worked through.
So for example in your 3rd for iteration
i = 2
e = 2
result = 1
So first while:
result = result*2 = 1*2 = 2
e = 1
e is still > 0 so after the second while:
result = result*2 = 2*2 = 4
e = 0
And there we go.
e-- did something twice and result is NOT 2.
result and e are re-initialized at the top of the for loop, but are modified within the while loop before result is displayed at the bottom of the for loop.
Studying for exams I have been given a lot of code to manually compute. This question its throwing me for a loop.
public static int[] mystery(int x) {
int[] result = new int[x / 2];
for (int i = 0; i < x/2; i++) {
result[i] = (i * i) % x;
}
return result;
}
for:
mystery(5)
mystery(10)
What I computed was [0, 1] for mystery(5) and [0, 1, 4, 4, 1] for mystery(10); however, I believe that those answers are not correct.
When going about questions like these, what strategies may help lead me to the answer faster?
Also if you conclude to an answer what were the steps you took to arrive at the answer?
Your answer to mystery(5) is correct.
The array for mystery(10) should contain [0,1,4,9,6], since you're calculating square numbers mod 10 in this case.
In my opinion there is really no strategy to get a result faster. It's all about experience in reading the code.
The steps I took for this particular answer were basically
look at the argument (x=10)
look where the argument appears and insert it
go through the code step by step
look at first line -> int division -> array has size 5
look at for loop, insert argument and work with concrete numbers -> for(int i = 0; i < 5; i++) => so i goes from 0 to 4
look at expression and decide, which values are changing and which ones are fixed -> result[i] = (i*i)%10;
Think, what loop does => ith square number mod 10 goes into array at index i
So result should be [0,1,4,9,6]
The key thing you should observe when reading this method is that the iterations of the loop are data-flow independent; the effect of iteration i can be analyzed in isolation, without needing to know what happened at any other iterations.
Thus there's really only one fact you need to be thinking about: resulti = i2 mod x. Then you can forget about the code and just do the math.
Where x = 10:
result0 = 02 mod 10 = 0
result1 = 12 mod 10 = 1
result2 = 22 mod 10 = 4
result3 = 32 mod 10 = 9
result4 = 42 mod 10 = 6
I'm reading the book 'Algorithms - Fourth edition' by Sedgewick and Wayne and I must admit that some parts in the "Analysis of Algorithms" chapter are confusing me! This is probably caused by my lack of mathematical knowledge... Anyways!
Somewhere in the book, there is an example of a program where the inner loop is said to be executed exactly N(N-1)(N-2)/6 times. Here it is:
public static int count(int[] a) {
int count = 0;
for (int i = 0; i < a.length; i++) {
for (int j = i + 1; i < a.length; j++) {
for (int k = j + 1; k < a.length; k++) {
if (a[i] + a[j] + a[k] == 0) {
count++;
}
}
}
}
return count;
}
I am familiar with the big O notation but when it comes to counting the exact number of opreations in loops, I'm lost. I understand the N(N-1)(N-2) part but why do we have to divide by 6? What is the logic behind it?
Any help would be greatly appreciated!
If you can understand the N(N-1)(N-2) part, here's a thought:
Take a combination of 3 numbers, i, j, k, whatever 3 that fall into the range 0 <= i,j,k < N and different one from another (this is also taken care in the code and that's why the formula is N(N-1)(N-2) and not N^3.
Now, lets say the numbers are 13, 17, 42. It doesn't really matters whoch numbers they are. In how many ways can you put them in line?
13-17-42
13-42-17
17-13-42
17-42-13
42-13-17
42-17-13
Six!
How many of these ways can appear in the code? Only one! (that's taken care in the initializaton of j and k).
So, the total number of N(N-1)(N-2) should be divided by 6.
You can use Sigma notation and discover how to come up with the formula mentioned in your book:
As we know...
1+2+3+4...+N => N(N-1)/2
Similarly, the innermost loop works something like
1.n+2(N-1)+3(N-2)+...N.1 => N(N-1)(N-2)/6
Here is a proof for this.
The 6 is derived from 3! (three factorial derived from three loops).
Consider the outer-most loop on, say, an array of 5 elements. You're counting N=5 for that part of the equation, but you'll only reach your inner loop for values i=0, i=1 or i=2. Similarly, you're representing the next loop by (N-1), but you'll only reach your inner loop for values j=1, j=2, or j=3 and not the four values implied by (N-1).
Dividing by 6 (for three loops) compensates for the values that would run out of values in the array before reaching the inner-most loop.