找到范围的最大交叉子集

时间:2013-02-21 22:54:41

标签: algorithm language-agnostic graph-algorithm set range

如果您有一组范围,例如以下简单示例...

[
    [12, 25], #1
    [14, 27], #2
    [15, 22], #3
    [17, 21], #4
    [20, 65], #5
    [62, 70], #6
    [64, 80]  #7
]

...你如何计算最大交叉子集(不确定如何对它进行短语,但我的意思是“相交且具有最高基数的范围的子集”)并确定交叉度(该子集中范围的基数)?

逻辑上我可以解决它,并且可能能够将其转换为天真的算法。沿着列表,我们看到1-5相交,5-7相交,#5与两组相交。

我想要的结果只是子集,因为它给了我关于基数的信息,只要它们都相交,我就可以轻松地计算集合的交集。在上面的示例中,它将是[[14, 27],[15, 22],[12, 25],[17, 21],[20, 65]]

在我的脑海中,我可能会尝试将每个范围转换为图形节点,连接相交的图形节点,并找到最大的完全连接图形。

我也在思考迭代地从头开始,继续建立一个交叉范围列表,每个交叉范围都有一个运行的交叉点来检查 - 直到你碰到一个不相交的元素,然后开始一个新的列表。继续针对现有交叉点检查每个项目。但是我不确定这是完整的。

我可以尝试实施某些东西(lang是ruby FWIW),但我很想知道其他人如何解决这个问题,以及最有效和最优雅的方式。

更新

我认为这是最大集团问题的一个特例,它是NP难的,因此实际上很难。我们非常感谢您对近似/实际使用的建议!

另请参阅:http://en.wikipedia.org/wiki/Maximum_clique / Find all complete sub-graphs within a graph

更新2

在这里找到了这个问题的NP-硬度和NP-完整性的一个很好的证明:http://www.cs.bris.ac.uk/~popa/ipl.pdf

看起来这就是行尾。对不起人!我将使用足够好的贪婪近似。感谢。

如答案所述,我不认为该论文描述了这个问题......我们可能会根据范围获得更多信息。

2 个答案:

答案 0 :(得分:11)

如果我正确理解了问题,那么它不是您链接到的论文中描述的NP问题的实例。以下是我对该问题的理解,以及多项式时间解决方案。

  1. 给出一组有限的实数范围,比如n:[A1,B1],[A2,B2],...,[An,Bn],其中Ai <= Bi。

  2. 创建一个起始点和结束点的排序列表,以数字顺序排列,指示该点是起点还是终点。

  3. 在您的示例中,这将是:12 +,14 +,15 +,17 +,20 +,21-,22-,25-,27-,62 +,64 +,65-,70-, 80 -

    1. 将curOverlap和maxOverlap初始化为零。

    2. 遍历列表,为每个+递增curOverlap,并为每个递减 - 。在每个增量上设置maxOverlap = max(curOverlap,maxOverlap)。

    3. 继续你的榜样:
          val,cur,max
          12,1,1     14,2,2     15,3,3     17,1,4,     20,5,5     21,4,5     22,3,5     25,2,5     27,1,5     62,2,5     64,3,5     65,2,5     70,1,5     80,0,5

      最大重叠为5.如果您想知道最大重叠发生的位置,您还可以存储与max关联的val。在这个例子中,这将给你20.然后,通过初始的范围集找到包括20的范围,这是微不足道的。

      -edit-如果您有重复的值,请计算每个值的最小值之前的加号,以便包括在单个点重叠的范围。

答案 1 :(得分:2)

这是Dave提出的解决方案的Java实现:

public static List<Range> getMaxIntersectingSubset(List<Range> ranges) {
    // First, we collect "a sorted list of the starting and ending points".
    // We're sorting such that all lower bounds come first
    List<Bound> intersections = Arrays.stream(ranges)
        .flatMap(arr -> Stream.of(Bound.ofLowerBound(arr[0]), Bound.ofUpperBound(arr[1])))
        .sorted(Comparator
            .comparing(Bound::value)
            .thenComparing((a, b) -> Boolean.compare(!a.isLowerBound(), b.isLowerBound())))
        )
        .collect(Collectors.toList());

    long curOverlap = 0;
    long maxOverlap = 0;
    List<Integer> indexes = new ArrayList<>();

    // Then we iterate the list, searching for the highest overlap
    for (int i = 0; i < intersections.size(); i++) {
        Bound intersection = intersections.get(i);
        curOverlap += (intersection.isLowerBound() ? 1 : -1);
        if (curOverlap > maxOverlap) {
            maxOverlap = curOverlap;
            indexes.clear();
            indexes.add(i);
        }
        else if (curOverlap == maxOverlap) {
            indexes.add(i);
        }
    }

    return indexes.stream()
        .map(index -> Range.of(intersections.get(index).value(), intersections.get(index + 1).value()))
        .collect(Collectors.toList());
}
public class Bound {

    private final int value;
    private final boolean lower;

    private Bound(int value, boolean lowerbound) {
        this.value = value;
        this.lower = lowerbound;
    }

    public static Bound ofLowerBound(int value) {
        return new Bound(value, true);
    }

    public static Bound ofUpperBound(int value) {
        return new Bound(value, false);
    }

    public int value() {
        return this.value;
    }

    public boolean isLowerBound() {
        return this.lower;
    }
}
public class Range {

    private final int start;
    private final int end;

    private Range(int start, int end) {
        if (start > end) {
            throw new IllegalArgumentException("start > end");
        }
        this.start = start;
        this.end = end;
    }

    public static Range of(int start, int end) {
        return new Range(start, end);
    }

    public int start() {
        return this.start;
    }

    public int end() {
        return this.end;
    }
}

这些是一些测试集:

  1. 来自this Stackoverflow question

    int[][] ints = {
        { 1, 100 },
        { 30, 95 },
        { 10, 60 },
        { 15, 25 },
        { 33, 66 },
        { 20, 50 },
        { 51, 100 },
        { 25, 70 }
    };
    

    根据上述问题的OP,结果应为[ 30, 55 ]。提供的代码输出此结果。但是,还有另一处重叠,尽管宽度较小。参见下面的图片。

    Test case #1a

    Test case #1b

  2. 此问题的OP提到了此列表:

    int[][] ints = {
        { 12, 25 },
        { 14, 27 },
        { 15, 22 },
        { 17, 21 },
        { 20, 65 },
        { 62, 70 },
        { 64, 80 }
    };
    

    在这里,输出为[ 20, 21 ]。这与下图匹配:

    Diagram of test case #2