Algorithm 最大化覆盖率、最小化项目使用率的算法?

Algorithm 最大化覆盖率、最小化项目使用率的算法?,algorithm,Algorithm,我有一个场景,你可以这样设想: 从宽100像素、高1000像素的图像开始。 除了该图像之外,还有一组该图像的裁剪部分。每个部分宽100像素,高100像素。节中包含的图像部分各不相同。例如,可能有一个从最顶端(像素0)开始,然后一个从垂直像素3开始,然后一个从垂直像素9开始,依此类推 我需要做的是找到一种方法来查看这些较小的图片集,并挑选出最小数量的部分,使我能够最大程度地覆盖原始图像 几点注意: 图像的内容其实并不重要。它真正匹配了重要的坐标 重建图像时,图像中永远不会有间隙,但可能没有足够的碎

我有一个场景,你可以这样设想:

从宽100像素、高1000像素的图像开始。 除了该图像之外,还有一组该图像的裁剪部分。每个部分宽100像素,高100像素。节中包含的图像部分各不相同。例如,可能有一个从最顶端(像素0)开始,然后一个从垂直像素3开始,然后一个从垂直像素9开始,依此类推

我需要做的是找到一种方法来查看这些较小的图片集,并挑选出最小数量的部分,使我能够最大程度地覆盖原始图像

