物体之间的最短距离

时间:2019-07-22 10:30:33

标签: javascript algorithm

说我有一个这样的对象:

let objs = { obj1: [ 0, 10 ], obj2: [ 3, 9 ], obj3: [ 5, 12, 14 ] }

每个obj都有多个距离点,但只能选择一个与其他obj的距离点组合。

我可以根据以上距离点以12种方式组合三个对象。

例如,它可能变为[0,3,5]; 在这种情况下,三个对象之间的总距离为5-0,即5;

或者它可以变成[10,9,5],距离为10-5 = 5;

组合也可以是[0,3,12],距离为12-0 = 12;

我打算实现的目标是找到最短的组合,在这种情况下应为[10,9,12];距离是12-9 = 3;

因此,我想到了一个接一个地执行组合;我可以做嵌套循环来做到这一点,但是效率很低。 我可以用来实现此目的的最有效方法是什么?

3 个答案:

答案 0 :(得分:3)

为obj中的每个值v创建对(v,o),然后按对的第一个元素对所有对进行排序。需要O(n log n)

现在,您要从每个o中至少一个位于其中的排序顺序中选择一些连续的元素。您可以在O(n log n)(或使用散列图O(n))中选择最佳答案

首先选择满足条件的序列的最小前缀。创建两个指针,即连续选择的元素的开始和结束。

开始= 1,结束= x(其中x是每个对象在所选集合内的最小值)

然后尝试增加起点(通过删除所选子序列的第一个元素),并在需要时增加终点。 尽量减少最终值和起始值之间的差异,这就是您的答案。

正如我所说,要跟踪是否所有对象都在其中,请创建一个map / hash map,您可以在其中为每个对象存储按选定顺序排列的对象数量。 当增加开始时,必须减少子序列内某些对象的数量。然后,应增加结束率,直到每个对象在所选子序列中出现的次数不为零。 (要获得O(n log n)复杂度,请存储多少个对象的值为0。增加任何对象时,请减少计数器;将任何对象的数量减少至0时,请增加计数器)

结果始终正确,不使用启发式方法。

答案 1 :(得分:2)

您可以得到笛卡尔乘积,然后通过选择最大值和最小值的增量较小的数组来简化数组。

let objects = { obj1: [ 0, 10 ], obj2: [ 3, 9 ], obj3: [ 5, 12, 14 ] },
    data = Object.values(objects),
    cartesian = data.reduce((a, b) => a.reduce((r, v) => r.concat(b.map(w => [].concat(v, w))), [])),
    result = cartesian.reduce((a, b) => Math.max(...a) - Math.min(...a) < Math.max(...b) - Math.min(...b) ? a : b)

console.log(result);
console.log(cartesian.map(a => a.join(' ')));
.as-console-wrapper { max-height: 100% !important; top: 0; }

一种更快的方法是使用排序数组,并仅按排序顺序获取每个数组的最后三项。

这是不使用笛卡尔积的线性搜索。

index  values                delta
-----  --------------------  -----
   0    0          10 
   1       3     9
   2          5       12 14
       --------                 5
       --    -----              9
             --------           5
                --------        3 <-- min
                ------   --     5

let objects = { obj1: [ 0, 10 ], obj2: [ 3, 9 ], obj3: [ 5, 12, 14 ] },
    data = Object
        .values(objects)
        .map((a, i) => a.map(v => [v, i]))
        .reduce((a, b) => a.concat(b))
        .sort((a, b) => a[0] - b[0] || a[1] - b[1]),
    parts = [undefined, undefined, undefined],
    result,
    i;

for (i = 0; i < data.length; i++) {
    parts[data[i][1]] = data[i][0];
    if (parts.some(v => v === undefined)) continue;
    if (!result || Math.max(...parts) - Math.min(...parts) < Math.max(...result) - Math.min(...result)) {
        result = parts.slice();
    }
}

console.log(result);
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }

答案 2 :(得分:2)

我认为没有什么比嵌套/递归循环更好的了,在最坏的情况下,您仍然需要检查所有组合。但是,我认为应用branch-and-bound algorithm会带来很好的优化效果,因为它会较早修剪距离不太好的分支-还利用了每个对象的点已排序的事实,因此您可以修剪多个选择一次。

实际上,对于每个对象,我们只能修剪到到目前为止所选择的间隔中最接近的下方和最下方的2个选择。因此,我估计了O(k * (2+log k)n)(给定的n对象平均有k个已经排序的点)的最坏情况复杂度,而不是天真的枚举的O(kn)。 / p>

诚然,这比Nina的第二种方法的O(kn log(kn) + kn²)或Maras算法的O(kn log(kn))差-通过使用n-way merge可以将其进一步改进为O(kn log n)在已经排序的列表上。