简体   繁体   English

两条线的起点和终点相交

[英]Intersect of two lines by begin and end points

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?我知道有两种简单的方法可以解决这个问题,但我想知道 Python 是否有更优雅的方法来解决这个问题?

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

Although not Python-centric per se, here's an elegant way of solving the problem.虽然本身不​​是以 Python 为中心的,但这里有一个解决问题的优雅方法。
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]

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.如果任一端在 b 内,则 a 与 b 相交。 In the function, c just assures that we know which end of b is which.在函数中,c 只是确保我们知道 b 的哪一端是哪一端。 It would work equally well swapping the treatment of b for a.将 b 的处理换成 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.我启动了一个 ipython 笔记本来测试性能。 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.基于在 -100 到 100 范围内随机生成的间隔样本,初始帖子中的第一种方法实际上更快。我通过 1000 次比较与 527 次比较,在每个循环中以 827 微秒进行比较。

Unfortunately, the testing showed that the first method in the post fails.不幸的是,测试表明帖子中的第一种方法失败了。

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

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

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

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

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.建议的解决方案失败了 24 种可能情况中的 5 种。 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.在以下代码中,将建议的函数传递给 test_intersection。 It will throw an exception if even one of the 24 possible cases fails.如果 24 种可能情况之一失败,它将抛出异常。 My solution, and the modification proposed using a tuple internally, pass all 24. The original solution fails 5 of the cases.我的解决方案,以及在内部使用元组提出的修改,通过了所有 24。原始解决方案失败了 5 个案例。 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. ([3,4], [3,7]), ([3,7], [3,7]), ([4,7], [3,7]) 以及“行的间隔”是向后的。

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])

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM