【问题标题】:Fastest way to compare each item in array with rest of array?将数组中的每个项目与数组的其余部分进行比较的最快方法?
【发布时间】:2019-02-13 21:49:15
【问题描述】:

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

这是我正在使用的代码:

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
}

给定HalfEdgetwin 定义如下:

/**
  * 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

【问题讨论】:

  • 这可能更适合Code Review
  • 您能否添加更多关于您要比较的项目的信息,如果没有更多信息,您将无法远离通用比较
  • 如果你的比较是对称的 - 即 doSomeComparision(a, b) === doSomeComparision(b, a),你可以通过设置 j = i + 1 - for (let j = i + 1; j &lt; myArray.length; j++) { `
  • @devlincarnate 此代码可能是 CR 上的 on-topic,但 myArray 需要正确定义,以便代码可以运行,并且需要添加对这段代码作用的更好解释

标签: javascript arrays algorithm for-loop


【解决方案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) 对。如果是这样,它们是双胞胎,并将它们标记为这样,否则使用新条目更新哈希表。每个元素只循环一次,并且从哈希表中插入/检索是在恒定时间内完成的。

【讨论】:

  • 我尝试了这种方法,效果很好!谢谢。
【解决方案2】:

不知道有没有什么更高效的具体算法,但我马上想到了以下优化:

  • 让 j 以 i+1 开头 - 否则您将所有项目比较两次 相互对抗
    - 使用外部 myArray.length 初始化变量 循环作为相同的操作进行两次。 罢工>
  • 如果比较 是任何一种直接的“相等/更大”,那么它可以帮助对 数组优先

编辑 1 更新
我认为优化取决于预期匹配的数量。即,如果所有 HalfEdge 对象都有一个双胞胎,那么我认为您当前采用上述更改的方法已经非常理想。 但是,如果预期双胞胎的百分比相当低,那么我建议如下: - 提取所有头的列表和所有尾的列表,对它们进行排序,并相互比较。记住哪些头找到了双尾。 然后,您是否再次进行原始循环,但只输入找到匹配的头部的内部循环。 不确定这是最优的,但我希望你能得到我的方法。

【讨论】:

  • myArray.length 未计算正确吗?它是一个属性,因此在性能方面它应该不会产生太大影响
  • 感谢您的回复!实际上,我已经从 j + 1 开始了 j。在我的代码示例中完全错过了这一点,这很糟糕。关于首先对数组进行排序,当我基本上比较两个 3D 向量时,您认为我还应该对数组进行排序吗?
【解决方案3】:

在不了解物品类型的更多信息的情况下

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))

【讨论】:

  • 感谢您的回复!正如对下面其他回复的评论,j = 0 是我的错字。我基本上是在一天结束时比较两个 3D 向量,你认为我还应该进行某种排序吗?
  • 3d 向量是什么意思?你能举个例子吗?
【解决方案4】:

由于值是 3D 坐标,因此构建一个八叉树 ( O(N) ) 并在其 HEAD 值上添加项目。然后从他们每个人开始,使用已经构建的八叉树( O(Nklog(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 值的孪生边。

【讨论】:

    猜你喜欢
    • 2020-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-21
    • 2019-02-26
    • 2016-02-17
    相关资源
    最近更新 更多