几点注意:

  • 图像的内容其实并不重要。它真正匹配了重要的坐标
  • 重建图像时,图像中永远不会有间隙,但可能没有足够的碎片到达底部
  • 各部分之间会有很多重叠。事实上,在某些情况下,两个部分之间只有一个或两个(垂直)像素的差异
  • 有人能给我指一下正确的方向吗?我可以用这种蛮力。。。但是我认为有更好的方法。

    我认为这是--集合的元素是像素(您可以编写代码,这样它就不会逐像素处理事情)

    因为它是100x1000,所以这个问题不再是NP难问题,甚至可能是p难问题。贪婪的方法是行不通的,但存在如下的动态规划解决方案,如果足够分散,它大致在
    O(N)
    时间内工作,否则
    O(N*max\u重叠)
    。诀窍是“向前和向后”

    这假设您将添加一个额外的正方形,即使覆盖率为额外的0.0001%,即“在每个点上,如果可以用正方形覆盖,则必须用正方形覆盖”。不过,您可以修改此算法以适当权衡

    这进一步假设,并非所有的正方形都在一个点上彼此重叠(它们有些分散,但仍可能重叠);否则可能需要很长时间


    还要注意的是,每当你有一个未用正方形填充的断点时,你可以将问题分成子问题。

    这一确切的问题包含在下面

    贪婪型算法将以最佳方式解决您的问题

    假设您希望首先覆盖尽可能多的非不相交区域,然后使用给定第一个约束的最少数量的部分,那么此算法将在O(n^2)时间内为您解决问题

    基本的想法是按照从上到下的顺序进行,只在你“裸体”的时候,也就是说,没有覆盖给定的部分时,才取一个部分。当你被迫选一个章节时,选一个最能涵盖你未来的章节。这个实现是O(n^2),但是您可以通过更好地管理CAND使其成为O(n log(n))

    #!/usr/bin/python
    
    START_EVENT, END_EVENT = 0, 1  # handle starts before ends at same point
    
    def max_future(cands):
      return max(cands, key=lambda c: (c[1], c)[1])
    
    def cover_max_segment_min(intervals):
      events = []
      for interval in intervals:
        events.append((interval[0], START_EVENT, interval))
        events.append((interval[1], END_EVENT, interval))
      cands = []
      outputs = []
      alive = None
      # Handle events by 
      #   event time, 
      #   starts before ends, 
      #   longer endings before shorter endings
      events.sort(key=lambda x: (x[0], x[1], -x[2][1]))
      for k, event_type, interval in events:
        if event_type == START_EVENT:
            cands.append(interval)
        if event_type == END_EVENT:
            cands.remove(interval)
            if interval is alive:
                alive = None
        if not alive and cands:
            outputs.append(max_future(cands))
            alive = outputs[-1]
      return outputs
    
    assert cover_max_segment_min([(0, 3), (1, 4), (3, 5)]) == \
       [(0, 3), (3, 5)]
    assert cover_max_segment_min([(0, 3), (3, 5), (1, 4)]) == \
       [(0, 3), (3, 5)]
    assert cover_max_segment_min([(0, 3)]) == [(0, 3)]
    assert cover_max_segment_min([]) == []
    assert cover_max_segment_min([(-10, 10), (1, 2), (3, 5)]) == [(-10, 10)]
    assert cover_max_segment_min([(1, 2), (2, 3), (3, 4)]) == \
       [(1, 2), (2, 3), (3, 4)]
    assert cover_max_segment_min([(1, 4), (1, 2), (3, 3)]) == \
       [(1, 4)]
    

    对不起,我不明白为什么这个问题是NP难的

    一般的想法是,您将通过选择“最佳”部分来迭代删除图像的底部部分,即

    • 覆盖图像底部的最大部分
    • 如果你找不到一个(因为没有一个部分覆盖最后一行像素),只需要最靠近底部的一个
    • 冲洗并重复
    首先对各部分进行排序。您将得到类似(0,1,3,10,…,988999)的结果,其中0对应于从顶部像素开始的部分。(与999相对应的仅包含一行)

    假设您的原始图像是100xN。最初,N=1000

    设n为最能覆盖原始图像末尾的图像索引:即n是该列表中的最小数字,使得n+100>=n。如果没有这样的数字,n就是最大的数字

    如果您的排序列表是(0,1,…899,900,901,…999),则n=900

    如果您的排序列表是(0,1,…899905910999),则n=905

    如果排序列表为(0,1,…,888898,),则n=898

    然后从N=N开始(您已经删除了原始图像底部的一部分)(当然,从排序列表中删除所有“>=N”的部分)


    我认为设置固定高度部分(100像素)会消除NP硬度。

    @Tim,裁剪的部分是重叠的还是唯一的?它们肯定会重叠。事实上,会有很多重叠,这就是为什么尽量减少使用的#部分是重要的。@Tim,酷,那么如果你采取贪婪的方法,使用重叠量最小的裁剪部分可以吗?这听起来非常类似于集合覆盖问题(),这是NP难问题。当然,它的结构比一般情况要复杂得多,因此如果存在一个有效的解决方案,我也不会感到太惊讶。另外,您对有界问题大小感兴趣,因此渐近性能可能不是问题。但要记住一点。找到一个减量是很有趣的…@Tim,大约有多少个100x100的截面是典型的?不幸的是,我不能相信这种说法能以最佳方式解决问题。这是一个NP难问题,即使我们只使用固定大小的正方形,也可能仍然是一个困难的问题。这并不是说不应该使用贪婪的方法或者不会产生好的结果。但不幸的是,声称它将以最佳方式解决问题是行不通的。例如,如前所述,这种特殊的贪婪算法将在以下情况下选取所有矩形:
    [A{](B}C)
    ,而不仅仅是A和C.3SAT所需的最小值是NP完全的。2 SAT是3SAT的子集。我可以给你一个2SAT的快速优化算法。我认为[(0,3)、(1,4)、(3,5)]示例与您的测试用例同构。我的psuedo代码有缺陷,我修复了它,它可以在您的测试用例上运行。这与我最终实现它的方式非常吻合
    #!/usr/bin/python
    
    START_EVENT, END_EVENT = 0, 1  # handle starts before ends at same point
    
    def max_future(cands):
      return max(cands, key=lambda c: (c[1], c)[1])
    
    def cover_max_segment_min(intervals):
      events = []
      for interval in intervals:
        events.append((interval[0], START_EVENT, interval))
        events.append((interval[1], END_EVENT, interval))
      cands = []
      outputs = []
      alive = None
      # Handle events by 
      #   event time, 
      #   starts before ends, 
      #   longer endings before shorter endings
      events.sort(key=lambda x: (x[0], x[1], -x[2][1]))
      for k, event_type, interval in events:
        if event_type == START_EVENT:
            cands.append(interval)
        if event_type == END_EVENT:
            cands.remove(interval)
            if interval is alive:
                alive = None
        if not alive and cands:
            outputs.append(max_future(cands))
            alive = outputs[-1]
      return outputs
    
    assert cover_max_segment_min([(0, 3), (1, 4), (3, 5)]) == \
       [(0, 3), (3, 5)]
    assert cover_max_segment_min([(0, 3), (3, 5), (1, 4)]) == \
       [(0, 3), (3, 5)]
    assert cover_max_segment_min([(0, 3)]) == [(0, 3)]
    assert cover_max_segment_min([]) == []
    assert cover_max_segment_min([(-10, 10), (1, 2), (3, 5)]) == [(-10, 10)]
    assert cover_max_segment_min([(1, 2), (2, 3), (3, 4)]) == \
       [(1, 2), (2, 3), (3, 4)]
    assert cover_max_segment_min([(1, 4), (1, 2), (3, 3)]) == \
       [(1, 4)]