Please read this before you rush to mark this as duplicate! - This is not about the actual modification, this is about checking if a particular inversion has been counted or not.
So there is this question in the popular CLRS' Introduction to Algorithms book that asks you to modify merge sort to calculate the number of inversions in an array. The authors also generously provide solution to this problem 2-4 here, whose screenshots I have attached below:
My question: In the second screenshot, the author has used a boolean counted = FALSE to check if the inversions corresponding to a particular R[j] for some value of j have been already counted or not. I am quite confused because of this as I think it is redundant.
We count inversions here:
if counted == FALSE and R[j] < L[i]
inversions = inversions + n1 - i + 1
counted = TRUE
so counted becomes TRUE only when R[j] < L[i], which is ALSO the else part below:
...
else
A[k] = R[j]
j = j + 1
counted = FALSE
So whenever we have R[j] < L[i] we copy R[j] into A[k] and increase the value of j by 1, which makes sure that we never encounter the previous R[j] again in the loop. In my opinion this makes the counted boolean redundant.
Or is there something more to it? Is there any particular example that breaks if we remove the counted boolean:
// my suggestion
...
for k = p to r
if R[j] < L[i]
inversions = inversions + n1 - i + 1
if L[i] <= R[j]
A[k] = L[i]
i = i + 1
else A[k] = R[j]
j = j + 1
I think you're right. The solution author is an experienced algorithmist, but with an entire book of exercises to solve, it's sort of inevitable that some of the answers won't be perfect.
Why don't you write to the authors?
Related
I am currently reading chapter 2 of the TCRC Introduction to Algorithms 3rd edition textbook and I am reading the author's interpretation of the loop invariant of this algorithm. I understand the author's logic for both the initialization and the maintenance. However, the termination is what I am kind of bogged up on. The author claims that at termination, j = n + 1. However, in the pseudocode of the algorithm, j loops from 2 to n. So shouldn't j = n - 1?
EDIT: The book's pseudo-code for insertion sort is:
for j = 2 to A.length
key = A[j]
// Insert A[j] into sorted sequence A[1...j - 1]
i = j - 1
while i > 0 and A[i] > key
A[i + 1] = A[i]
i = i - 1
A[i + 1] = key
EDIT: After reading it carefully, I have finally understood why j = n + 1 during termination. It's because the for loop goes from 2 to n (inclusively), so after j exceeds n, the loop terminates, hence why j = n + 1 at termination. I appreciate the help.
Disclaimer: this can be totally incorrect... It is just a brain spit.
Side note: since j is incremented during this loop, the starting point is irrelevant for the end condition.
for j = 2 to A.length //A.length = n in your question
There is a bit of ambiguity in this pseudo code.
First of all, we assume j is defined outside this for loop and will have an end value when the loop is terminated. see #Dukeling's comment
Second, your code is targeting an array, using the j as indexer: A[j]
The ambiguity exist with the word to in for j = 2 to A.length, is it including or excluding A.length? and there is this indexer A[j]
In common cases, for the indexer in A[j], the valid range for j is [0...A.length -1]
Some languages uses another range, namely: [1...A.length] I think this is intended by the author because A[0] is not being hit at all.
If that's the case.... and the for condition increments j before it breaks the loop (to test the condition and see that it is false), then... you'll get j = A.length + 1.
As a side note:
In common C like languages, arrays have a valid range from [0...A.length -1].
And in this C example, c has the value of A.length after termination:
int c = 0;
for (c = 3; c < A.length; c++)
{
}
//c = A.length after the loop is completed.
This question already has answers here:
Find a pair of elements from an array whose sum equals a given number
(33 answers)
Closed 5 years ago.
I have an O(n^2) solution to the classic two-sum problem. Where A[1...n] sorted array of positive integers. t is some positive integer.
Need to show that A contains two distinct elements a and b s.t. a+ b = t
Here is my solution so far:
t = a number;
for (i=0; i<A.length; i++)
for each A[j]
if A[i] + A[j] == t
return true
return false
How do I make this a linear solution? O(n) scratching my head trying to figure it out.
Here's an approach I have in mind so far. i will start at the beginning of A, j will start at the end of A. i will increment, j will decrement. So I'll have two counter variables in the for loop, i & j.
There are couple of ways to improve upon that.
You could extend your algorithm, but instead of doing a simple search for every term, you could do a binary search
t = a number
for (i = 0; i < A.length; i++)
j = binarySearch(A, t - A[i], i, A.length - 1)
if (j != null)
return true
return false
Binary search is done by O(log N) steps, since you perform a binary search per every element in the array, the complexity of the whole algorithm would be O(N*log N)
This already is a tremendous improvement upon O(N^2), but you can do better.
Let's take the sum 11 and the array 1, 3, 4, 8, 9 for example.
You can already see that (3,8) satisfy the sum. To find that, imagine having two pointers, once pointing at the beginning of the array (1), we'll call it H and denote it with bold and another one pointing at the end of the array (9), we'll call it T and denote it with emphasis.
1 3 4 8 9
Right now the sum of the two pointers is 1 + 9 = 10.
10 is less than the desired sum (11), there is no way to reach the desired sum by moving the T pointer, so we'll move the H pointer right:
1 3 4 8 9
3 + 9 = 12 which is greater than the desired sum, there is no way to reach the desired sum by moving the H pointer, moving it right will further increase the sum, moving it left bring us to the initital state, so we'll move the T pointer left:
1 3 4 8 9
3 + 8 = 11 <-- this is the desired sum, we're done.
So the rules of the algorithm consist of moving the H pointer left or moving the T pointer right, we're finished when the sum of the two pointer is equal to the desired sum, or H and T crossed (T became less than H).
t = a number
H = 0
T = A.length - 1
S = -1
while H < T && S != t
S = A[H] + A[T]
if S < t
H++
else if S > t
T--
return S == t
It's easy to see that this algorithm runs at O(N) because we traverse each element at most once.
You make 2 new variables that contain index 0 and index n-1, let's call them i and j respectively.
Then, you check the sum of A[i] and A[j] and if the sum is smaller than t, then increment i (the lower index), and if it is bigger then decrement j (the higher index). continue until you either find i and j such that A[i] + A[j] = t so you return true, or j <= i, and you return false.
int i = 0, j = n-1;
while(i < j) {
if(A[i] + A[j] == t)
return true;
if(A[i] + A[j] < t)
i++;
else
j--;
return false;
Given that A[i] is relatively small (maybe less than 10^6), you can create an array B of size 10^6 with each value equal to 0. Then apply the following algorithm:
for i in 1...N:
B[A[i]] += 1
for i in 1...N:
if t - A[i] > 0:
if B[t-A[i]] > 0:
return True
Edit: well, now that we know that the array is sorted, it may be wiser to find another algorithm. I'll leave the answer here since it still applies to a certain class of related problems.
The question is like this--
For every string given as input, you need to tell the number of subsequences of it that are palindromes (need not necessarily be distinct). Note that the empty string is not a palindrome.
For example, the palindromic subsequences of "aab" are:
"a", "a", "b", "aa", and the method returns 4.
I had the Dynamic Programming solution to finding Longest Palindromic Subsequence in mind and therefore tried to take ideas from it. Couldn't really get the solution. May be dynamic programming is not even required. Suggestions please.
And there is one more catch. When the condition "need not necessarily be distinct" is removed, can we still count without actually generating all the palindromic subsequences?
[EDIT 19/10/2015: An anonymous reviewer pointed out a problem with the formula, which prompted me to notice another, even bigger mistake... Now fixed.]
I now see how to drop the solution time down to O(n^2). I'll leave my other answer up in case it's interesting as a stepping-stone to this one. Note: This is (also) only a solution to the first part of the problem; I see no way to efficiently count only distinct palindromic subsequences (PS).
Instead of counting the number of PS that begin and end at exactly the positions i and j, let's count how many begin at or after i and end at or before j. Call this g(i, j).
We can try to write g(i, j) = g(i, j-1) + g(i+1, j) + (x[i] == x[j])*g(i+1, j-1) for the case when j > i. But this doesn't quite work, because the first two terms will double-count any PS that begin after i and end before j.
The key insight is to notice that we can easily calculate the number of PS that begin or end at some exact position by subtracting off other values of g(), and perhaps adding yet more values of g() back on to compensate for double-counting. For example, the number of PS that begin at exactly i and end at exactly j is g(i, j) - g(i+1, j) - g(i, j-1) + g(i+1, j-1): the last term corrects for the fact that both the second and third terms count all g(i+1, j-1) PS that begin after i and end before j.
Every PS that begins at or after i and ends at or before j is in exactly 1 of 4 categories:
It begins after i, and ends before j.
It begins at i, and ends before j.
It begins after i, and ends at j.
It begins at i, and ends at j.
g(i+1, j) counts all PS in category 1 or 3, and g(i, j-1) counts all PS in category 1 or 2, so their sum g(i+1, j) + g(i, j-1) counts all PS in category 2 or 3 once each, and all PS in category 1 twice. Since g(i+1, j-1) counts all PS in category 1 only, subtracting this off to get g(i+1, j) + g(i, j-1) - g(i+1, j-1) gives the total number of PS in category 1, 2 and 3. The remaining PS are those in category 4. If x[i] != x[j] then there are no PS in this category; otherwise, there are exactly as many as there are PS that begin at or after i+1 and end at or before j-1, namely g(i+1, j-1), plus one extra for the 2-character sequence x[i]x[j]. [EDIT: Thanks to commenter Tuxdude for 2 fixes here!]
With this in hand, we can express g() in a way that changes the quadratic case from f() to constant time:
g(i, i) = 1 (i.e. when j = i)
g(i, i+1) = 2 + (x[i] == x[i+1]) (i.e. 3 iff adjacent chars are identical, otherwise 2)
g(i, j) = 0 when j < i (this new boundary case is needed)
g(i, j) = g(i+1, j) + g(i, j-1) - g(i+1, j-1) + (x[i] == x[j])*(g(i+1, j-1)+1) when j >= i+2
The final answer is now simply g(1, n).
Here's a horrible O(n^4) solution:
Every palindromic subsequence begins at some position i and ends at some position j >= i such that x[i] = x[j], and its "interior" (all characters except the first and last) is either empty or a palindromic subsequence of x[i+1 .. j-1].
So we can define f(i, j) to be the number of palindromic subsequences beginning at i and ending at j >= i. Then
f(i, j) = 0 if x[i] != x[j]
f(i, i) = 1 (i.e. when j = i)
f(i, j) = 1 + the sum of f(i', j') over all i < i' <= j' < j otherwise
[EDIT: Fixed to count palindromic subsequences of length <= 2 too!]
Then the final answer is the sum of f(i, j) over all 1 <= i <= j <= n.
The DP for this is O(n^4) because there are n^2 table entries, and computing each one takes O(n^2) time. (It's probably possible to speed this up to at least O(n^3) by making use of the fact that x[i] != x[j] implies f(i, j) = 0.)
Intuitive O(n^3) solution using DP:
Let each state dp(i,j) represents number of palindromic subsequences in string[i...j]
Then simple recursive formula is
for k in range i, j-1:
if(A[j]==A[k]){
dp(i,j) = dp(i,j) + dp(k+1,j-1);
The Idea is very simple.. For adding a new character check if it is end of a subsequence or not. If there exist same character in the previously computed smaller subproblem, then it add the number of subsequences contained in range (k+1,j-1).
Just take care of corner cases.
Add one as newly added character is a single character subsequence too.
Even if there are no subsequences in the range (k+1,j-1) , you would still get 1 new subsequences of length 2 (like "aa").
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])
In Introduction to Algorithms(CLRS), Cormen et al. talk about solving the Rod-cutting problem as follows(page 369)
EXTENDED-BOTTOM-UP-CUT-ROD(p, n)
let r[0...n] and s[0....n] be new arrays
r[0] = 0
for j = 1 to n:
q = -infinity
for i = 1 to j:
if q < p[i] + r[j - i]: // (6)
q = p[i] + r[j - i]
s[j] = i
r[j] = q
return r and s
Here p[i] is the price of cutting the rod at length i, r[i] is the revenue of cutting the rod at length i and s[i], gives us the optimal size for the first piece to cut off.
My question is about the outer loop that iterates j from 1 to n and the inner loop i that goes from 1 to n as well.
On line 6 we are comparing q (the maximum revenue gained so far) with r[j - i], the maximum revenue gained during the previous cut.
When j = 1 and i = 1, it seems to be fine, but the very next iteration of the inner loop where j = 1 and i = 2, won't r[j - i] be r[1 - 2] = r[-1]?
I am not sure if the negative index makes sense here. Is that a typo in CLRS or I am missing something here?
I case some of you don't know what the rod-cutting problem is, here's an example.
Here's the key: for i = 1 to j
i will begin at 1 and increase in value up to but not exceeding the value of j.
i will never be greater than j, thus j-i will never be less than zero.
Variable i will not be greater than variable j because of the inner loop and thus index r become never less than zero.
You are missing the conditions in the inner for loop. In that, the value of i goes only upto j. So if it exceeds j, the loop will be terminated. Hence no question of the negative indices you mentioned.