我在这里有一个相当基本的问题。我想找出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
答案 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])