binary search and eps in comparisons - c++

I have 2 comparisons inside binary search, but I can't make an exact preference in between two underlain. I oscillate between in two samples below:
for (int step = 0; step < 100; ++step) {
double middle = (left + right) / 2;
if (f(middle) > 0) right = middle; else left = middle;
}
and
for (int step = 0; step < 100; ++step) {
double middle = (left + right) / 2;
if (f(middle) > eps) right = middle; else left = middle;
}
f is a monotonically increasing function, because even with small eps, there's a danger that the corresponding error in the binary search parameter will be much bigger. On the other hand, even if our comparison is incorrect for equal values due to rounding errors, the binary search will still converge correctly since equal values may only appear in one point and everything will be correct in points very close to it. I want to have an idea about that.

Judging from your code, you are trying to decide when the function will have a zero value. The first method is already good enough, for it is consistent with your intention. It seems that there is no need to use the second method.

Related

clarification about working of two pointer approach

I have some doubts about using two pointer approach.
Case 1: - Suppose we have an array A, that is sorted and a target value B. We want to find out if there exist two elements whose difference is equal to B or not.
int helper(vector<int> &A, int B)
{
int left = 0, n = A.size();
int right = left + 1;
while (right < n)
{
int currDiff = A[right] - A[left];
if (currDiff < B)
right++;
else if (currDiff > B)
{
left++;
if (left == right)
right++;
}
else
return 1;
}
return 0;
}
Case 2: - Suppose we have an array A, that is sorted and a target value B. We want to find out if there exist two elements whose sum is equal to B or not.
int helper(vector<int> &A, int B)
{
int left = 0, n = A.size();
int right = n - 1;
while (left < right)
{
int currSum = A[right] + A[left];
if (currSum < B)
left++;
else if (currSum > B)
{
right--;
}
else
return 1;
}
return 0;
}
The doubt is that in case 1 we set both pointers on the left side(left = 0, right = left + 1) and start scanning while in case 2 we set one pointer on the left side and the other one on the right side(left = 0, right = A.size() - 1).
I am a bit confused about how this is working.
There's no rule that you must have to set the two pointers in different way. It's all about the algorithm you're following. It may be good, it may be bad. Let's say, for difference, we set the left=0 and right=A.size()-1. As the given array A is sorted, the first difference between A[right] and A[left] will be maximum.
int currDiff = A[right] - A[left]; //max possible value for currDiff for A
So, now if currDiff is greater than the given number, what will you do? increase the left or decrease the right? Let say you do the later one, I mean decrease the right, and the corresponding condition satisfies again, do the same, decrease the right. Now, let say now you got the currDiff is smaller than the given number, what will you do? increase the left? probably. But in the next iteration, if you get the same condition satisfied, that is, currDiff is still smaller than the given number, what will you do now? Again increase the left? What if increasing the right in this particular position would give you the result?
So, you see, there arises a lot of cases needed to be handled if you started the finding diff of pair having left and right in the opposite ends.
Finally, what I want to say is, it's all about the algorithm you are following, nothing else.

Why finding median of 2 sorted arrays of different sizes takes O(log(min(n,m)))

