Related
Let's say I have two intervals,
[a1, a2] and [b1, b2]
Where a1,a2,b1,b2 are all in the range [0, 2 pi]. Now, given these two intervals, I want to find their overlapping interval. This is quite tricky. Since an example of two intervals is
[5, 1] and [0, 6]
Which are sketched below (the red areas are the intervals).
Notice that these two intervals return an overlapping interval that consists of two intervals:
[0,1] and [5,6]
There are multiple different cases that must be treated, is there any known algorithm that does this?
I do not know of an existing algorithm (which doesn't mean there isn't one), but here's one I've come up with.
As already mentioned by #Michael Kenzel, numbers don't increase monotonically, which makes everything very complicated.
But we can observe that we can unroll the circle onto the number line.
Then each interval then appears infinitely many times with a period of 2π.
Let's first define a normalize operation as following:
normalize([a, b]):
if (a > b):
a -= 2π
Using this operation we unroll both our intervals onto a [-2π, 2π] part of the number line.
Example intervals:
[2, 5] -> [2, 5]
[4, π] -> [-2, π]
Two intervals on a circle can overlap at most 2 times.
(I don't have a proper proof of this, but intuitively: an overlap starts where one interval started and another one has not ended. This can happen only once on a number line and twice in our case.)
By just looking at normalized intervals, we can miss one of the overlaps. In the example above we would detect [2, π] overlap and miss [4, 5]. This happens because we have unrolled the original intervals not onto [0, 2π], but a twice longer part of the number line, [-2π, 2π].
To correct for that, we can, for example, take the part that falls onto the negative line and shift it by 2π to the right, this way having all pieces in the original [0, 2π]. But it is computationally ineffective, as we will, in the worst case, have to test 2 pieces on one interval against 2 pieces of another interval - total of 4 operations.
Here is an illustration of such an unlucky example that will require 4 comparisons:
If we want to be a bit more efficient, we will try to do only 2 interval-vs-interval operations. We won't need more as there will be at most 2 overlaps.
As the intervals repeat infinitely on the number line with the period of 2π, we can just take 2 neighboring duplicates of the first interval and compare them against the second one.
To make sure that the second interval will be, so to say, in between those duplicates, we can take the one that starts earlier and add 2π to its both ends. Or subtract 2π from the one that starts later.
There will be not more than two overlaps, which can be then brought back to the [0, 2π] interval by addition/subtraction of 2π.
In our original example it would look like that:
To sum it up:
given [a, b], [c, d]
[A, B] = normalize([a, b])
[C, D] = normalize([c, d])
if (A < C):
[E, F] = [A + 2π, B + 2π]
else:
[E, F] = [A - 2π, B - 2π]
I_1 = intersect([A, B], [C, D])
I_2 = intersect([E, F], [C, D])
bring I_1 and I_2 back to the [0, 2π]
I think I didn't miss any corner cases, but feel free to point to any mistake in my logic.
The first thing to note is that no new angle is created when you discover the intersection sector of two sectors 'A' and 'B'. Sector 'A' is defined by two limits, Acw and Acc, and sector 'B' is defined by two limits, Bcw and Bcc. Here 'cw' and 'cc' denote 'ClockWise' and 'CounterClockwise'.
The boundaries of the intersection sector will be made from at most two out of these four angles. The problem is entirely concerned with selecting two out of these four angles to be the limits of the intersection sector, let's call them Icw and Icc.
It is important to distinguish between "cw" and "cc" and keep them straight throughout the problem, because any pair of angles actually defines two sectors, right? This is as you have shown in your picture at the top of this answer. The issue of "angle wraparound" will arise naturally as the problem is solved.
Some Helper Functions
OK, so we have our four angles, and we have to select two of the four to be the limits of our intersection sector. In order to do this, we need an operator that determines whether an angle dAngle falls between two limits, let's call them dLimitCW and dLimitCC.
It is in this operator that the issue of "angle wraparound" arises. I did mine by constraining all angles to the range of -π to π. To determine whether dAngle falls between dLimitCW and dLimitCC, we subtract dAngle from dLimitCW and dLimitCC, and then constrain the result to fall within the [-π, π] range by adding or subtracting 2π. This is just like rotating the polar coordinate system by the angle dAngle , so that what was dAngle is now zero; what was dLimitCW is now dLimitCWRot, and what was dLimitCC is now dLimitCCRot.
The code looks like this:
bool AngleLiesBetween(double dAngle, double dLimitCW, double dLimitCC)
{
double dLimitCWRot, dLimitCCRot;
// Rotate everything so that dAngle is on zero axis.
dLimitCWRot = constrainAnglePlusMinusPi(dLimitCW - dAngle);
dLimitCCRot = constrainAnglePlusMinusPi(dLimitCC - dAngle);
if (dLimitCWRot > dLimitCCRot)
return (signbit(dLimitCWRot * dLimitCCRot));
else
return (!signbit(dLimitCWRot * dLimitCCRot));
}
where the function constrainAnglePlusMinusPi is
double constrainAnglePlusMinusPi(double x)
{
x = fmod(x + pi, 2*pi);
if (x < 0)
x += 2*pi;
return x - pi;
}
Once we have our "angle lies between" function, we use it to select which of the four angles that made up the limit angles of the two sectors make up the intersection sector.
The Nitty Gritty
To do this, we must first detect the case in which the two angular ranges do not overlap; we do this by calling our AngleLiesBetween() function four times; if it returns a "false" all four times, there is no overlap and the intersection is undefined:
if (
(AngleLiesBetween(Acw, Bcw, Bcc) == false) &&
(AngleLiesBetween(Acc, Bcw, Bcc) == false) &&
(AngleLiesBetween(Bcw, Acw, Acc) == false) &&
(AngleLiesBetween(Bcc, Acw, Acc) == false)
)
{
// Ranges have no overlap, result is undefined.
*this = CAngleRange(0.0f, 0.0f);
return;
}
Here I'm returning a CAngleRange object containing (0.0, 0.0) to indicate "no overlap," but you can do it some other way if you like, like by having your "intersection" function return a value of "false."
Once you've handled the "no overlap" case, the rest is easy. You check each of the six remaining combinations one at a time and determine which two limits are the limits of I by their outcomes:
if ((AngleLiesBetween(Acw, Bcw, Bcc) == true) && (AngleLiesBetween(Acc, Bcw, Bcc) == false)) then Icw = Acw and Icc = Bcc;
if ((AngleLiesBetween(Acw, Bcw, Bcc) == false) && (AngleLiesBetween(Acc, Bcw, Bcc) == true)) then Icw = Bcw and Icc = Acc;
if ((AngleLiesBetween(Acw, Bcw, Bcc) == true) && (AngleLiesBetween(Acc, Bcw, Bcc) == true)) then Icw = Acw and Icc = Acc;
if ((AngleLiesBetween(Bcw, Acw, Acc) == true) && (AngleLiesBetween(Bcc, Acw, Acc) == false)) then Icw = Bcw and Icc = Acc;
if ((AngleLiesBetween(Bcw, Acw, Acc) == false) && (AngleLiesBetween(Bcc, Acw, Acc) == true)) then Icw = Acw and Icc = Bcc;
and finally
if ((AngleLiesBetween(Bcw, Acw, Acc) == true) && (AngleLiesBetween(Bcc, Acw, Acc) == true)) then Icw = Bcw and Icc = Bcc.
You don't have to constrain the results to [-π, π] or [0, 2π] because you haven't changed them; each of the result angles is just one of the angles you presented as input to the function in the first place.
Of course you can optimize and streamline the code I've given in various ways that take advantage of the symmetries inherent in the problem, but I think when all is said and done you have to compare eight separate angle combinations for "between-ness" no matter how you optimize things. I like to keep things simple and straightforward in my code, in case I have made an error and have to come back and debug it in a few years when I've forgotten all my clever optimizations and streamlining efforts.
About Angle Wraparound
Notice that the issue of "angle wraparound" got handled in function AngleLiesBetween(); we rotated the coordinate system to put the angle we are checking (which we called dAngle) for "between-ness" at zero degrees. This naturally puts the two angle limits (dLimitCW and dLimitCC) on either side of the polar origin in the case in which dAngle is between those limits. Thus, the wrap-around issue disappears; we've "rotated it out of the way," so to speak.
About the Polar Coordinate System
It may be worth noting that in the polar coordinate system I'm using, CW angles are more positive than CC angles (unless the wrap-around is between them). This is the opposite of the polar coordinates we learn in calculus class, where angles increase in the CC (counterclockwise) direction.
This is because of the quandary that results from the decision — made long, long ago — that computer devices (like display surfaces, originally implemented on cathode ray tubes) would count the vertical direction as increasing downward, instead of upward, as we learn when we study analytic geometry and calculus.
The people who made this decision did so because they wanted to display text on the screen, with the "first" line at the top, the "second" line (line 2) below the "first" line (line 1), and so forth. To make this easier to keep track of this in their early machine code, they had the +y direction go "down," as in "toward the bottom of the screen." This decision has far-reaching and annoying consequences for people who do image geometry in code.
One of the consequences is that by flipping the direction of +y, the "sense of rotation" also flipped from the conventional "angle increases counterclockwise" sense we're used to from high-school math. This issue is mostly invisible at the coding level; it only comes out when you look at your results on the screen.
This is why it is very important to write code to visualize your results on the screen before you trust them. Here "trust them" is certainly necessary before you let your customer see them.
As long as you have intervals where the numbers just increase monotonically, it's simple; you just take the max() of the minimums and the min() of the maximums and done. It would seem that the major source of complication here is the fact that you can have intervals that wrap around at 0, i.e., the numbers that are part of the interval are not monotonically-increasing. It would seem to me that one way around this problem is to simply treat intervals that wrap around as not one interval but the union of two intervals. For example, think of [5, 1] as [5, 2 pi] ∪ [0, 1]. Then the problem of finding the intersection of [5, 1] and [0, 6] turns into the problem of finding the intersection of [5, 2 pi] and [0, 6] as well as the intersection of [0, 1] and [0, 6]. Mathematically, you'd be taking advantage of the distributive law of set intersection, i.e., (A ∪ B) ∩ C = (A ∩ C) ∪ (B ∩ C). So given two intervals A and B, we would start by splitting each into two intervals, A1 and A2, and B1 and B2, where A1 and B1 each start after 0 and end before 2 pi, and A2 and B2 start before 2 pi and end before 2 pi. Slitting like this, we can compute our intersections like
(A1 ∪ A2) ∩ (B1 ∪ B2) = (A1 ∩ (B1 ∪ B2)) ∪ (A2 ∩ (B1 ∪ B2) = (A1 ∩ B1) ∪ (A1 ∩ B2) ∪ (A2 ∩ B1) ∪ (A2 ∩ B2)
i.e., compute the intersection of all combinations of A1, A2, B1, and B2…
I've got the following task to do:
Given a rectangular chocolate bar, consisting of m x n small rectangles, and the wish of breaking it into its parts. At each step you can only pick one piece and break it either along any of its vertical lines or along its horizontal lines. How should you break the chocolate bar using the minimum number of steps?
I know you need exactly m x n - 1 steps to break the chocolate bar, but I'm asked to do it "the CS way:"
Define a predicate which selects the minimum number of steps among all alternative possibilities to break the chocolate bar into pieces. Construct a straucture on an additional argument position, which tells you where and how to break the bar and what to do with the resulting two pieces.
My thoughts: after breaking the piece of chocolate once, you have the choice of breaking it either on its vertical or its horizontal lines. So this is my code, but it doesn't work:
break_chocolate(Horizontal, Vertical, Minimum) :-
break_horizontal(Horizontal, Vertical, Min1),
break_vertical(Horizontal, Vertical, Min2),
Minimum is min(Min1, Min2).
break_horizontal(0,0,_).
break_vertical(0,0,_).
break_horizontal(0, V, Min) :-
V > 0,
break_horizontal(0, V, Min).
break_horizontal(H, V, Min) :-
H1 is H-1,
Min1 is Min + 1,
break_vertical(H1, V, Min1).
break_horizontal(H, V, Min) :-
H1 is H-1,
Min1 is Min + 1,
break_vertical(H1, V, Min).
break_vertical(H, V, Min) :-
V1 is V-1,
Min1 is Min + 1,
break_horizontal(H, V1, Min1).
break_vertical(H, V, Min) :-
V1 is V-1,
Min1 is Min + 1,
break_vertical(H, V1, Min1).
break_vertical(H, 0, Min) :-
H > 0,
break_horizontal(H, 0, Min).
Could anyone help me with this one?
This is not a complete answer, but should push you towards the right direction:
First an observation: every time you cut a chocolate bar, you end up with exactly one more pieces than you had before. So, actually, there is no "minimal" number of breaks you can have; you start with 1 piece (the whole bar), and you end up with m * n pieces, so you always have exactly m * n - 1 breaks. So either you have misunderstood your problem statement, or somehow misrepresented it in your question.
Second: once you break into two pieces, you will have to break each of the two in the same way that you have broken the previous one. One way to program that would be with a recursive call. I don't see this in your program, as it stands.
Third: so do you want to report the breaks that you make? How are you going to do this?
Whether you program in Prolog, C, or JavaScript, understanding your problem is a prerequisite to finding a solution.
Here are some additional hints for representing and solving your problem.
Each break separates one piece into two pieces (see Boris' second hint). You can, therefore, think of the collection of breaks as a binary tree of breaks which has the following characteristics:
The root node of the tree has the value M-N (the bar is M x N in dimension)
Suppose X-Y represents the value of any node in the tree representing a single X by Y piece that is not the single piece, 1-1. Since the two children of the node represents the piece of dimension X and Y being broken along one dimension, then the children of X-Y either have the values A-Y and B-Y where A + B = X, or the values X-A and X-B where A + B = Y
All of the leaf nodes of the tree have the value 1-1 (the smallest possible piece)
Each node of a binary tree consists of the node value, the left sub tree, and the right sub tree. An empty sub tree would have the value nil (or some other suitably chosen atom). A common representation of a tree would be something like, btree(X-Y, LeftSubTree, RightSubTree) (the term X-Y being the value of the top node of the tree, which in this problem, would be the dimensions of the piece in question). Using this scheme, the smallest piece of candy would be, btree(1-1, nil, nil), for example. A set of breaks for a 2 x 1 candy bar would be, btree(2-1, btree(1-1, nil, nil), btree(1-1, nil, nil)).
You can use the CLPFD library to constrain C #= A + B, A #> 0, B #> 0 and, to eliminate symmetrical cases, A #=< B.
As an algorithm (I'm not familiar with Prolog), I can't find any different answers in the number of breaks. I've tried 4x4, and been unable to come up with an answer other than 15 (either above or below); I've tried with a 5x2 and been unable to come up with an answer other than 9.
On this basis, I would suggest the simplest possible coding method:
while there is more than one column:
snap off the left-most column
while this column has more than one square:
snap off the top square
while the remaining column has more than one square:
snap off the top square
Depending on the situation, you may wish to change one or more of: (left, column)<->(top, row), left->right, top->bottom.
Maybe I should ask this in Mathoverflow but here it goes:
I have 2 data sets (sets of x and y coordinates) with different number of elements. I have to find the best match between them by stretching one of the data sets (multiplying all x with a factor of m and all y with a factor of n) and moving it around (adding p and q to all x and y respectively).
Basically these 2 sets represent different curves and i have to fit curve B (which has less elements) to some segment of curve A (which has many more elements).
How can I find the values m, n, p, and q for the closest match?
Answers can be pseudo code, C, Java or Python. Thanks.
Following is a solution for finding values m, n, p and q when after transforming the first curve it matches exactly with a part of the second curve:
Basically, we have to solve the following matrix equation:
[m n][x y]' + [p q]' = [X Y]' ...... (1)
where [x y]' and [X Y]' are the coordinates of first and second curves respectively. Let's assume first curve has total l number of coordinates and second curve has total h number of coordinates.
(1) implies,
[mx+p ny+1]' = [X Y]'
i.e we have to solve:
mx_1+p = X_k, mx_2+p = X_{k+1}, ..., mx_l+p = X_{k+l-1}
ny_1+q = Y_k, ny_2+q = Y_{k+1}, ..., ny_l+q = Y_{k+l-1}
where k+l-1 <= h-l
We can solve it in the following naive way:
for (i=1 to h-l){
(m,p) = SOLVE(x1, X_i, x2, X_{i+1})// 2 unknowns, 2 equations
(n,q) = SOLVE(y1, Y_i, y2, Y_{i+1})// 2 unknowns, 2 equations
for (j=3 to l){
if(m*x[j]+p != m*X[i+2]+p)break;//value of m, p found from 1st 2 doesn't work for rest
if(n*y[j]+q != n*Y[i+2]+q)break;//value of n, q found from 1st 2 doesn't work for rest
}
if(j==l){//match found
return i;//returns the smallest index of 2nd curves' coordinates where we found a match
}
}
return -1;//no match found
I am not sure if there can be an optimized version of this.
I have a list of elements, each one identified with a type, I need to reorder the list to maximize the minimum distance between elements of the same type.
The set is small (10 to 30 items), so performance is not really important.
There's no limit about the quantity of items per type or quantity of types, the data can be considered random.
For example, if I have a list of:
5 items of A
3 items of B
2 items of C
2 items of D
1 item of E
1 item of F
I would like to produce something like:
A, B, C, A, D, F, B, A, E, C, A, D, B, A
A has at least 2 items between occurences
B has at least 4 items between occurences
C has 6 items between occurences
D has 6 items between occurences
Is there an algorithm to achieve this?
-Update-
After exchanging some comments, I came to a definition of a secondary goal:
main goal: maximize the minimum distance between elements of the same type, considering only the type(s) with less distance.
secondary goal: maximize the minimum distance between elements on every type. IE: if a combination increases the minimum distance of a certain type without decreasing other, then choose it.
-Update 2-
About the answers.
There were a lot of useful answers, although none is a solution for both goals, specially the second one which is tricky.
Some thoughts about the answers:
PengOne: Sounds good, although it doesn't provide a concrete implementation, and not always leads to the best result according to the second goal.
Evgeny Kluev: Provides a concrete implementation to the main goal, but it doesn't lead to the best result according to the secondary goal.
tobias_k: I liked the random approach, it doesn't always lead to the best result, but it's a good approximation and cost effective.
I tried a combination of Evgeny Kluev, backtracking, and tobias_k formula, but it needed too much time to get the result.
Finally, at least for my problem, I considered tobias_k to be the most adequate algorithm, for its simplicity and good results in a timely fashion. Probably, it could be improved using Simulated annealing.
First, you don't have a well-defined optimization problem yet. If you want to maximized the minimum distance between two items of the same type, that's well defined. If you want to maximize the minimum distance between two A's and between two B's and ... and between two Z's, then that's not well defined. How would you compare two solutions:
A's are at least 4 apart, B's at least 4 apart, and C's at least 2 apart
A's at least 3 apart, B's at least 3 apart, and C's at least 4 apart
You need a well-defined measure of "good" (or, more accurately, "better"). I'll assume for now that the measure is: maximize the minimum distance between any two of the same item.
Here's an algorithm that achieves a minimum distance of ceiling(N/n(A)) where N is the total number of items and n(A) is the number of items of instance A, assuming that A is the most numerous.
Order the item types A1, A2, ... , Ak where n(Ai) >= n(A{i+1}).
Initialize the list L to be empty.
For j from k to 1, distribute items of type Ak as uniformly as possible in L.
Example: Given the distribution in the question, the algorithm produces:
F
E, F
D, E, D, F
D, C, E, D, C, F
B, D, C, E, B, D, C, F, B
A, B, D, A, C, E, A, B, D, A, C, F, A, B
This sounded like an interesting problem, so I just gave it a try. Here's my super-simplistic randomized approach, done in Python:
def optimize(items, quality_function, stop=1000):
no_improvement = 0
best = 0
while no_improvement < stop:
i = random.randint(0, len(items)-1)
j = random.randint(0, len(items)-1)
copy = items[::]
copy[i], copy[j] = copy[j], copy[i]
q = quality_function(copy)
if q > best:
items, best = copy, q
no_improvement = 0
else:
no_improvement += 1
return items
As already discussed in the comments, the really tricky part is the quality function, passed as a parameter to the optimizer. After some trying I came up with one that almost always yields optimal results. Thank to pmoleri, for pointing out how to make this a whole lot more efficient.
def quality_maxmindist(items):
s = 0
for item in set(items):
indcs = [i for i in range(len(items)) if items[i] == item]
if len(indcs) > 1:
s += sum(1./(indcs[i+1] - indcs[i]) for i in range(len(indcs)-1))
return 1./s
And here some random result:
>>> print optimize(items, quality_maxmindist)
['A', 'B', 'C', 'A', 'D', 'E', 'A', 'B', 'F', 'C', 'A', 'D', 'B', 'A']
Note that, passing another quality function, the same optimizer could be used for different list-rearrangement tasks, e.g. as a (rather silly) randomized sorter.
Here is an algorithm that only maximizes the minimum distance between elements of the same type and does nothing beyond that. The following list is used as an example:
AAAAA BBBBB CCCC DDDD EEEE FFF GG
Sort element sets by number of elements of each type in descending order. Actually only largest sets (A & B) should be placed to the head of the list as well as those element sets that have one element less (C & D & E). Other sets may be unsorted.
Reserve R last positions in the array for one element from each of the largest sets, divide the remaining array evenly between the S-1 remaining elements of the largest sets. This gives optimal distance: K = (N - R) / (S - 1). Represent target array as a 2D matrix with K columns and L = N / K full rows (and possibly one partial row with N % K elements). For example sets we have R = 2, S = 5, N = 27, K = 6, L = 4.
If matrix has S - 1 full rows, fill first R columns of this matrix with elements of the largest sets (A & B), otherwise sequentially fill all columns, starting from last one.
For our example this gives:
AB....
AB....
AB....
AB....
AB.
If we try to fill the remaining columns with other sets in the same order, there is a problem:
ABCDE.
ABCDE.
ABCDE.
ABCE..
ABD
The last 'E' is only 5 positions apart from the first 'E'.
Sequentially fill all columns, starting from last one.
For our example this gives:
ABFEDC
ABFEDC
ABFEDC
ABGEDC
ABG
Returning to linear array we have:
ABFEDCABFEDCABFEDCABGEDCABG
Here is an attempt to use simulated annealing for this problem (C sources): http://ideone.com/OGkkc.
I believe you could see your problem like a bunch of particles that physically repel eachother. You could iterate to a 'stable' situation.
Basic pseudo-code:
force( x, y ) = 0 if x.type==y.type
1/distance(x,y) otherwise
nextposition( x, force ) = coined?(x) => same
else => x + force
notconverged(row,newrow) = // simplistically
row!=newrow
row=[a,b,a,b,b,b,a,e];
newrow=nextposition(row);
while( notconverged(row,newrow) )
newrow=nextposition(row);
I don't know if it converges, but it's an idea :)
I'm sure there may be a more efficient solution, but here is one possibility for you:
First, note that it is very easy to find an ordering which produces a minimum-distance-between-items-of-same-type of 1. Just use any random ordering, and the MDBIOST will be at least 1, if not more.
So, start off with the assumption that the MDBIOST will be 2. Do a recursive search of the space of possible orderings, based on the assumption that MDBIOST will be 2. There are a number of conditions you can use to prune branches from this search. Terminate the search if you find an ordering which works.
If you found one that works, try again, under the assumption that MDBIOST will be 3. Then 4... and so on, until the search fails.
UPDATE: It would actually be better to start with a high number, because that will constrain the possible choices more. Then gradually reduce the number, until you find an ordering which works.
Here's another approach.
If every item must be kept at least k places from every other item of the same type, then write down items from left to right, keeping track of the number of items left of each type. At each point put down an item with the largest number left that you can legally put down.
This will work for N items if there are no more than ceil(N / k) items of the same type, as it will preserve this property - after putting down k items we have k less items and we have put down at least one of each type that started with at ceil(N / k) items of that type.
Given a clutch of mixed items you could work out the largest k you can support and then lay out the items to solve for this k.
I'm sure this must have been asked before, but I'm not finding it: I'm only finding related, but harder questions.
I've got four points, representing two lines like this:
A C B D
|------*---|-----+----|-*---+---|----------|
0 10 20 30 40
So in the example, AB = {7, 21} and CD = {16,26}. (The lines could be in any relation to each other, and any size.) I want to find out whether or not they overlap, and by how much if so. (In the example, the answer would be 5.) My current solution involves a bunch of complicated if/then steps, and I can't help but think there's a nice arithmetical solution. Is there?
(P.S. Really, I'm doing bounding-box intersection, but if I can get it in one dimension, the other will be the same, obviously.)
Try this:
intersects = (max(a,b) > min(c,d)) && (min(a,b) < max(c,d))
overlap = min(max(a,b), max(c,d)) - max(min(c,d), min(a,b))
If you can assume a <= b and c <= d:
intersects = (b > c) && (a < d)
overlap = min(b, d) - max(c, a)
You can also calculate intersects as follows:
intersects = (overlap > 0)
A line segment is the intersection of two opposing rays (two half-infinite lines in opposite directions). You have two line segments to intersect -- the result is the intersection of all 4 rays. So you can factor your code as three successive ray-intersections: the leftward of the left-facing rays intersected with the rightward of the right-facing rays.
(This is another way of stating the now-accepted answer.)