JS:快速列表交集算法

时间:2020-09-21 07:05:42

标签: javascript list algorithm intersection

编辑:由于@Bergi和@MattEllen的先前评论,我的思想有所进步,因此我重新发布了这个问题。

假设我们有两个具有重复值的排序列表

// here arr1.length = arr2.length = 16
ar11 = 0 0 0 0 1 1 1 1 2 2 2 2 3 3 4 5 
arr2 = 0 0 1 1 1 1 2 2 2 2 3 4 5 6 7 8

我们要计算它们的交点(isect)

// duplicates preserved !
isect = 0 0 1 1 1 1 2 2 2 2 3 4 5

我们可以使用根据Intersection of two lists including duplicates?

中所述的Python版本改编的简单线性算法
// basic intersection code
// allows lists to handle duplicates
const dumb_intersect = (arrays, intervals, params) => {
    const { arr1, arr2, results }  = arrays
    const { matches} = params
    const { i1, i2 } = intervals

    ii_iterate(arr1, i1, (value, rank) => {    
        const count = matches.get(value) || 0
        matches.set(value, count + 1)
    })

    ii_iterate(arr2, i2, (value, rank) => {
        const count = matches.get(value) || 0
        if(count > 0) {
           results.push(value)
           if (count > 1) {
                matches.set(value, count - 1)
           } else {
                matches.delete(value)
           }
       }
    })
}

在此示例中:

  • matches是一个全局映射,可以在以后的dumb_intersect调用中重复使用
  • i1 = {最小:0,最大:15}和{最小:0,最大:15}(显而易见!)
  • ii_iterate获取一个列表和一个间隔,并在其索引绑定到该间隔时对列表的每个元素应用回调。

为简单起见,我给您ii_iterate的代码:

// apply a callback function to each element of a slice of an array
const ii_iterate = (arr, ii, callback) => {
    return arr.slice(ii.min, ii.max + 1).map((val, idx) => {

        return callback(val, idx)
    })
}

TL:DR; ;-)

所有这些东西都是很棒的,但是我认为我可以做得更好,更快!

特别是如果我们对间隔的割除进行二进位(二分频)的序列,直到阈值:

// THRESOLD = 4 elements
cuts[0]: [0..15]                            # 1 interval * 16 elements
cuts[1]: [0..7] [8..15]                     # 2 * 8
cuts[2]: [0..3] [4..7] [8..11] [12..15]     # 4 * 4

...并仅应用重叠间隔的dumb_intersect

通常,对于庞大的列表,阈值的计算很容易:

thresold = 1 + Math.floor(Math.log((1 + arr1.length) * (1 + arr2.length)))

N = 1000    thresold = 15
N = 1000000 thresold = 28

但这是问题的次要方面。 Bianry拆分过程也很容易做到。初始示例的Arr1和Arr2变为阈值4:

ar11 = 0 0 0 0; 1 1 1 1; 2 2 2 2; 3 3 4 5
arr2 = 0 0 1 1; 1 1 2 2; 2 2 3 4; 5 6 7 8

(请记住那可能是大列表)

这个问题似乎是两个间隔列表之间的“乌龟拉力赛车”:

  • 我需要一些游标之王才能迭代列表
  • 然后比较INF_STRICT,SUP_STR或MATCHING的时间间隔
  • 将dumb_intersect应用于匹配间隔
  • 直到它结束

由于dumb_intersect的复杂度为线性O(N),所以我今天不确定可以在O(log(N))或O(sqrt(N))中进行优化->因此,我将仅使用线性将来的编码版本!!

谢谢大家。

1 个答案:

答案 0 :(得分:0)

还请注意,arr1和arr2是预先排序的列表,具有升序。

在这种情况下,分割间隔的所有麻烦是什么?我们只需要一个merge algorithm即可做到:

function intersection(arrays, intervals) {
    const { arr1, arr2, results } = arrays
    const { i1, i2 } = intervals

    let i = i1.min, j = i2.min;
    while (i <= i1.max && j <= i2.max) {
        if (arr1[i] < arr2[j]) {
            i++
        } else if (arr1[i] > arr2[j]) {
            j++
        } else { // arr1[i] == arr2[j]
            results.push(arr1[i])
            i++
            j++
        }   
    }
}