Tips in optimizing an algorithm - algorithm

I'll try to phrase this question without making it sound like I am seeking for homework answers (this is just a practice problem for algorithms)
you have an array of numbers where every value can occur at most 2x [1 3 5 2 5 1 2 3]
Examine sums from one value to the other instance of itself (5 + 2 + 5) (2 + 5 + 1 + 2)
Find an algorithm that finds the max of such sum.
I came up with a pretty simple algorithm:
iterate through the array (for i=1 to n)
iterate through the remaining array (for j=i+1)
if A[i] == A[j]
s = 0
iterate through the values between those two points (for k=i to j)
s = s + A[k]
maxVal = max(maxVal,s)
What are some steps I can take towards optimizing the algorithm. (or any algorithm for that matter). Obviously this solution is the first that came to mind but I am having trouble envisioning better solutions that would be more efficient.
Edit: For sake of the problem I'll just say all elements are postitive

Calculate array of cumulative sums:
C[0] = A[0]
for i = 1 to n
C[i] = C[i - 1] + A[i]
A[1 3 5 2 5 1 2 3]
C[0 1 4 9 11 16 17 19 22]
Use these values when pair found:
Sum(i to j) = C[j] - C[i - 1]
P.S. Are all elements always positive?

You can get rid of the most inner loop by pre calculate all the sum from index 1 to index i and store it into an array, call sum. So if you want to get the sum between i and j, the result will be sum[j] - sum[i - 1].
for(i = 1 to n)
sum[i] = A[i];
if(i - 1 > 1)
sum[i] += sum[i - 1];
Also notice that there are only two occurrences for each value in the array, we can remember the first position of this value using a map/dictionary or an array pos (if possible) and if we saw it again , we can use it to calculate the sum between the first and this position.
pos[];
for(i = 1 to n)
if(pos[A[i]] ==0)
pos[A[i]] = i;
else
result = max(result,sum[i] - sum[pos[A[i]] - 1])
So in total, the time complexity of this will be O(n)

Related

Find the k-th sum from sums of every pair

