Intersect of two lines by begin and end points

落花浮王杯 提交于 2021-02-20 05:18:08

问题


I have a fairly basic question here. I want to find if two lines on a 1-D plane intersect. I know of two simple ways to solve this, but I wanted to know if Python has a more elegant way to solve this?

Ex:

x = [1, 10] # 1 = begin, 10 = end
y = [15, 20]
z = [5, 12]

#Method 1: Works. Is quick. Lots of typing.
def is_intersect_1(a, b):
    bool_check = False
    if a[0] <= b[0] <= a[1] or \
    a[0] <= b[1] <= a[1] or \
    b[0] <= a[0] <= b[1] or \
    b[0] <= a[1] <= b[1]:
        bool_check = True
    return bool_check

is_intersect_1(x,y) # False
is_intersect_1(x,z) # True

#Method 2: Quicker to write. Simpler to read. Uses more memory and is slower.

def is_intersect_2(a, b):
    bool_check = False
    if set(range(a[0], a[1]+1)).intersection(set(range(b[0], b[1])):
        bool_check = True
    return bool_check

is_intersect_2(x,y) # False
is_intersect_2(x,z) # True

回答1:


Although not Python-centric per se, here's an elegant way of solving the problem.
The central idea is that if the two intervals aren't completely disjoint, then they must intersect, so all you have to do is check for that condition.

class Interval(object):
    """ Representation of a closed interval from 'a' to 'b'. """
    def __init__(self, a, b):
        self.a, self.b = (a, b) if a < b else (b, a)  # make a min & b max

    def intersects(self, other):
        return self.b >= other.a and self.a <= other.b

    def __str__(self):
        return '[{0.a:>{w}}, {0.b:>{w}}]'.format(self, w=2)

testcases = ((Interval(1,  5), Interval(11, 14)),  #  xxxxx
                                                   #            xxxxx
             (Interval(1,  9), Interval( 7, 15)),  #  xxxxxxxxx
                                                   #        xxxxxxxxx
             (Interval(5,  9), Interval( 1, 15)),  #      xxxxx
                                                   #  xxxxxxxxxxxxxxx
             (Interval(0, 15), Interval( 5,  9)))  #  xxxxxxxxxxxxxxx
                                                   #      xxxxx

for I1, I2 in testcases:
    print('{} {:^7} intersect with {}'.format(
                            I1, "does" if I1.intersects(I2) else "doesn't", I2))

Output:

[ 1,  5] doesn't intersect with [11, 14]
[ 1,  9]  does   intersect with [ 7, 15]
[ 5,  9]  does   intersect with [ 1, 15]
[ 0, 15]  does   intersect with [ 5,  9]



回答2:


I haven't attempted to measure the performance, but I think this is clearer and likely to be faster - it trades the "or", potentially, of two additional "ternary comparisons" for two comparisons (min and max):

>>> x = [1,10]
>>> y = [20,15]
>>> z = [5,12]
>>> def intersects (a, b):
...   c = [min (b), max(b)]
...   return (c[0] < a[0] < c[1]) or (c[0] < a[1] < c[1])
... 
>>> intersects (x, y)
False
>>> intersects (x, z)
True

a intersects b if either end is within b. In the function, c just assures that we know which end of b is which. It would work equally well swapping the treatment of b for a.

Measuring the performance would require running a suite of all possible permutations of the specification of the second line, and the choice of intersection of either end, or neither.

Edited from here. I cranked up an ipython notebook to test the performance. The first method in the initial post is in fact faster based on a sample of intervals generated at random in the range -100 to 100. Mine made the comparison in 827 microseconds per loop through the 1000 comparisons versus 527.

Unfortunately, the testing showed that the first method in the post fails.

[59, -35] [89, -9] False

f = intersects2
for x in w:
    print (v, x, f(x, v))    

[59, -35] [89, -9] False

[59, -35] [76, 89] False




回答3:


This question turned out to be more interesting than I expected. To me, the original solution just looked too complicated. I don't trust complicated, so I tried my own hand at a solution, which was simpler, and easily provably correct. I should have left the comparison at less than or equal, instead of just less than. After playing around to compare the speeds of the two, I accidentally, on a test of only two conditions, found that the original solution was flawed. I also found out that, though my solution - with the correction just mentioned - was slower than the proposed solution.

The proposed solution fails 5 of 24 possible cases. I leave it to the author to correct his function. I have not even tried to determine where his error occurred. But I offer the following function, to be used for testing.

There are tools that test code coverage. This problem and the solutions are interesting in that the coverage to test fully the solution need to be at less than the line level in granularity.

In the following code, pass the proposed function to test_intersection. It will throw an exception if even one of the 24 possible cases fails. My solution, and the modification proposed using a tuple internally, pass all 24. The original solution fails 5 of the cases. After posting this, I realize that there are some additional cases that could be added. ([3,4], [3,7]), ([3,7], [3,7]), ([4,7], [3,7]) as well as the variants in which the "intervals" of the lines are backward.

def test_intersection (f):
    assert not f ([1,2], [3,7])
    assert     f ([1,3], [3,7])
    assert     f ([1,4], [3,7])
    assert     f ([4,5], [3,7])
    assert     f ([4,8], [3,7])
    assert     f ([7,9], [3,7])
    assert not f ([8,9], [3,7])
    assert not f ([2,1], [3,7])
    assert     f ([3,1], [3,7])
    assert     f ([4,1], [3,7])
    assert     f ([5,4], [3,7])
    assert     f ([8,4], [3,7])
    assert     f ([9,7], [3,7])
    assert not f ([9,8], [3,7])
    assert not f ([1,2], [7,3])
    assert     f ([1,3], [7,3])
    assert     f ([1,4], [7,3])
    assert     f ([4,5], [7,3])
    assert     f ([4,8], [7,3])
    assert     f ([7,9], [7,3])
    assert not f ([8,9], [7,3])
    assert not f ([2,1], [7,3])
    assert     f ([3,1], [7,3])
    assert     f ([4,1], [7,3])
    assert     f ([5,4], [7,3])
    assert     f ([9,7], [7,3])
    assert not f ([9,8], [7,3])


来源:https://stackoverflow.com/questions/29043456/intersect-of-two-lines-by-begin-and-end-points

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!