I have a question in regards to the time complexity of the below code. I am guessing that the time complexity is, O(n^3) but my friend told me that the time complexity should be O(n^2). However, I am still not convinced with the answer. My stand is that: the first and second for loop would cost O(1/2 n^2) and inner loop would need another some O(n) complexity. Therefore, it is about O(n^3).
for (int i = 1; i <= len; i++) {
for (int j = i + 1; j <= len; j++) {
int mid = (i + j) / 2;
for (int k = i; k <= j; k++) {
dist[i][j] += Math.abs(A[k - 1] - A[mid - 1]);
}
}
}
So you need to find the time complexity of something like this:
for (int i = 1; i <= N; i++) {
for (int j = i + 1; j <= N; j++) {
for (int k = i; k <= j; k++) {
// some O(1) operation
}
}
}
Each of the loops run in O(N), so the complexity is O(N^3). You can also write a simple test program in your language (I wrote in python):
def check(N):
s = 0
for i in xrange(1, N + 1):
for j in xrange(i + 1, N + 1):
for k in xrange(i, j + 1):
s += 1
return s
print [check(i) for i in xrange(1, 10)] // [0, 2, 7, 16, 30, 50, 77, 112, 156]
And checked for a closed form for this sequence. It is ,
which is clearly O(n^3)
Related
I'm having trouble figuring out the time equation for a couple small snippets of code.
int sum = 0;
for (int k = n; k > 0; k /= 2)
for (int i = 0; i < k; i++)
sum++;
int sum = 0;
for (int i = 1; i < n; i *=2)
for (int j = 0; j < i; j++)
sum++;
int sum = 0;
for (int i = 1; i < n; i *=2)
for (int j = 0; j < n; j++)
sum++;
They're all very similar as you can see. I'm not looking for an exact answer or anything, I'm just not really sure where to start with the inner loops. It seems like they would all run n times, but they can't all be the same, right? I'm pretty sure all of the outer loops would be log(n) and that the sum++ would just be a constant (1), but I'm not really sure how all of the inner loops are different and how that would change the equation.
The third code snippet is the easiest to analyze. For each outer loop iteration the inner loop will make 'n' iterations. Since the number of outer loop iterations is O(log(n)) the total number of iterations (and the complexity of the third snippet) is O(n*log(n)).
The first two code snippets have the same complexity, just the outer loop iterates in the descending order in the first snippet, and in the ascending order in the second one. So you iterate over all powers of two which are smaller than 'n', and then repeat the inner loop the corresponding number of times. The total number of iterations is
1 + 2 + 4 + ... + 2^k
where k=log2(n). Sum of powers of 2 is 2^(k+1)=2*2^k=2*n. So, the complexity in both cases is O(n).
int sum = 0;
for (int k = n; k > 0; k /= 2)
for (int i = 0; i < k; i++)
sum++;
n + n/2 + n/4 + n/8 + ... + 1 ≈ 2n = Θ(n)
int sum = 0;
for (int i = 1; i < n; i *=2)
for (int j = 0; j < i; j++)
sum++;
1 + ... + n/8 + n/4 + n/2 + n ≈ 2n = Θ(n)
(Well, not exactly ending with n, n/2 etc, but within a factor of 2 of those, so doesn't matter for the complexity class.)
int sum = 0;
for (int i = 1; i < n; i *=2)
for (int j = 0; j < n; j++)
sum++;
n + n + ... + n ≈ log(n) × n = Θ(n log n)
I have got this algorithm
int count = 0;
for(int i = n; i >= 1; i = i/2) {
for ( int j = 1; j <= i; j++) {
count++;
}
}
Am I right in saying that the Big-O for this would be n/2?
TL;DR The time complexity is O(n).
More details
Am I right in saying that the BigO for this would be n/2?
No that is accurate, in big-O notation you drop the constant part so (1/2)n simplifies to O(n).
I am not sure where that n/2 comes from because only the outer loop
for(int i = n; i >= 1; i = i/2) {
...
}
is log2n not n/2.
And with both loops together:
int count = 0;
for(int i = n; i >= 1; i = i/2) {
for ( int j = 1; j <= i; j++) {
count++;
}
}
the count would vary between N and 2N.
Let us go through the calculations:
int count = 0;
for(int i = n; i >= 1; i = i/2) {
for ( int j = 1; j <= i; j++) {
count++;
}
}
The inner loop will execute N iterations then N/2, then N/4 ... until N/N.
In another words we have (N + N/2 + N/4 ... N/N) which can be simplified to N * (1/2 + 1/4 + .. + 1/2^L)), with L = Log2 N.
This (1/2 + 1/4 + .. + ) series is well-known for being 1. Therefore, we can simplified N * (1/2 + 1/4 + .. + 1/2^L)) to O(N).
You are correct! This is basically a geometric progression with a quotient of 2 and the number of elements is lg(n) as we divide i by 2 each iteration of the outer loop.
1, 2, 4, ..., n
Using a known formula to calculate the sum, we get:
The reason we have lg (n) elements, is because we divide i each iteration by 2, thus we need to solve for the number of iterations k:
public static int desc(int number) {
// Time complexity = 2n+n2
// space complexity = ?
int i, j, temp;
int array[] = new int[Integer.toString(number).length()];
for (i = 0; i < array.length; i++)
array[i] = Integer.toString(number).charAt(i) - '0';
for (i = 0; i < array.length - 1; i++) {
for (j = 0; j < array.length - i - 1; j++) {
if (array[j] < array[j + 1]) {
temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
StringBuilder strNum = new StringBuilder();
for (int k : array) {
strNum.append(k);
}
int finalInt = Integer.parseInt(strNum.toString());
System.out.println(finalInt);
return finalInt;
}
This is my code as per my understanding I am able to calculate time complexity please suggest if the time complexity is correct or not and also help me to calculate the space complexity of this program I am bit confuse on how to calculate of space complexity .
First let's optimize your code from:
int array[] = new int[Integer.toString(number).length()];
for (i = 0; i < array.length; i++)
array[i] = Integer.toString(number).charAt(i) - '0';
to:
String to_number = Integer.toString(number);
int array[] = new int[to_number.length()];
for (i = 0; i < array.length; i++)
array[i] = to_number.charAt(i) - '0';
No need to call Integer.toString(number) multiple times.
Being N the number of digits in the parameter number (i.e., Integer.toString(number).length()) the time complexity can be calculated as follows:
String to_number = Integer.toString(number);
has a time complexity of N and the same for:
for (i = 0; i < array.length; i++)
array[i] = to_number.charAt(i) - '0';
The follow up double loop:
for (i = 0; i < array.length - 1; i++) {
for (j = 0; j < array.length - i - 1; j++) {
if (array[j] < array[j + 1]) {
temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
is a well-know for being N(N-1)/2, which can be simplified to a time complexity of O(N^2). The next loop
for (int k : array) {
strNum.append(k);
}
has time complexity of N. Finally,
int finalInt = Integer.parseInt(strNum.toString());
is also N. So the time complexity is N + N + N^2 + N + N, which simplifies to O(N^2) time complexity, also known as quadratic time complexity.
Now let us look at the space complexity:
int array[] = new int[Integer.toString(number).length()];
in this case N, and
for (int k : array) {
strNum.append(k);
}
also N, so the space complexity is 2N, which simplifies to O(N).
Two players take turns choosing one of the outer coins. At the end we calculate the difference between the score two players get, given that they play optimally. for example the list{4,3,2,1},
the optimal sequence would be 4, 3, 2, 1. then i will get 4+2 = 6 scores and the opponent 4 scores.
Now i have developed an algorithm as follow:
My Job is to print the scores out, and also the optimal sequence in index. so in the array {4,3,2,1} the optimal sequence would be 0,1,2,3.
The maximum Runtime and Memory should not exceed n^2.
Therefore I implemented the above algorithm with bottom up approach,which means in an i*j table, according to my algorithm, subproblems are solved one by one until the only main problem, which locates at the top right corner(where i =0 and j = n-1). It works calculating the scores, but i have no idea how to trace the optimal sequence during runtime, since when I calculate subproblems by subproblems, only the score will be save and used in the next problem, while the sequence, which led to the final result, is hard to trace back.
I tried to create Pairs or multidimensional ArrayList to record the sequences and their corresponding memo[i][j]...... Well, they worked, but the memory needed would then be greater than n^2 and this is not allowed in my task.
So, does anymore have a better idea that does not require that much memory space?
Any help would be appreciated, cheers!
My code:
public int maxGain(int[] values) {
int n = values.length;
int [][] memo = new int[n][n];
for (int i = 0; i < n; i++)
memo[i][i] = values[i];
for (int i = 0, j = 1; j < n; i++, j++)
memo[i][j] = Math.max(values[i], values[j]);
for (int k = 2; k < n; k++) {
for (int i = 0, j = k; j < n; i++, j++) {
int a = values[i] + Math.min(memo[i + 2][j], memo[i + 1][j - 1]);
int b = values[j] + Math.min(memo[i + 1][j - 1], memo[i][j - 2]);
memo[i][j] = Math.max(a, b);
}
}
return memo[0][n - 1];
}
I guess your question is similar to Predict the Winner of LeetCode (486) with some minor changes that you would want to make:
Java
class Solution {
public boolean maxGain(int[] nums) {
int length = nums.length;
int[][] dp = new int[length][length];
for (int i = 0; i < length; i++)
dp[i][i] = nums[i];
for (int l = 1; l < length; l++)
for (int i = 0; i < length - l; i++) {
int j = i + l;
dp[i][j] = Math.max(nums[i] - dp[i + 1][j], nums[j] - dp[i][j - 1]);
}
return dp[0][length - 1] > -1;
}
}
Python
class Solution:
def max_gain(self, nums):
length = len(nums)
memo = [[-1 for _ in range(length)] for _ in range(length)]
#functools.lru_cache(None)
def f():
def helper(nums, i, j):
if i > j:
return 0
if i == j:
return nums[i]
if memo[i][j] != -1:
return memo[i][j]
cur = max(nums[i] + min(helper(nums, i + 2, j), helper(nums, i + 1, j - 1)),
nums[j] + min(helper(nums, i, j - 2), helper(nums, i + 1, j - 1)))
memo[i][j] = cur
return cur
score = helper(nums, 0, length - 1)
total = sum(nums)
return 2 * score >= total
return f()
O(N) Memory
The space complexity might be an order of N for the second solution provided in this link:
class Solution {
public boolean maxGain(int[] nums) {
if (nums == null)
return true;
int length = nums.length;
int[] dp = new int[length];
for (int i = length - 1; i >= 0; i--) {
for (int j = i; j < length; j++) {
if (i == j)
dp[i] = nums[i];
else
dp[j] = Math.max(nums[i] - dp[j], nums[j] - dp[j - 1]);
}
}
return dp[length - 1] > -1;
}
}
Reference
Most optimal solutions are here in the discussion board
How to find time complexities for the following loops.
1)
int I, j, k, n, mini, tmp;
for(i = 0; i< k; i++){
mini = i;
for(j =i +1; j < n; j++)
if (a[j] < a[mini])
mini = j;
tmp = a[i];
a[i] = a[mini];
a[mini] = tmp;
}
return a[k-1];
}
2)
void SelectionSort(int A[], int n) {
int i = 0;
while (i < n - 1) {
int j = i + 1;
while (j < n) {
if (A[j] < A[i])
swap(A[j], A[i])
j++;
}
i++;
}
}
Both are O(n^2) 1,2
In both, the outer loop runs from 0 to n (exclusive), and for each iteration - the inner loop runs from i+1 to n (exclusive).
If we sum the running time of the inner loops we get:
n- (0+1) + n- (1+1) + .... + n-(n-1 + 1) =
= n-1 + n-2 + .... + 0 =
0 + 1 + ... + n-1 = (*)
n(n-1)/2
which is in O(n^2)
The equation (*) comes from sum of aritmetic progression.
As a side note - both are sorting algorithms, the first is min sort and the second is as the function name says, selection sort.
(1) technically the first is O(k^2), but I assume it means the same here.
(2) Assuming the return a[k-1]; should be AFTER closing the scope of the outer loop, and its placement is a mistake. If it is not a mistake - the outer loop runs only once, and complexity is O(n).