I'm given the array with n elements and I need to find k-th sum from sums of every pair n^2 in time complexity O(n*logn), sums are in ascending order.
Example input
In the first line are given number of elements and number of sum to find. In the second line list of number which sums of pair we need to generate.
3 6
1 4 6
The answer is 8 for given list, below is array of every pair of sums, where 8, sum of 4+4 is on the 6-th position.
2 5 5 7 7 8 10 10 12
where first three elements are genereted as follow
1+1 = 2
1+4 = 5
4+1 = 5
Edit:
I came up to this that the main problem is to find place for sum of elements with themselves. I will give example to make it more clear.
For sequence [1, 4, 10], we have
2 5 5 8 11 11 14 14 20
The problem is where to place sum of 4+4, that depends if 1+10 > 4+4, others sums have fixed place because second element + last will be always bigger than last + first (if we have elements in ascending order).
This can be solved in O(n log maxSum).
Pseudocode:
sort(array)
low = array[1] * 2
high = array[n] * 2
while (low <= high): (binarySearch between low and high)
mid = floor((high + low) / 2)
qty = checkHowManySumsAreEqualOrLessThan(mid)
if qty >= k:
high = mid - 1
else:
low = mid + 1
answer = low // low will be the first value of mid where qty was >= k. This means that on low - 1, qty was < k. This means the solution must be low
Sorting is O(n log n). Binary search costs log(array[n] * 2 - array[0] * 2).
checkHowManySumsAreEqualOrLessThan(mid) can be done in O(n) using 2 pointers, let me know if you can't figure out how.
This works because even though we are not doing the binary search over k, it is true that if there were x sums <= mid, if k < x then the kth sum would be lower than mid. Same for when k > x.
Thanks to juvian solving the hard parts, I was able to write this solution, the comments should explain it:
def count_sums_of_at_most(amount, nums1, nums2):
p1 = 0 # Pointer into the first array, start at the beginning
p2 = len(nums2) - 1 # Pointer into the second array, start at the end
# Move p1 up and p2 down, walking through the "diagonal" in O(n)
sum_count = 0
while p1 < len(nums1):
while amount < nums1[p1] + nums2[p2]:
p2 -= 1
if p2 < 0:
# p1 became too large, we are done
break
else:
# Found a valid p2 for the given p1
sum_count += p2 + 1
p1 += 1
continue
break
return sum_count
def find_sum(k, nums1, nums2):
# Sort both arrays, this runs in O(n * log(n))
nums1.sort()
nums2.sort()
# Binary search through all sums, runs in O(n * log(max_sum))
low = nums1[0] + nums2[0]
high = nums1[-1] + nums2[-1]
while low <= high:
mid = (high + low) // 2
sum_count = count_sums_of_at_most(mid, nums1, nums2)
if sum_count >= k:
high = mid - 1
else:
low = mid + 1
return low
arr = [1, 4, 5, 6]
for k in range(1, 1 + len(arr) ** 2):
print('sum', k, 'is', find_sum(k, arr, arr))
This prints:
sum 1 is 2
sum 2 is 5
sum 3 is 5
sum 4 is 6
sum 5 is 6
sum 6 is 7
sum 7 is 7
sum 8 is 8
sum 9 is 9
sum 10 is 9
sum 11 is 10
sum 12 is 10
sum 13 is 10
sum 14 is 11
sum 15 is 11
sum 16 is 12
Edit: this is O(n^2)
The way I understood the problem, the first number on the first row is the number of numbers, and the second number is k.
You can do this problem by using a PriorityQueue, which orders everything for you as you input numbers. Use 2 nested for loops such that they visit each pair once.
for(int k = 0; k < n; k++){
for(int j = 0; j <= k; j++){
If j==k, enter k+j into the PriorityQueue once, if not, enter the sum twice. Then, loop through the PriorityQueue to get he 6th value.
Will edit with full code if you'd like.

Unable to figure out the logic of this usage

I have two arrays, A and B of same size (say, k) and I need to find the nth smallest sum of one value from A and one value from B. For eg, let
A is [1,2,3] and B is [4, 5, 6].There are 9 elements in the set of sums :
1+4=5;
2+4=6;
1 + 5 = 6;
1 + 6 = 7;
2 + 5 = 7;
3 + 4 = 7;
2 + 6 = 8;
3 + 5 = 8;
3 + 6 = 9;
If I need to find the 4th smallest sum, my answer is 7.
A naive solution involving a double-loop is easy to find, but I came across this code for the double-loop:
sort(a+1,a+k+1);
sort(b+1,b+k+1);
for(i=1; i<=k; i++)
{
n=10001/i; //WHY THIS LINE
ind=min(k,n); //WHY THIS LINE
v=a[i];
for(j=1; j<=ind; j++)
{
vc.push_back((v+b[j]));
}
}
I'm unable to understand the use of that 'n' here which I guess is some kind of optimization as without this 'n', the rest of the solution is naive. Also, I'm not sure if it's important, but the constraints of n are
1 <= k <= 10000
Hope somebody can help.
Source: A problem from CodeChef - LOWSUM
All this is saying is:
"If the k-th smallest sum (with k<=10000) is made of a[i]+b[j] for some given i, then j can not be bigger than (k+1)/i, so it can't be bigger than 10001/i either". So you just don't look for a j bigger than (10001)/i to associate with a given i.
This is because you know that the smallest i values in a, associated with the (k+1)/i values in b, will already give you at least k+1 possible sums, all smaller than the ones made with a[i] and b[j>(k+1)/i]
Obviously j should not be bigger than k either, since all the a[i]+b[j<=q] would be smaller. So j must be smaller than min(k, 10001/i).
(I haven't properly checked my cases of equality, the +1 that are needed or not, etc, but the idea is here).

Difference between comparisons and swaps in insertion sort

Given below is a pseudocode of insertion sort algorithm applied on array A (zero-based indexing).
def compare(a, b) :
if a > b return 1
else return -1
for i : 1 to length(A)
j = i - 1
while j > 0
if compare(A[j-1], A[j]) > 0
swap(A[j], A[j-1])
j = j - 1
else break
Given an array A of integers , find difference between number of compare function calls and number of swap function calls by above algorithm when applied on A.
Let us take an example with A = {1, 2, 4, 3}. If we apply insertion sort as above on A we call sequeunce of compare and swap functions in following order
compare (A[0], A[1])
compare (A[1], A[2])
compare (A[2], A[3])
swap (A[2], A[3])
compare (A[1], A[2])
Here compare function is called 4 times, swap function is called 1 time. The answer is 4-1 = 3.
I need to find the difference optimally without running the actual insertion sort algorithm which takes O(n^2).
For every i from 2 to length(A) the number of compare calls will be one more than the number of swap calls besides the situation where the current element is minimal among all elements from 1 to i (in this situation we exit from the loop when j becomes 0). The answer will be length(A)-1 - minimal number occurrences.
minElement = A[1] // one-based array
result = length(A) - 1
for i = 2 to length(A)
if A[i] < minElement
minElement = A[i]
result = result - 1
put a counter in while loop and another in if condition.
The subtraction of these two will give the answer.

Find largest continuous sum such that the minimum of it and it's complement is largest

I'm given a sequence of numbers a_1,a_2,...,a_n. It's sum is S=a_1+a_2+...+a_n and I need to find a subsequence a_i,...,a_j such that min(S-(a_i+...+a_j),a_i+...+a_j) is the largest possible (both sums must be non-empty).
Example:
1,2,3,4,5 the sequence is 3,4, because then min(S-(a_i+...+a_j),a_i+...+a_j)=min(8,7)=7 (and it's the largest possible which can be checked for other subsequences).
I tried to do this the hard way.
I load all values into the array tab[n].
I do this n-1 times tab[i]+=tab[i-j]. So that tab[j] is the sum from the beginning till j.
I check all possible sums a_i+...+a_j=tab[j]-tab[i-1] and substract it from the sum, take the minimum and see if it's larger than before.
It takes O(n^2). This makes me very sad and miserable. Is there a better way?
Seems like this can be done in O(n) time.
Compute the sum S. The ideal subsequence sum is the longest one which gets closest to S/2.
Start with i=j=0 and increase j until sum(a_i..a_j) and sum(a_i..a_{j+1}) are as close as possible to S/2. Note which ever is closer and save the values of i_best,j_best,sum_best.
Increment i and then increase j again until sum(a_i..a_j) and sum(a_i..a_{j+1}) are as close as possible to S/2. Note which ever is closer and replace the values of i_best,j_best,sum_best if they are better. Repeat this step until done.
Note that both i and j are never decremented, so they are changed a total of at most O(n) times. Since all other operations take only constant time, this results in an O(n) runtime for the entire algorithm.
Let's first do some clarifications.
A subsequence of a sequence is actually a subset of the indices of the sequence. Haivng said that, and specifically int he case where you sequence has distinct elements, your problem will reduce to the famous Partition problem, which is known to be NP-complete. If that is the case, you can manage to solve the problem in O(Sn) where "n" is the number of elements and "S" is the total sum. This is not polynomial time as "S" can be arbitrarily large.
So lets consider the case with a contiguous subsequence. You need to observe array elements twice. First run sums them up into some "S". In the second run you carefully adjust array length. Lets assume you know that a[i] + a[i + 1] + ... + a[j] > S / 2. Then you let i = i + 1 to reduce the sum. Conversely, if it was smaller, you would increase j.
This code runs in O(n).
Python code:
from math import fabs
a = [1, 2, 3, 4, 5]
i = 0
j = 0
S = sum(a)
s = 0
while s + a[j] <= S / 2:
s = s + a[j]
j = j + 1
s = s + a[j]
best_case = (i, j)
best_difference = fabs(S / 2 - s)
while True:
if fabs(S / 2 - s) < best_difference:
best_case = (i, j)
best_difference = fabs(S / 2 - s)
if s > S / 2:
s -= a[i]
i += 1
else:
j += 1
if j == len(a):
break
s += a[j]
print best_case
i = best_case[0]
j = best_case[1]
print "Best subarray = ", a[i:j + 1]
print "Best sum = " , sum(a[i:j + 1])

Special case of sparse matrices multiplication

I'm trying to come up with fast algorithm to find result of operation, where
L - is symmetric n x n matrix with real numbers.
A - is sparse n x m matrix, m < n. Each row has one and only one non-zero element, and it's equal to 1. It's also guaranteed that every column has at most two non-zero elements.
I come up with one algorithm, but I feel like there should be something faster than this.
Let's represent every column of A as pair of row numbers with non-zero elements. If a column has only one non-zero element, its row number listed twice. E.g. for the following matrix
Such representation would be
column 0: [0, 2]; column 1: [1, 3]; column 2: [4, 4]
Or we can list it as a single array: A = [0, 2, 1, 3, 4, 4]; Now, can be calculated as:
for (i = 0; i < A.length; i += 2):
if A[i] != A[i + 1]:
# sum of two column vectors, i/2-th column of L'
L'[i/2] = L[A[i]] + L[A[i + 1]]
else:
L'[i/2] = L[A[i]]
To calculate we do it one more time:
for (i = 0; i < A.length; i += 2):
if A[i] != A[i + 1]:
# sum of two row vectors, i/2-th row of L''
L''[i/2] = L'[A[i]] + L'[A[i + 1]]
else:
L''[i/2] = L'[A[i]]
The time complexity of such approach is O(mn + mn), and space complexity (to get final result) is O(nn). I'm wondering if it's possible to improve it to O(mm) in terms of space and/or performance?
The second loop combines at most 2m rows of L', so if m is much smaller than n there will be several rows of L' that are never used.
One way to avoid calculating and storing these unused entries is to change your first loop into a function and only calculate the individual elements of L' as they are needed.
def L'(row,col):
i=col*2
if A[i] != A[i + 1]:
# sum of two column vectors, i/2-th column of L'
return L[row][A[i]] + L[row][A[i + 1]]
else:
return L[row][A[i]]
for (i = 0; i < A.length; i += 2):
if A[i] != A[i + 1]:
for (k=0;k<m;k++):
L''[i/2][k] = L'(A[i],k) + L'(A[i + 1],k)
else:
for (k=0;k<m;k++):
L''[i/2][k] = L'(A[i],k)
This should then have space and complexity O(m*m)
The operation Transpose(A) * L works as follows:
For each column of A we see:
column 1 has `1` in row 1 and 3
column 2 has `1` in row 2 and 4
column 3 has `1` in row 5
The output matrix B = Transpose(A) * L has three rows which are equal to:
Row(B, 1) = Row(A, 1) + Row(A, 3)
Row(B, 2) = Row(A, 2) + Row(A, 4)
Row(B, 3) = Row(A, 5)
If we multiply C = B * A:
Column(C, 1) = Column(B, 1) + Column(B, 3)
Column(C, 2) = Column(B, 2) + Column(B, 4)
Column(C, 3) = Column(B, 5)
If you follow through this in a algorithmic way, you should achieve something very similar to what Peter de Rivaz has suggested.
The time complexity of your algorithm is O(n^2), not O(m*n). The rows and columns of L have length n, and the A array has length 2n.
If a[k] is the column where row k of A has a 1, then you can write:
A[k,i] = δ(a[k],i)
and the product, P = A^T*L*A is:
P[i,j] = Σ(k,l) A^T[i,k]*L[k,l]*A[l,j]
= Σ(k,l) A[k,i]*L[k,l]*A[l,j]
= Σ(k,l) δ(a[k],i)*L[k,l]*δ(a[l],j)
If we turn this around and look at what happens to the elements of L, we see that L[k,l] is added to P[a[k],a[l]], and it's easy to get O(m^2) space complexity using O(n^2) time complexity.
Because a[k] is defined for all k=0..n-1, we know that every element of L must appear somewhere in the product. Because there are O(n^2) distinct elements in L, you can't do better than O(n^2) time complexity.

Resources