Algorithm complexity of traversing a 3D array - arrays

I have an algorithm that traverses a 3d array. For every value in the array I do some compiutations. I'm trying to figure out the time-complexity of the algorithm. In my case, it's not a complete traverse, some value of the array are not considered.
def process_matrix(k: int, V: int):
import numpy as np
sp_matrix = np.zeros((V, V, k))
for e in range(k):
for i in range(V):
# Note that the range of index j decreases while index i is growing
for j in range(i, V):
# Also the index a decreases acording to index i
for a in range(i, V):
if (something):
sp_matrix[i][j][e] = set_some_value()
As you can see I'm not considering the values j < i for every index e.
If we take only the three most outer loops, I think the complexity is:
V * (1+V)/2 * k
k -> the most outer loop
V*(1+V)/2 -> for the sencond and third loop I used Gauss formula for adding consecutive numbers
With some approximation, the complexity for this three loops, I think is O(((V^2)/2)*k).
First I thought the inner loop contributes to the O with another (1+V)/2. With the result (V * (1+V)/2 * k) * (1+V)/2. But then I considered this situation:
k = 1
V = 3
The resulting array is:
j=0 j=1 j=2
i=0 | 3 | 3 | 3 |
i=1 | x | 2 | 2 |
i=2 | x | x | 1 |
(the values in the matrix rapresents how many times the most inner loop.. loops)
The total is: 3+3+3+2+2+1 = 14
I expect the same value using my formula (V * (1+V)/2 * k) * (1+V)/2,
(3*(1+3)/2*1) * (1+3)/2 = 12
But it's not...

Big-O notation is about setting upper limits, ignoring constant factors. In this sense, and considering the 3 outer loops, O(((V^2)/2)*k) = O(k * V^2), and if k is constant, = O(V^2).
However, if you start to count executions of the innermost code, and compare those against your expected number of executions, you are leaving big-O territory, since constant factors can no longer be ignored. Also, counting executions of a single instruction, while useful, is by no means as exact as measuring real-world performance (which, however, will depend on the exact workload and machine/environment you test it on).
Since your 3 inner loops are essentially drawing a tetrahedron, you can use its formula to get an approximation of complexity: O(V^3 / 3). But, if you want to get exact, I have successfully tested out the following JS code:
let K=1, V=6, t=0; // t is for counting totals; reset to 0 for each try
for (let k=0; k<K; k++) // inside repeats K times
for (let i=0; i<V; i++) // inside repeats V*K times
for (let j=i; j<V; j++) // inside repeats (V+1)*(V) / 2 * K times
for (let a=i; a<V; a++) // inside repeats (V+1)*(V+.5)* V / 3 * K times
console.log(k,i,j,a,++t, (V+1)*(V+.5)* V / 3 * K);

Related

Check subset sum for special array equation

I was trying to solve the following problem.
We are given N and A[0]
N <= 5000
A[0] <= 10^6 and even
if i is odd then
A[i] >= 3 * A[i-1]
if i is even
A[i]= 2 * A[i-1] + 3 * A[i-2]
element at odd index must be odd and at even it must be even.
We need to minimize the sum of the array.
and We are given a Q numbers
Q <= 1000
X<= 10^18
We need to determine is it possible to get subset-sum = X from our array.
What I have tried,
Creating a minimum sum array is easy. Just follow the equations and constraints.
The approach that I know for subset-sum is dynamic programming which has time complexity sum*sizeof(Array) but since sum can be as large as 10^18 that approach won't work.
Is there any equation relation that I am missing?
We can make it with a bit of math:
sorry for latex I am not sure it is possible on stack?
let X_n be the sequence (same as being defined by your A)
I assume X_0 is positive.
Thus sequence is strictly increasing and minimization occurs when X_{2n+1} = 3X_{2n}
We can compute the general term of X_{2n} and X_{2n+1}
v_0 =
X0
X1
v_1 =
X1
X2
the relation between v_0 and v_1 is
M_a =
0 1
3 2
the relation between v_1 and v_2 is
M_b =
0 1
0 3
hence the relation between v_2 and v_0 is
M = M_bM_a =
3 2
9 6
we deduce
v_{2n} =
X_{2n}
X_{2n+1}
v_{2n} = M^n v_0
Follow the classical diagonalization... and we (unless mistaken) get
X_{2n} = 9^n/3 X_0 + 2*9^{n-1}X_1
X_{2n+1} = 9^n X_0 + 2*9^{n-1}/3X_1
recall that X_1 = 3X_0 thus
X_{2n} = 9^n X_0
X_{2n+1} = 3.9^n X_0
Now if we represent the sum we want to check in base 9 we get
9^{n+1} 9^n
___ ________ ___ ___
X^{2n+2} X^2n
In the X^{2n} places we can only put a 1 or a 0 (that means we take the 2n-th elem from the A)
we may also put a 3 in the place of the X^{2n} place which means we selected the 2n+1th elem from the array
so we just have to decompose number in base 9, and check whether all its digits or either 0,1 or 3 (and also if its leading digit is not out of bound of our array....)

