【问题标题】:Python program to detect intersection of one-dimensional line segmentsPython程序检测一维线段的交点
【发布时间】:2015-08-26 01:29:15
【问题描述】:

我有四种颜色的线段——粉色、绿色、橙色、红色——如下图所示。

例如,第一个粉红色段的开始和结束位置为(5258,5422)

坐标存储在这个元组字典中:

mycoord = { 'pink'  :[(5258,5422), (5479,5864)],
            'green' :[(5425,5450)],
            'orange':[(5266,5770)],
            'red'   :[(5258,5864)] }

我要做的是获取所有可能的交叉点的开始和结束值,如下图所示:

因此,期望的答案是:

sect1/pink-red        : 5258,5266
sect2/pink-orange-red : 5266,5422
sect3/orange-red      : 5422,5425
sect4/green-orange-red: 5425,5450
sect5/orange-red      : 5450,5479
sect6/pink-orange-red : 5479,5770
sect7/pink-red        : 5770,5864

请注意,我想为每个交叉点保留颜色指示器(例如, pink-red)。如何使用 Python 实现这一点?

【问题讨论】:

  • 快速猜测:将值展平并排序?
  • 展平和排序可能会丢失颜色信息;你需要像numpy的argsort这样的东西。 Neversaint:你已经尝试过什么了吗?你可以看看 Python 的集合,并使用交集;也许这可以帮助您入门。
  • @neversaint 您接受了一个当有多个相同颜色的重叠段时不起作用的答案。我的答案适用于所有情况。
  • @MichaelLaszlo:好的。我接受了你的。

标签: python algorithm graph


【解决方案1】:

我建议您按照以下方式进行。

  • 对端点进行排序,记住每个端点的颜色以及它是左(打开)还是右(关闭)端点。

  • 遍历端点,使用散列跟踪开放跨度,该哈希将每种颜色映射到该颜色的开放跨度数。打开给定颜色的跨度时增加,关闭跨度时减少。当它们的计数达到零时删除颜色。对于每个不同的端点,将该点的所有开放跨度的颜色放入一个集合中。

  • 迭代连续的不同端点对。这些构成了您感兴趣的跨度的左右端点。对于每个端点,您都知道当时处于活动状态的颜色。在跨度期间处于活动状态的颜色集是在左端处于活动状态的颜色和在右端处于活动状态的颜色中的set intersection

注意:如果两个端点之间的颜色交集是空的,那么您已经发现跨度之间存在间隙,因此您知道应该跳过它。您可能还想跳过只有一种颜色的跨度。下面的实现没有。您可以通过修改此行轻松地将其更改为跳过单色跨度:

  if len(colors) > 0:

这样写:

  if len(colors) > 1:

如果您有兴趣查看跨度之间的差距,可以将阈值更改为 -1 或完全删除条件。

实施:

mycoord = { 'pink'  :[(5258,5422), (5479,5864)],
            'green' :[(5425,5450)],
            'orange':[(5266,5770)],
            'red'   :[(5258,5864)] }

# Sort the endpoints. Remember their color and whether they open or close.
points = []
for color, spans in mycoord.items():
  for open, close in spans:
    points.append((open, 'open', color))
    points.append((close, 'close', color))
points.sort()

# Iterate over the endpoints. Keep track of open spans. Mark intersections.
active_spans = {}
intersections = []
for point, kind, color in points:
  if len(intersections) != 0 and intersections[-1][0] == point:
    intersections[-1][1].add(color)
  else:
    color_set = set([color] + list(active_spans.keys()))
    intersections.append((point, color_set))
  if kind == 'close':
    active_spans[color] -= 1
    if active_spans[color] == 0:
      del active_spans[color]
  else:
    active_spans[color] = active_spans.setdefault(color, 0) + 1

# Iterate over consecutive pairs of unique intersections. Intersect the color sets.
tab_width = sum(map(len, mycoord)) + len(mycoord) 
count = 0
for i in range(1, len(intersections)):
  a, b = intersections[i - 1], intersections[i]
  colors = sorted(a[1] & b[1])
  if len(colors) > 0:
    count += 1
    print('sect{0}/{1:<{2}}: {3},{4}'.format(count, '-'.join(colors), tab_width,
        a[0], b[0]))

结果:

sect1/pink-red              : 5258,5266
sect2/orange-pink-red       : 5266,5422
sect3/orange-red            : 5422,5425
sect4/green-orange-red      : 5425,5450
sect5/orange-red            : 5450,5479
sect6/orange-pink-red       : 5479,5770
sect7/pink-red              : 5770,5864

【讨论】:

    【解决方案2】:

    使用上面 Michael Laszlo 的大括号打开/关闭思想:

    >>> mycoord = { 'pink'  :[(5258,5422), (5479,5864)],
                'green' :[(5425,5450)],
                'orange':[(5266,5770)],
                'red'   :[(5258,5864)] }
    >>> labeled_values=[]
    # make tuples of (value, brace status (open/close), color)
    >>> for color,color_ranges in mycoord.items():
            for color_range in color_ranges:
                labeled_values.append((color_range[0],True,color))
                labeled_values.append((color_range[1],False,color))
    
        # labeled_values are now like (5258, True, 'pink'), (5422, False, 'pink') ...
    >>> sects = []
    # traverse the sorted values and maintain a color-set
    >>> color_set_so_far=set()
    >>> range_start = -1
    >>> for value,range_open,color in sorted(labeled_values):   
            if not range_open or range_start != value:
                sects.append(("-".join(color_set_so_far), range_start, value))
    
            if range_open:          
                color_set_so_far.add(color)
            else:       
                color_set_so_far.remove(color)      
    
            range_start = value
    
    >>> sects = [s for s in sects if s[0] and s[1]!=s[2]] # filter out empty ranges
    >>> sects
    # [('pink-red', 5258, 5266), ('pink-orange-red', 5266, 5422), ('orange-red', 5422, 5425), ('orange-green-red', 5425, 5450), ('orange-red', 5450, 5479), ('pink-orange-red', 5479, 5770), ('pink-red', 5770, 5864)]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-11-14
      • 2011-01-11
      • 2020-05-03
      • 1970-01-01
      • 1970-01-01
      • 2015-07-03
      • 2021-10-29
      相关资源
      最近更新 更多