比较数组中每个项目与数组其余部分的最快方法?

时间:2019-02-13 21:49:16

标签: javascript arrays algorithm for-loop

我有一个项目数组,对于该数组中的每个项目,我需要对同一数组中的其余项目进行一些检查。

这是我正在使用的代码:

const myArray = [ ...some stuff ];

let currentItem;
let nextItem;
for (let i = 0; i < myArray.length; i++) {
  currentItem = myArray[i];
  for (let j = i + 1; j < myArray.length; j++) {
    nextItem = myArray[j];
    doSomeComparision(currentItem, nextItem);
  }
}

虽然可行,但我需要找到一种更有效的算法,因为如果数组很大,它会大大降低速度。

有人可以提供一些有关如何使该算法更好的建议吗?

编辑1

很抱歉。

我本应该在此提供更多的背景信息。 我正在将以上循环与HalfEdge数据结构(也称为DCEL)一起使用。

HalfEdge基本上是具有3个属性的对象:

class HalfEdge = {
  head: // some (x,y,z) coords
  tail: // some (x,y,z) coords
  twin: // reference to another HalfEdge
}

给定twin中的HalfEdge的定义如下:

/**
  * if two Half-Edges are twins:
  * Edge A   TAIL ----> HEAD
  *           =          =
  * Edge B   HEAD <---- TAIL
  */

我的数组包含许多HalfEdges,对于数组中的每个HalfEdge,我想找到它的孪生子(即满足上述条件的孪生子)。

基本上,我正在比较两个3D向量(一个来自currentItem,另一个来自nextItem)。

编辑2

代码示例中的固定错字(即从let j = 0let j = i + 1

4 个答案:

答案 0 :(得分:1)

在不了解有关项目类型的更多信息的情况下

1)您应该首先对数组进行排序,然后才可以进行比较,然后复杂度应为o(log n)+ n ^ 2,这可能取决于项的类型并可能带来更多改进。

2)从i + 1开始内部循环应将其进一步减小为o(log n + n)

const myArray = [ ...some stuff ].sort((a,b) => sortingComparison(a,b)); // sorting comparison must return a number

let currentItem;
let nextItem;
for (let i = 0; i < myArray.length; i++) {
  currentItem = myArray[i];
  for (let j = i + 1; j < myArray.length; j++) {
    nextItem = myArray[j];
    doSomeComparision(currentItem, nextItem);
  }
}

奖金: 这是一些花哨的功能代码(如果您希望获得原始性能,则for循环版本会更快)

function compare(value, array) {
  array.forEach((nextValue) => {
    // Do your coparisson here
    // nextValue === value
  }
}

const myArray = [items]
myArray
  .sort((a,b) => (a-b))
  .forEach((v, idx) => compare(v, myArray.slice(idx, myArray.length))

答案 1 :(得分:1)

我不知道是否有任何一种特定的算法更有效,但是我立即想到以下优化:

  • 让j以i + 1开头-否则,您将所有项目进行两次比较 互相对抗
    -使用myArray.length外部初始化变量 相同的操作循环执行两次。
  • 如果比较 是任何直接的“等于/更大”类型,则可以帮助对 首先排列

在“编辑1”上更新
我认为优化取决于预期的匹配次数。也就是说,如果所有HalfEdge对象都具有孪生子,那么我认为您采用上述更改的当前方法已经非常理想。 但是,如果预期双胞胎的比例很低,那么我建议以下几点: -提取所有头部的列表和所有末端的列表,对它们进行排序,然后相互比较。记住哪些头上有双尾巴。 然后,再次执行原始循环,但仅输入找到匹配项的磁头的内部循环。 不确定这是否是最佳选择,但我希望您能采用我的方法。

答案 2 :(得分:1)

这是您的问题的线性时间解决方案。我对javascript不太熟悉,因此我会以伪代码正确地给您算法感到更舒服。

lookup := hashtable()
for i .. myArray.length
  twin_id := lookup[myArray[i].tail, myArray[i].head]
  if twin_id != null
    myArray[i].twin := twin_id
    myArray[twin_id].twin := i
  else
    lookup[myArray[i].head, myArray[i].tail] = i

这个想法是构造一个(head, tail)对的哈希表,并检查是否已经存在一个与当前节点匹配的(tail, head)对。如果是这样,则它们是双胞胎,并标记为双胞胎,否则用新条目更新哈希表。每个元素都只循环一次,并且从哈希表中进行插入/检索的时间是固定的。

答案 3 :(得分:0)

由于值是3D坐标,因此请建立八叉树(O(N))并在其HEAD值上添加项目。然后使用它们已经建立的八叉树(O(N k log(N)))从每个节点中将其跟随到其TAIL值,其节点包含最多k个边,这意味着最低的k个比较每个TAIL的级别节点。另外,找到每个TAIL可能需要从上到下移动到八叉树的log(N)级。

具有建筑物八叉树常数的O(N)+ O(N * k * log(N)),每个节点具有足够低的k条边(以及八叉树的logN级别)。

当您在八叉树中遵循TAIL时,任何具有相同值的HEAD都将位于具有最多k个元素的同一节点中,或者任何“足够接近”的HEAD值都将位于该最低级别的节点及其最近的邻居中。

您要查找精确的HEAD == TAIL还是使用一些公差?宽容可能需要“宽松的八叉树” imo。

如果每个边都有定义的长度,那么如果两个边都是对称的,则可以通过此值限制搜索半径。

对于最多5k-10k的边缘,八叉树中可能只有5-10个级别,具体取决于每个节点的边缘限制,如果将此限制选为2-4,则每个HEAD只需要做10-40操作以找到具有相同TAIL值的双边。