Sum of all the elements of 3 different arrays

I want and optimized algorithm to find sum of each and every element of array.
for example let 3 array:
a = [1,2,3,4];
b = [5,6];
c = [8,9];
then final sum will be equal to:
sum(1,5,8)+sum(1,5,9)+sum(1,6,8)+sum(1,6,9)+sum(2,5,8)...+sum(4,6,9)
I tried doing but the algorithm I used had time complexity O(n^3), so I want anything less than this complexity.
Here is my algorithm:
sum = 0
for(i=0;i<a.size();i++)
for(j=0;j<b.size();j++)
for(k=0;k<c.size();k++)
sum = sum+a[i]+b[j]+c[k];
For this example, a, b and c have 4, 2 and 2 elements respectively. If you want to add them in every combination, there will be 4 * 2 * 2 = 16 terms to add. In those terms, each element of a will appear 4 times, because it will be added to 2 * 2 = 4 combinations of elements of b and c. Similarly, each element of b (or c) will appear 8 times, because it will be added to each 4 * 2 = 8 combinations of each elements of a and c (or b).
So, in the final sum, each element of a will appear 4 times and each element of b and c will appear 8 times. Once you figure that out, you can do fewer number of multiplications and additions to get the result.(Just sum of elements of individual arrays and then multiply these sums by 4 , 8 and 8 respectively).
Each element of a will appear in sums with each element of b and each element of c.
This means that every element in a will appear in a number of sums equal to b.length * c.length.
This is also easy to see from the brute force pseudo-code: (modified for readability)
for i = 0 to a.length
for j = 0 to b.length // happens once for each i
for k = 0 to c.length // happens b.length times for each i
sum += a[i] + ... // happens b.length * c.length times for each i
Generalising this, we come up with the following algorithm:
Sum all elements a, multiply the result by b.length * c.length.
Sum all elements b, multiply the result by a.length * b.length.
Sum all elements c, multiply the result by a.length * b.length.
Return the above three values added together.
This is an O(n) algorithm, where n is the total number of elements or average number of elements per array.

Difference of elements of 2 sorted arrays within given interval