Pleas consider this problem:
We have 2 sorted arrays of different sizes, A[n] and B[m];
I have and implemented a classical algorithm that takes at most O(log(min(n,m))).
Here's the approach:
Start partitioning the two arrays into two groups of halves (not two parts, but both partitioned should have same number of elements). The first half contains some first elements from the first and the second arrays, and the second half contains the rest (or the last) elements form the first and the second arrays. Because the arrays can be of different sizes, it does not mean to take every half from each array. Reach a condition such that, every element in the first half is less than or equal to every element in the second half.
Please see the code above:
double median(std::vector<int> V1, std::vector<int> V2)
{
if (V1.size() > V2.size())
{
V1.swap(V2);
};
int s1 = V1.size();
int s2 = V2.size();
int low = 0;
int high = s1;
while (low <= high)
{
int px = (low + high) / 2;
int py = (s1 + s2 + 1) / 2 - px;
int maxLeftX = (px == 0) ? MIN : V1[px - 1];
int minRightX = (px == s1) ? MAX : V1[px];
int maxLeftY = (py == 0) ? MIN : V2[py - 1];
int minRightY = (py == s2) ? MAX : V2[py];
if (maxLeftX <= minRightY && maxLeftY <= minRightX)
{
if ((s1 + s2) % 2 == 0)
{
return (double(std::max(maxLeftX, maxLeftY)) + double(std::min(minRightX, minRightY)))/2;
}
else
{
return std::max(maxLeftX, maxLeftY);
}
}
else if(maxLeftX > minRightY)
{
high = px - 1;
}
else
{
low = px + 1;
}
}
throw;
}
Although the approach is pretty straightforward and it works, I still cannot convince myself of its correctness. Furthermore I cant understand why its takes O(log(min(n,m)) steps.
If anyone can briefly explain the correcthnes and why it takes O(log(min(n,m))) steps that would be awesome. Even if you can provide a link with meaningfull explanation.
Time complexity is quite straightforward, you binary search through the array with less elements to find such a partition, that enables you to find the median. You make exactly O(log(#elements)) steps, and since your #elements is exactly min(n, m) the complexity is O(log(min(n+m)).
There are exactly (n + m)/2 elements smaller than the median and the same amount of elements greater. Let's think about them as two halves (let the median belong to one of your choice).
You can surely divide the smaller array into two subarrays, that one of them lies entirely in the first half and the second one in the other half. However, you have no idea how many elements are in any of them.
Let's choose some x - your guess of number of elements from the smaller array in the first half. It must be in range from 0 to n. Then you know, since there are exactly (n + m)/2 elements smaller than the median, that you have to choose (n+m)/2 - x elements from the bigger array. Then you have to check if that partition actually works.
To check if partition is good you have to check if all the elements in the smaller half are smaller than all the elements in the greater half. You have to check if maxLeftX <= minRightY and if maxLeftY <= minRightX (then every element in the left half is smaller then every element in the right half)
If so, you've found the correct partition. You can now easily find your median (it's either max(maxLeftX, maxLeftY)), min(minRightX, minRightY) or their sum divided by 2).
If not, you either took too much elements from the smaller array (the case when maxLeftX > minRightY), so next time you have to guess smaller value for x, or too little of them, then you have to guess greater value for x.
To get the best complexity always guess in the middle of a range of possible values that x may take.

Understanding the Baeza-Yates Régnier algorithm (multiple string matching, extended from Boyer-Moore)

First of all, excuse me if I write a lot, I tried to summarize my research so that everyone can understand.
R. Baeza-Yates and M. Regnier published in 1990 a new algorithm for searching a two dimensional mm pattern in a two dimensional nn text. The publication is very well written and quite understandable for a novice like me, the algorithm is described in pseudocode and I was able to implements it successfully.
One part of the BYR algorithm requires the Aho-Corasick algorithm. This allows to search occurences of multiple keywords in a string text. However, they also say that this part of their algorithm can be greatly improved by using Aho-Corasick not, but Commentz-Walter algorithm (based on Boyer-Moore rather than Knuth-Morris-Pratt algorithm). They evoke an alternative to the Commentz-Walter algorithm, alternative that they themselves developed. This is described and explained in their previous publication (see 4th chapter).
This is where my problem lies. As I said, the algorithm goes through the text and check if it contains a word from the set of keywords. The words are arranged upside down and placed in a tree. To be efficient, it will sometimes be necessary to skip a number of letters, when he knows that there is no match found.
To determine the number of characters that can be skipped, two tables d and dd have to be computed. Then, the algorithm is very simple:
The algorithm works as follows:
We align the root of the trie with position m in the text, and we start matching the text from right to left following the corresponding
path in the trie.
If a match is found (final node), we output the index of the corresponding string.
After a match or mismatch, we move the trie further in the text using the maximum of the shift associated to the current node (means dd), and the
value of d[x], where x is the character in the text corresponding to
the root of the trie.
Start matching the trie again from right to left in the new position.
My problem is that I do not know how to compute the dd function. In their publication, R. Baeza-Yates and M. Regnier propose a formal definition of it:
pi is a word among the set of keyword, j is the index of a letter in this word, so pi[j] is like a node in the previous trie I showed. Number in the node represented dd(node). L is the number of words, and mi is the number of letters in the word pi.
They give no indication concerning the construction of this function. They only recommend to watch the work of W. Rytter. This document builds a function similar to that expected, the difference being that in this case, there is only one keyword and not a set.
The definiton of dd (called D here), is as follow:
It may be noted similarities with the previous definition, but I do not understand everything.
The pseudocode for the construction of this function is given in the paper, I have implemented it, here in C++:
int pattern[] = { 1, 2, 3, 1 }; /* I use int instead of char, simpler */
const int n = sizeof(pattern) / 4;
int D[n];
int f[n];
int j = n;
int t = n + 1;
for (int k = 1; k <= n; k++){
D[k-1] = 2 * n - k;
}
while (j > 0) {
f[j-1] = t;
while (t <= n) {
if (pattern[j-1] != pattern[t-1]) {
D[t-1] = min(D[t-1], n - j);
t = f[t-1];
}
else {
break;
}
}
t = t - 1;
j = j - 1;
}
int f1[n];
int q = t;
t = n + 1 - q;
int q1 = 1;
int j1 = 1;
int t1 = 0;
while (j1 <= t) {
f1[j1 - 1] = t1;
while (t1 >= 1) {
if (pattern[j1 - 1] != pattern[t1 - 1]) {
t1 = f1[t1 - 1];
}
else {
break;
}
}
t1 = t1 + 1;
j1 = j1 + 1;
}
while (q < n) {
for (int k = q1; k <= q; k++) {
D[k - 1] = min(D[k - 1], n + q - k);
}
q1 = q + 1;
q = q + t - f1[t - 1];
t = f1[t - 1];
}
for (int i = 0; i < n; i++)
{
cout << D[i] << " ";
}
It works, but I do not know how to expand it for several words, I do not know how to coincide with the formal definition of dd given by Baeza-Yates and Régnier. I said that the two definitions was similar, but I do not know to what extent.
I did not find any other information about their algorithm, it is impossible for me to know how to implement the construction of dd, but I am looking for someone who could perhaps understand and show me how to get there, explaining me the link between the definitions of D and dd.
I think d[x] corresponds to the bad character rule in http://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm and D corresponds to the Good Suffix rule in the same article. This would mean that x in d[x] is not the character in the root of the tree, but the value of the first character in the text being searched that fails to match a child of the current node.
I think the idea is the same as Boyer-Moore. You move along the tree as long as you have a match, and when you have a mismatch you know two things: the character causing the mismatch, and the substring you have matched so far. Taking each of these things independently, you may be able to work out that if you shifted along the text being searched 1,2,..k positions you still wouldn't have a match, because at these offsets the character that caused a mismatch would still cause a mismatch, or the portion of the text that previously matched would not match at this shifted offset. So you can skip on to the first offset not ruled out by either value.
Actually, this suggests a variant scheme, in which d and DD provide not numbers but bit-masks, and you and together the two bitmaps and shift according to the position of the first bit that is still set. Presumably this doesn't save you enough to be worth the extra set-up time.

C++ Adding big numbers together with operator overload

I am new to C++ and attempting to create a "BigInt" class. I decided to base most of the implementation on reading the numbers into vectors.
So far I have only written the copy constructor for an input string.
Largenum::Largenum(std::string input)
{
for (std::string::const_iterator it = input.begin(); it!=input.end(); ++it)
{
number.push_back(*it- '0');
}
}
The problem I am having is with the addition function. I have created a function which seems to work after I tested it a few times, but as you can see its highly inefficient. I have 2 different vectors such as:
std::vector<int> x = {1,3,4,5,9,1};
std::vector<int> y = {2,4,5,6};
The way I thought to solve this problem was to add 0s before the shorter, in this case y vector to make both vectors have the same size such as:
x = {1,3,4,5,9,1};
y = {0,0,2,4,5,6};
Then to add them using elementary style addition.
I don't want to add 0s infront of vector Y as it would be slow with a large number. My current solution is to reverse the vector, then push_back the appropriate amount of 0s, then reverse it back. This may be slower then simply inserting at the front it seems, I have not tested yet.
The problem is that after I do all of the addition on the vectors and push_back the result. I am left with a backward vector and I need to use reverse yet again! There has got to be a much better way then my method but I am stuck on finding it. Ideally I would make A const as well. Here is the code of the function:
Largenum Largenum::operator+(Largenum &A)
{
bool carry = 0;
Largenum sum;
std::vector<int>::size_type max = std::max(A.number.size(), this->number.size());
std::vector<int>::size_type diff = std::abs (A.number.size()-this->number.size());
if (A.number.size()>this->number.size())
{
std::reverse(this->number.begin(), this->number.end());
for (std::vector<int>::size_type i = 0; i<(max-diff); ++i) this->number.push_back(0);
std::reverse(this->number.begin(), this->number.end());
}
else if (this->number.size() > A.number.size())
{
std::reverse(A.number.begin(), A.number.end());
for (std::vector<int>::size_type i = 0; i<(max-diff); ++i) A.number.push_back(0);
std::reverse(A.number.begin(), A.number.end());
}
for (std::vector<int>::size_type i = max; i!=0; --i)
{
int num = (A.number[i-1] + this->number[i-1] + carry)%10;
sum.number.push_back(num);
(A.number[i-1] + this->number[i-1] + carry >= 10) ? carry = 1 : carry = 0;
}
if (carry) sum.number.push_back(1);
reverse(sum.number.begin(), sum.number.end());
return sum;
}
If anyone has any input that would be great, this is my first program using classes in C++ and its fairly overwhelming.
I think your function is quite close to the most optimal one I have seen. Still here are few suggestions how to improve it:
Decimal numeric system is quite inefficient, you have a lot of digits for big numbers. Better use a higher base to reduce the number of digits you have to add. Reading and writing such numbers in human readable representation will be a bit harder, but you will optimize the operations several times, because you will have less digits.
When implementing big integers I represent them in reverse order, thus I have the least significant digit at position with index 0, and the most significant one at the end of the array. This way when carry forces you to add a new digit you only perform a push_back, not a whole reverse.
One issue: integer modulus is pretty slow on modern processors, even compared to branch misprediction. Rather than doing an explicit %10, try this for your third for-loop:
int num = A.number[i-1] + this->number[i-1] + carry;
if(num >= 10)
{
carry = 1;
num -= 10;
}
else
{
carry = 0;
}
sum.number.push_back(num);

How Can I Make My Array Rotation More Efficient?

How can I make my circular array rotation more efficient? I read in this thread about an excellent sorting algorithm, but it won't work for what I need because there are spaces at the end of the array that get sorted into the middle.
The rotation function needs to work for both left and right rotation. Not every space of the array will be filled.
void Quack::rotate(int r)
{
if(r > 0) //if r is positive, rotate left
{
for(int i = 0; i < r; i++)
items[(qBack + i) % qCapacity] = items[(qFront + i) % qCapacity];
//move items in array
}
else if(r < 0) //if r is negative, rotate right
{
for(int i = 0; i < (r * -1); i++)
items[(qFront - i - 1) % qCapacity] =
items[(qBack - i - 1) % qCapacity];
//move items in array
}
//if r = 0, nothing happens
//rotate front and back by r
qFront = (qFront + r) % qCapacity;
qBack = (qBack + r) % qCapacity;
}
I haven't used it, so I can't promise it will do everything you need. But you might want to look into simply replacing this function body with the std::rotate function.
It should already be well optimized, and will be much less likely to introduce bugs into your application.
http://www.sgi.com/tech/stl/rotate.html
If you want suggestions for optimization though, I recommend avoiding all modulo operations. They may require a divide, which is one of the most expensive operations you can perform on your processor. They are a convenient way to think about how to accomplish your goal, but could be very costly for your CPU to execute.
You can remove your modulo operators if you use two loops: one from the middle to the end, and the other from the beginning to the middle.
But if you can, see if you can avoid doing the rotation altogether. If you are careful you might be able to eliminate pointless full-array traversal/copy operations. See my comment on the OP for how to accomplish this.