I have the following algorithm:
for(int i=0; i<list.size(); i++){
String cur = list.get(i);
int cnt =0;
while(output.contains(cur)){
cur= cur.substring(0,5) + String.valueOf(cnt);
cnt++;
}
output.add(cur);
}
Not that "list" and "output" are ArrayLists.
I'm thinking that the Time complexity is O(n^2). But what about the while loop that has a output.contains(cur) inside ?
The complexity of this algorithm seems to be depending on the initial contents of output list:
output is empty, while loop is not executed, complexity is O(N), where N is the size of list
list and output are crafted to comply with output.contains(cur) condition
For example,
List<String> list = Arrays.asList("abcde0", "abcde1", "abcde2", "abcde3", "abcde4", "abcde5", "abcde6");
List<String> output = new ArrayList<>(list);
The size of output will be growing thus the number of iterations will be like this:
1) n+1
2) n+1 + n+2 = 2 (n+1) + 1
3) n+1 + n+2 + n+3 = 3 (n+1) + 3
4) n+1 + n+2 + n+3 + n+4 = 4 (n+1) + 6
...
n) n (n+1) + (1 + n-1)*(n-1)/2 = n (n+1) + n (n - 1)/2 = n (3n + 1)/2
Thus, in this (possibly not the worst case) the complexity can be O(N^2).
Related
I can't seem to figure out the time complexity for this algorithm. I know that the while loop will execute at O(log n) times because n keeps halving. But I'm not super sure how to represent the time complexity of the inner for loop.
while(n>0){
for(int j = 0; j < n; j++){
System.out.println("*");
}
n = n/2;
}
Each while loop divides n by 2.
Therefore, the first for loop would run n times, the second for loop would run n/2 times, the third for loop would run n/4 times, and so on.
If we set n to an arbitrary large value, then the complexity would be:
n + n/2 + n/4 + n/8 + n/16 + n/32 + ... = 2n
Of course, n is an integer, and programmatically, the division would result in 0 after enough repetitions.
Therefore, the time-complexity of the algorithm would be O(2N) = O(N)
We can figure out the complexity of both loops by inspection. For an input of n = 8, the inner loop would print out this many stars at each iteration:
8 + 4 + 2 + 1 = 15 ~ 2^4
For n = 16:
16 + 8 + 4 + 2 + 1 = 31 ~ 2^5
The complexity here is:
O(2^([log_2(N)]+1)) = 2*O(2^log_2[N])
= O(N)
We can see that at each step the number of print statements is roughly twice the value of the input N. So the overall number of print operations is actually O(N), which is linear in the actual value of the input N.
Lets n₀ be the start value of n.
At the i-th (from 0) iteration of the while loop, n = n₀ * ½ⁱ
The cost of one iteration of the while loop is n (because of the for loop)
Hence the global cost is: sum for i from 0 to whatever of n₀ * ½ⁱ
Or: n₀ times the sum for i from 0 to whatever of ½ⁱ
The sum is a sum of a geometric series, and is less than 2.
Hence the global cost is O(2.n₀) or O(n₀)
public static int[] intSum(int[] arr2) {
int[] arr1 = new int[arr2.length];
for (int i = 0; i < arr2.length; i++) {
for (int j = 0; j <= i; j++) {
arr1[i] += arr2[j];
}
}
return arr1;
}
Given the method, we were told that the inner loop can be summarized using Gauss's sum identity, and as such, the complexity in polynomial form is
T(n) = c_0 + c_1 * n + c_2 * n + (1 + 2 + ... + (n-1)) * c_3
=> T(n) = c_0 + n * c_1 * n * c_2 * (n(n+1))/2 * c_3
I don't really understand this calculation. I get that c_1 * n is the array initialization which is O(n) in Java, and that c_2 * n would be the outer loop, but how does the sum up from 1 to n-1 work here and how is that related to the inner loop?
Ignoring the syntax errors in your code snippet, the inner loop runs from 0 to i, where i increases each time the for is executed again. This gives the sum 0 + 1 + 2 + ... + n.
Now this sum was calculated by the young Gauss in the following way (for even n, but a similar construction works for odd n):
1 + n = n+1
2 + (n-1) = n+1
3 + (n-2) = n+1
...
n/2 + n/2+1 = n+1
So in total there are n/2 lines with a sum of n+1 each. This gives the result n*(n+1)/2
The while loop is O(logn). Will the inner loop do O(n) work since it will concatenate n characters max (resulting in O(logn + n) in total)? Would using a StringBuilder make it O(1)?
List<String> l = new ArrayList<>();
// some code to add N items to l
//.
//.
//.
while (l.size() > 1) {
int lo = 0, hi = l.size() - 1;
List<String> temp = new ArrayList<>();
while (lo < hi) {
temp.add("(" + l.get(lo) + "," + l.get(hi) + ")");
lo++;
hi--;
}
l = temp;
}
I have come up with multiple solutions the best one outputting a string is a modification of your original post.
This solution would match the algorithmic time of using StringBuilder(l.size * 2 -1). The * 2 - 1 is to allow for the commas.
The following solution reduces the inner loop to O(1), by pre-allocating the array list with the maximum number of elements, each of which can be directly assigned. Your original code required a deep copy of all of the elements of the arraylist, resulting in a time of O(N) to add a new element, beyond initial capacity of the arraylist. The following code initializes the array list to the proper size.
The entire complexity is 1/2 * N + (1/2 * (N - 1) + (1/4 * (N - 2)) + (1/8 * (N -3))...)
My math/analysis may not be correct, however, the lower bound is given that the string concatenation of all elements of an array requires at minimum N operations => O(N)
The upper bound, assuming my math is correct above, is also O(N). Since anything less than half of a value can not added to a value to make the total above the original value.
If my math is wrong the worst case is N log N.
The absolute upper bound is N squared(if you ignore the fact N decreases every iteration).
List<String> l = new ArrayList<>();
// some code to add N items to l
//.
//.
//.
while (l.size() > 1) {
int lo = 0, hi = l.size() - 1;
List<String> temp = new ArrayList<>(l.size/2 + 1);
while (lo < hi) {
temp.add("(" + l.get(lo) + "," + l.get(hi) + ")");
lo++;
hi--;
}
l = temp;
}
In practice, the allocation and de-allocation of memory of 1/2 N ArrayLists for all but small to medium values of N, becomes the limiting factor of the algorithm.
First of all - yes, I have read several posts here on SO, as well as other places about estimating the complexity of an algorithm.
I have read this, this and this, as well as others
I want to try with an algorithm I wrote that finds the largest rectangle, to see if I understood anything at all from what I've read.
public static long getLargestRectangle(long[] arr, int index, long max) {
int count = 1; //1 step * 1
int i = index - 1; //1 step * 1
int j = index + 1; //1 step * 1
if(index == arr.length-1) return max; //1 step * 2
while(i > -1 && arr[index] <= arr[i]) { //1 step * (N+1)
count++; //1 step * N
i--; //1 step * N
}
while(j <= arr.length-1 && arr[index] <= arr[j]) { //1 step * (N+1)
count++; //1 step * N
j++; //1 step * N
}
max = (max < (arr[index] * count) ? (arr[index] * count) : max); //1 step * 7
return getLargestRectangle(arr, index + 1, max); //1 step * 1
}
//total number of steps: 1 + 1 + 1 + (N + 1) + N + N + (N + 1) + N + N + 7
//=> 6N + 12 = O(N) ?
Am I way off here? I'd love some insight.
EDIT
Like this?
T(n) = O(N) + T(n+1)
T(n) = O(N) + O(N) + T(n+2)
T(n) = O(N) + O(N) + O(N) + T(n+3)
T(n) = i * O(N) + (n+i)
T(n) = n * O(N) + (n+n)
= O(N^2)
If this is wrong, I'd really appreciate if you could update your answer and show me.
Am I way off here? I'd love some insight.
I am afraid so :(
return getLargestRectangle(arr, index + 1, max); //1 step * 1
This above is NOT 1 step, it is a recursive invokation of the method. This method "shrinks" the array by 1 element, so this step actually costs T(n-1), where T(.) is the time complexity of the algorithm.
Combining this with what you already have, you get
T(n) = T(n-1) + O(N)
Solving this recurrence formula will give you the algorithm's complexity.
Note: T(n) = T(n-1) + O(N) is a syntatic sugar, it should actually have been T(n) <= T(n-1) + CONST*N for some constant CONST, since you cannot add a set (O(N)) to a scalar (T(n-1)).
Also note: N!=n. n changes over time, N is the initial length of the array. It is so because your algorithm is actually traversing from n (index) to 0, and from n to N.
This does not change the time complexity in terms of big O notation, however.
Ok so I'm new to analyzing algorithms and would really appreciate any helpful tips that can be shared on how to go about this. I am trying to determine how many times count is incremented as a function of n. I have ran it in an ide and for values 1-7 the output is 1,3,6,10,15,21,28. Im just unsure how to write this as a function of n? Thanks.
The loop is as follows:
for(int i = 1 ; i <= n ; i++){
for (int j = 1 ; j <= i ; j++) {
count++;
}
}
The aim of this type of exercise is to teach how to you analyze it on paper instead of running it on a machine. But let's look at the pattern:
The outer loop will run a total of n times
The inner loop will run between 1 to n times depend on what i is at the time. But you know that on average this will run (n+1)/2 times.
Thus count = n(n+1)/2), which is O(n^2)
See arithmetic series
Update: As requested - why the inner loop is (n+1)/2:
The outer loop will increment i between 1 and n. So on each iteration of the outer loop, the inner loop will "loop" one more than than it did previously.
Thus the inner loop will iterate this number of times:
1 + 2 + 3 + ... + n
So we can do something clever and pair up :
n with 1: (n + 1) = n + 1
n-1 with 2: (n - 1) + 2 = n + 1
n-2 with 3: (n - 2) + 3 = n + 1
...
And since we paired these up, we have n/2 such pairs:
So the sum of 1 + 2 + ... + n is ( (n+1) * (n/2) ).
So the average is ( (n+1) * (n/2) ) / n = (n+1)/2
(Visualize it as you are writing 1 + 2 + 3 + ... + n on a long strip of paper, and you fold it in half.)
I would also highly recommend reading this famous story about Karl Friedrich Gauss so you'll always remember how to sum arithmetic series =)
1
1+2 = 3
1+2+3 = 6
1+2+3+4 = 10
1+2+3+4+5 = 15
It is only me who sees the pattern? :-)
Here you go:
count = n * (n + 1) / 2