python中起点和终点的两条线相交

时间:2015-03-13 23:14:00

标签: python

我在这里有一个相当基本的问题。我想找出1-D平面上的两条线是否相交。我知道有两种简单的方法可以解决这个问题,但我想知道Python是否有更优雅的方法来解决这个问题?

例如:

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

3 个答案:

答案 0 :(得分:2)

虽然本身并不以Python为中心,但这是一种解决问题的优雅方式 中心思想是,如果两个区间不完全不相交,那么它们必须相交,所以你所要做的就是检查那个条件。

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

输出:

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

答案 1 :(得分:1)

我还没有尝试衡量表现,但我认为这更加清晰,而且可能更快 - 它可能会更换两个额外的三元比较&#34;或者#34; #34;两次比较(最小和最大):

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

如果任一端在b内,则交叉b。在函数中,c只是确保我们知道b的哪一端是哪一个。它同样可以很好地交换b的处理。

测量性能需要运行一套所有可能的第二行规范的排列,以及两端交叉的选择,或者两者都没有。

从这里编辑。 我制作了一个ipython笔记本来测试性能。实际上,基于在-100到100范围内随机生成的间隔样本,初始帖子中的第一种方法更快。通过1000次比较,我们通过每次循环827微秒进行比较,而不是527。

不幸的是,测试表明帖子中的第一种方法失败了。

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

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

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

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

答案 2 :(得分:0)

这个问题比我想象的更有趣。对我来说,原始解决方案看起来太复杂了。我不相信复杂,所以我亲自尝试了一个解决方案,这个解决方案更简单,并且很容易证明是正确的。我应该把比较小于或等于,而不是小于。在比较两者的速度之后,我不小心,在仅有两个条件的测试中,发现原始解决方案存在缺陷。我还发现,虽然我的解决方案 - 刚刚提到的修正 - 比建议的解决方案慢。

建议的解决方案在24种可能的情况中失败了5次。我留给作者纠正他的功能。我甚至没有试图确定他的错误发生在哪里。但我提供以下功能,用于测试。

有些工具可以测试代码覆盖率。这个问题和解决方案很有意思,因为完全测试解决方案的覆盖范围需要小于粒度级别的行级别。

在以下代码中,将建议的函数传递给test_intersection。如果24个可能案例中的一个失败,它将抛出异常。我的解决方案,以及在内部使用元组提出的修改,都通过了所有24.原始解决方案在5个案例中失败。发布后,我意识到还有一些其他案例可以添加。 ([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])