Let us assume that we have 2 sorted arrays A and B of integers and a given interval [L,M] . If x is an element of A and y an element of B ,our task is to find all pairs of (x,y) that hold the following property: L<=y-x<=M.
Which is the most suitable algorithm for that purpose?
So far ,I have considered the following solution:
Brute force. Check the difference of all possible pairs of elements with a double loop .Complexity O(n^2).
A slightly different version of the previous solution is to make use of the fact that arrays are sorted by not checking the elements of A ,once difference gets out of interval .Complexity would still be O(n^2) but hopefully our program would run faster at an average case.
However ,I believe that O(n^2) is not optimal .Is there an algorithm with better complexity?
Here is a solution.
Have a pointer at the beginning of each array say i for array A and j for array B.
Calculate the difference between B[j] and A[i].
If it is less than L, increment the pointer in array B[], i.e increment j by 1
If it is more than M, increment i, i.e pointer of A.
If the difference is in between, then do the following:
search for the position of an element whose value is B[j]-A[i]-L or the nearest
element whose value is lesser than (B[j]-A[i])-L in array A. This
takes O(logN) time. Say the position is p. Increment the count of
(x,y) pairs by p-i+1
Increment only pointer j
My solution only counts the number of possible (x,y) pairs in O(NlogN) time
For A=[1,2,3] and B=[10,12,15] and L=12 and M=14, answer is 3.
Hope this helps. I leave it up to you, to implement the solution
Edit: Enumerating all the possible (x,y) pairs would take O(N^2) worst case time. We will be able to return the count of such pairs (x,y) in O(NlogN) time. Sorry for not clarifying it earlier.
Edit2: I am attaching a sample implementation of my proposed method below:
def binsearch(a, x):
low = 0
high = len(a)-1
while(low<=high):
mid = (low+high)/2
if a[mid] == x:
return mid
elif a[mid]<x:
k = mid
low = low + mid
else:
high = high - mid
return k
a = [1, 2, 3]
b = [10, 12, 15, 16]
i = 0
j = 0
lenA = len(a)
lenB = len(b)
L = 12
M = 14
count = 0
result = []
while i<lenA and j<lenB:
if b[j] - a[i] < L:
j = j + 1
elif b[j] - a[i] > M:
i = i + 1
else:
p = binsearch(a,b[j]-L)
count = count + p - i + 1
j = j + 1
print "number of (x,y) pairs: ", count
Because it's possible for every combination to be in the specified range, the worst-case is O([A][B]), which is basically O(n^2)
However, if you want the best simple algorithm, this is what I've come up with. It starts similarly to user-targaryen's algorithm, but handles overlaps in a simplistic fashion
Create three variables: x,y,Z and s (set all to 0)
Create a boolean 'success' and set to false
Calculate Z = B[y] - A[x]
if Z < L
increment y
if Z >= L and <= M
if success is false
set s = y
set success = true
increment y
store x,y
if Z > M
set y = s //this may seem inefficient with my current example
//but you'll see the necessity if you have a sorted list with duplicate values)
//you can just change the A from my example to {1,1,2,2,3} to see what I mean
set success = false
an example:
A = {1,2,3,4,5}
B = {3,4,5,6,7}
L = 2, M = 3
In this example, the first pair is x,y. The second number is s. The third pair is the values A[x] and B[y]. The fourth number is Z, the difference between A[x] and B[y]. The final value is X for not a match and O for a match
0,0 - 0 - 1,3 = 2 O
increment y
0,1 - 0 - 1,4 = 3 O
increment y
0,2 - 0 - 1,5 = 4 X
//this is the tricky part. Look closely at the changes this makes
set y to s
increment x
1,0 - 0 - 2,3 = 1 X
increment y
1,1 - 0 - 2,4 = 2 O
set s = y, set success = true
increment y
1,2 - 1 - 2,5 = 3 O
increment y
1,3 - 1 - 2,6 = 4 X
set y to s
increment x
2,1 - 1 - 3,4 = 1 X
increment y
2,2 - 1 - 3,5 = 2 O
set s = y, set success = true
increment y
2,3 - 2 - 3,6 = 3 O
... and so on

Matlab Convert Vector to Binary Matrix [duplicate]

This question already has answers here:
Create a zero-filled 2D array with ones at positions indexed by a vector
(4 answers)
Closed 6 years ago.
I have a vector v of size (m,1) whose elements are integers picked from 1:n. I want to create a matrix M of size (m,n) whose elements M(i,j) are 1 if v(i) = j, and are 0 otherwise. I do not want to use loops, and would like to implement this as a simple vector-matrix manipulation only.
So I thought first, to create a matrix with repeated elements
M = v * ones(1,n) % this is a (m,n) matrix of repeated v
For example v=[1,1,3,2]'
m = 4 and n = 3
M =
1 1 1
1 1 1
3 3 3
2 2 2
then I need to create a comparison vector c of size (1,n)
c = 1:n
1 2 3
Then I need to perform a series of logical comparisons
M(1,:)==c % this results in [1,0,0]
.
M(4,:)==c % this results in [0,1,0]
However, I thought it should be possible to perform the last steps of going through each single row in compact matrix notation, but I'm stumped and not knowledgeable enough about indexing.
The end result should be
M =
1 0 0
1 0 0
0 0 1
0 1 0
A very simple call to bsxfun will do the trick:
>> n = 3;
>> v = [1,1,3,2].';
>> M = bsxfun(#eq, v, 1:n)
M =
1 0 0
1 0 0
0 0 1
0 1 0
How the code works is actually quite simple. bsxfun is what is known as the Binary Singleton EXpansion function. What this does is that you provide two arrays / matrices of any size, as long as they are broadcastable. This means that they need to be able to expand in size so that both of them equal in size. In this case, v is your vector of interest and is the first parameter - note that it's transposed. The second parameter is a vector from 1 up to n. What will happen now is the column vector v gets replicated / expands for as many values as there are n and the second vector gets replicated for as many rows as there are in v. We then do an eq / equals operator between these two arrays. This expanded matrix in effect has all 1s in the first column, all 2s in the second column, up until n. By doing an eq between these two matrices, you are in effect determining which values in v are equal to the respective column index.
Here is a detailed time test and breakdown of each function. I placed each implementation into a separate function and I also let n=max(v) so that Luis's first code will work. I used timeit to time each function:
function timing_binary
n = 10000;
v = randi(1000,n,1);
m = numel(v);
function luis_func()
M1 = full(sparse(1:m,v,1));
end
function luis_func2()
%m = numel(v);
%n = 3; %// or compute n automatically as n = max(v);
M2 = zeros(m, n);
M2((1:m).' + (v-1)*m) = 1;
end
function ray_func()
M3 = bsxfun(#eq, v, 1:n);
end
function op_func()
M4= ones(1,m)'*[1:n] == v * ones(1,n);
end
t1 = timeit(#luis_func);
t2 = timeit(#luis_func2);
t3 = timeit(#ray_func);
t4 = timeit(#op_func);
fprintf('Luis Mendo - Sparse: %f\n', t1);
fprintf('Luis Mendo - Indexing: %f\n', t2);
fprintf('rayryeng - bsxfun: %f\n', t3);
fprintf('OP: %f\n', t4);
end
This test assumes n = 10000 and the vector v is a 10000 x 1 vector of randomly distributed integers from 1 up to 1000. BTW, I had to modify Luis's second function so that the indexing will work as the addition requires vectors of compatible dimensions.
Running this code, we get:
>> timing_binary
Luis Mendo - Sparse: 0.015086
Luis Mendo - Indexing: 0.327993
rayryeng - bsxfun: 0.040672
OP: 0.841827
Luis Mendo's sparse code wins (as I expected), followed by bsxfun, followed by indexing and followed by your proposed approach using matrix operations. The timings are in seconds.
Assuming n equals max(v), you can use sparse:
v = [1,1,3,2];
M = full(sparse(1:numel(v),v,1));
What sparse does is build a sparse matrix using the first argument as row indices, the second as column indices, and the third as matrix values. This is then converted into a full matrix with full.
Another approach is to define the matrix containing initially zeros and then use linear indexing to fill in the ones:
v = [1,1,3,2];
m = numel(v);
n = 3; %// or compute n automatically as n = max(v);
M = zeros(m, n);
M((1:m) + (v-1)*m) = 1;
I think I've also found a way to do it, and it would be nice if somebody could tell me which of the methods shown is faster for very large vectors and matrices. The additional method I thought of is the following
M= ones(1,m)'*[1:n] == v * ones(1,n)

Optimal way to check for number of triplets where a[k]<a[i]<a[j] for all i<j<k in an array

I'm solving a problem where I have to find number of triplets of Ai, Aj, and Ak such that Ak < Ai < Aj and i < j < k in an array .
The solution to this by brute force is trivial but has complexity O(N^3). What is the optimal way to solve this?
This is an O(n^2) approach that fixes i and iterates over the rest of the array in reverse order, keeping track of the number of elements below a[i].
def count_triplets(a):
"""Count the number of triplets a[k]<a[i]<a[j] for i<j<k"""
t = 0
for i,ai in enumerate(a):
kcount = 0 # The number of elements smaller than a[i]
for x in a[:i:-1]:
if x<ai:
kcount += 1
elif x>ai:
t += kcount
return t
A=[1,6,3,4,7,4]
print count_triplets(A)
Worked example
For the given array array the interesting case is when i is equal to 1 and ai is equal to 6.
The program now works backwards over the remaining entries in the array as follows:
x = 4
x < ai so kcount increased to 1
x = 7
x > ai so t increased by kcount, i.e. t increased to 1
x = 4
x < ai so kcount increased to 2
x = 3
x < ai so kcount increased to 3
All other values of i don't end up increasing t at all, so the final value for t is 1.
TEST CODE
The Hackerrank site wants the code to support a number of inputs. The code below passes all tests.
N=input()
for n in range(N):
A=map(int,raw_input().split())
print count_triplets(A)

Resources