【问题标题】:Understanding complexity of JavaScript algorithm using object as a hash了解使用对象作为哈希的 JavaScript 算法的复杂性
【发布时间】:2015-05-26 04:24:22
【问题描述】:

我正在尝试提出一种算法来解决以下问题。

给定一个 id 数组

var ids = [8098272281362432, 7824519999782912];

在一个项目数组中查找所有匹配项。

var people = [
    {
      "id": 8098272281362432,
      "age": 59,
      "name": "Douglas Hunter"
    }, 
    {
      "id": 625873891885056,
      "age": 1,
      "name": "Lottie Owen"
    }, 
    {
      "id": 7824519999782912,
      "age": 100,
      "name": "Maud Wise"
    }, 
    {
      "id": 2561552265773056,
      "age": 115,
      "name": "Annie Bennett"
    }
];

方法 1 我可以通过按id (O(n log n)) 对两个数组进行排序然后从上到下遍历这两个数组一次来解决这个问题 (O(n))

var alg1 = function(people, ids) {
  var matches = [];

  var sortedPeople = people.sort(function(a, b) {
    return a.id - b.id
  });
  var sortedIds = ids.sort(function(a, b) {
    return a - b
  });
  var curPersonIndex = 0;

  sortedIds.forEach(function(id) {
    while (sortedPeople[curPersonIndex].id !== id) {
      curPersonIndex++;
    }

    matches.push(sortedPeople[curPersonIndex]);
  });

  return matches;
};

方法 2 我虽然可以使用O(n) 算法来改进这一点,方法是创建 id 到人员的映射,然后我可以为每个 id 查找人员。

var alg2 = function(people, ids) {
  var matches = [];

  peopleMap = {};

  people.forEach(function(person) {
    //Is this O(1) or O(log n)?
    peopleMap[person.id] = person;
  });

  ids.forEach(function(id) {
    matches.push(peopleMap[id]);
  });

  return matches;
};

但是,当我对此进行测试时,这两种算法的表现似乎都差不多。 #1 在 chrome 中更快,#2 在 Firefox 中稍快。

http://plnkr.co/edit/FidAdBqS98RKebxaIlva?p=preview

我感觉将字段插入对象是O(log n) 而不是O(1),正如我所预料的那样。不过,我已经阅读了一些相互矛盾的帖子,所以我不确定。我想这可能取决于浏览器。有什么方法可以在 JavaScript 中使用 O(n) 算法始终如一地解决这个问题?

【问题讨论】:

  • 如果您有工作代码并希望对其进行改进,我想知道codereview.stackexchange.com 是否是您发帖的最佳地点。
  • 花费额外的时间来构建peopleMap 可能会为大型数据集带来更多回报,或者如果您创建一次地图然后重复查找。此外,您似乎没有利用 #1 中的排序数组,所以我想知道您为什么还要费心对它们进行排序。
  • 数组是经常更新还是只更新一次?
  • @VikramBhat 数组只更新一次

标签: javascript algorithm time-complexity


【解决方案1】:

首先,JavaScript 不要求将对象实现为哈希(平均 O(1) 查找)而不是使用其他一些结构,例如 O(log(n)) 的 BTree。所以没有人能保证对象属性查找的性能。

但通常人们使用哈希。是O(1)

但正如笑话所说,出于所有意图和目的,log(n) 是一个常数。对于 Google,它是一个稍大的常数。 这抓住了一个真实的事实。 log(1000)log(1000000000) 之间的差异是 3 倍。访问 CPU 缓存和访问 RAM 之间的差异是 10 倍。虽然理论上 O(1)O(log(n)) 好,但实际上对于我们实际可能遇到的数据大小,实现细节会产生更大的差异。任何一方都可以获胜。

因此,您的基准测试对理论缩放规则没有任何用处。对于您的用例而言,不同的实现具有不同的性能赢家这一事实是您必须工作的世界的不幸事实之一。

【讨论】:

  • 我用 200 万个项目对此进行了测试。 Log2(2000000) = 21 因此,如果方法 1 是 O(n log n) 而方法 2 是 O(n),那么我预计会看到 log n 开始产生一些效果。但是这两种算法仍然几乎相同。因此,我认为这可能更像是插入到成本为O(log n) 而不是log n 太小而无法注意到的对象的情况。但我想这仍然很难说,因为 CPU 缓存等其他因素可能会阻碍
  • 您真的无法提供足够的数据来通过实验确定存在哪些日志因子。此外,哈希在缓存上往往很困难,而排序算法非常友好。举个极端的例子,如果你有 100 GB 的数据在磁盘上,sort 将执行散列的许多数量级。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-08-28
  • 2011-01-04
  • 2012-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多