【问题标题】:Difference of Ranges范围的差异
【发布时间】:2014-03-09 10:20:33
【问题描述】:

如果我有以下数组,每个元素代表整数范围对:

var a = [[0, 47], [50, 51], [53, 53], [55, 55], [58, 97], [101, 101], [103, 1114111]];
var b = [[48, 57], [97, 102]];

我正在使用this code 计算交集

var output = [];

for (var i = 0; i < a.length; ++i) {
    for (var j = 0; j < b.length; ++j) {
        var intersection = [
        Math.max(a[i][0], b[j][0]),
        Math.min(a[i][1], b[j][1]), ];

        if (intersection[0] <= intersection[1]) {
            output.push(intersection)
        }
    }
}

console.log(JSON.stringify(output));

[ [ 50, 51 ], [ 53, 53 ], [ 55, 55 ], [ 97, 97 ], [ 101, 101 ] ]

我还需要计算差异(所有值 0..1114111,除了上面相交的范围)。

这样做的有效方法是什么?

【问题讨论】:

  • 你试过那个代码了吗?发生了什么/问题出在哪里?您期待什么结果?
  • @Xotic750 它是 O(n^2),其中 O(n log n) 是可能的。
  • 你需要明确你需要的算法应该做什么。请不要包含解决不同问题的代码?这很混乱
  • @Alix 不,您不能在 O (n log n) 中进行交叉交叉,因为输出可能大于此值。目前尚不清楚您所说的“差异”是什么意思。你的意思是all-to-all在这里吗?我在问 bc “我怎样才能找到不拦截任何数组的所有范围?”听起来完全不同,与您的代码示例完全无关
  • @Alix:“所有值 0..1114111 除了上面相交的范围”是微不足道的,只需遍历间隔列表(l_1, r_1), (l_2, r_2), ... 并输出(r_1, l_2), (r_2, l_3), ...。你会问这个让我很困惑。您遇到的问题真的不同吗?

标签: javascript algorithm set set-intersection set-difference


【解决方案1】:

观察:“除了上面相交的范围之外的所有值 0..1114111”实际上是微不足道的,您只需遍历交叉点并输出其补码(将端点连接到以下间隔的起点)。

所以你的问题减少到更快地找到交叉点。使用扫描线算法:

  1. 创建事件列表(t, x, y),其中t 是区间的边界点,如果区间来自ax 为1,如果来自b,则为2。 y 如果边界点是起点,则为 1;如果边界点为终点,则为 -1。
  2. 按字典顺序按(t, -y) 排序
  3. 设置count[1] = count[2] = 0
  4. 遍历事件点。更新count[x] += y

现在结果是count[1] &gt; 0count[2] &gt; 0 同时存在的范围。

复杂度为O(n log n)。这是一个代码示例:http://jsfiddle.net/QA5FY/14/ 感谢用户 Xotic750 提供基本实现。

Javascript

var a = [
    [0, 47],
    [50, 51],
    [53, 53],
    [55, 55],
    [58, 97],
    [101, 101],
    [103, 1114111]
];

var b = [
    [48, 57],
    [97, 102]
];

var evts = [];

function add(arr, x) {
    arr.forEach(function (pair) {
        evts.push({
            t: pair[0],
            x: x,
            y: 1
        }, {
            t: pair[1],
            x: x,
            y: -1
        });
    });
}

add(a, 0);
add(b, 1);

evts.sort(function (a, b) {
    return (a.t != b.t) ? (a.t - b.t) : (b.y - a.y);
});

var last = -1;
var count = [0, 0];
var res = [];

for (var i = 0; i < evts.length; ++i) {
    count[evts[i].x] += evts[i].y;
    if (count[evts[i].x] === 1 && count[evts[i].x ^ 1] > 0) last = i;
    else if (count[0] === 0 || count[1] === 0) {
        if (last >= 0) res.push([evts[last].t, evts[i].t]);
        last = -1;
    }
}

res.forEach(function (pair) {
    console.log(pair[0] + " " + pair[1]);
});

输出

50 51
53 53
55 55
97 97
101 101 

【讨论】:

  • 啊,我正盯着这个试图了解ab 的元素是否在同一个t 中。您刚刚所做的编辑很有意义,我会在几个小时内尝试一下。 #2 也不应该按x 排序吗?
  • @Alix:起初我陈述了一种算法来计算 a 和 b 的并集的补码。现在这是一个计算 a 和 b 交集的算法。不,您不需要按x 排序,它只是标记间隔的来源。您按-y 排序以防止零长度间隔,但这不是正确性所必需的,您也可以按t 排序
  • 顺便说一句,当我说所有值 0..1114111 时,我忽略了它。实际上应该是所有值都定义在范围内,所以在这种情况下,我认为它是相同的,但如果 a 的最后一个元素是 200, 1114111,例如它会有所不同。
  • @AlixAxel:当然,我的意思是计算非重叠区间列表的补码非常简单
  • @Niklas B. 我是否正确理解了您的伪代码? jsFiddle
猜你喜欢
  • 1970-01-01
  • 2013-04-12
  • 1970-01-01
  • 2019-12-27
  • 1970-01-01
  • 2016-01-14
  • 2019-02-25
  • 2016-05-05
  • 2011-11-15
相关资源
最近更新 更多