理解使用对象作为散列的JavaScript算法的复杂性

理解使用对象作为散列的JavaScript算法的复杂性,javascript,algorithm,time-complexity,Javascript,Algorithm,Time Complexity,我试图想出一个算法来解决下面的问题 给定一个ID数组 var ids = [8098272281362432, 7824519999782912]; 查找项目数组中的所有匹配项 var people = [ { "id": 8098272281362432, "age": 59, "name": "Douglas Hunter" }, { "id": 625873891885056, "age": 1,

我试图想出一个算法来解决下面的问题

给定一个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)

方法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中稍快


我感觉在对象中插入一个字段是
O(logn)
而不是
O(1)
。我读过一些关于这个的相互矛盾的帖子,所以我不确定。我想这可能取决于浏览器。有没有办法用JavaScript中的O(n)算法一致地解决这个问题?

首先,JavaScript不要求将对象实现为散列(平均
O(1)
lookup),而不是使用一些其他结构,如BTree,即
O(log(n))
。因此,没有人能够保证对象属性查找的性能

但通常人们使用散列。它们是
O(1)

但正如这个笑话所说,无论出于何种目的,log(n)都是一个常数。对于谷歌来说,这是一个稍大的常数。这抓住了一个真实的事实。
log(1000)
log(100000000)
之间的差异是3倍。访问CPU缓存中的内容与访问RAM之间的差异是10倍。虽然理论上
O(1)
O(log(n))
好,但实际上,对于我们实际可能遇到的数据量,实现细节的差别要大得多。任何一方都可以获胜


因此,您的基准测试没有说明任何关于理论缩放规则的有用信息。不同的实现在您的用例中有不同的性能赢家,这是您必须在其中工作的不幸事实之一。

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

但通常人们使用散列。它们是
O(1)

但正如这个笑话所说,无论出于何种目的,log(n)都是一个常数。对于谷歌来说,这是一个稍大的常数。这抓住了一个真实的事实。
log(1000)
log(100000000)
之间的差异是3倍。访问CPU缓存中的内容与访问RAM之间的差异是10倍。虽然理论上
O(1)
O(log(n))
好,但实际上,对于我们实际可能遇到的数据量,实现细节的差别要大得多。任何一方都可以获胜


因此,您的基准测试没有说明任何关于理论缩放规则的有用信息。不同的实现会为您的用例带来不同的性能赢家,这是您必须在其中工作的不幸事实之一。

如果您有工作代码并希望改进它,我想知道这是否是你发表文章的最佳地点。花额外的时间构建
人物地图
可能会为大型数据集带来更多回报,或者如果你创建一次地图,然后重复查找。此外,您似乎没有利用#1中已排序的数组,因此我想知道您为什么还要费心对它们进行排序。数组是经常更新还是只更新一次?@VikramBhat数组只更新一次,如果您有工作代码并希望对其进行改进,我想知道这是否是你发表文章的最佳地点。花额外的时间构建
人物地图
可能会为大型数据集带来更多回报,或者如果你创建一次地图,然后重复查找。另外,你似乎没有利用#1中的排序数组,所以我想知道你为什么还要费心对它们进行排序。数组是经常更新还是只更新一次?@VikramBhat数组在我用200万个项目进行测试后才更新
Log2(2000000)=21
因此,如果方法1是
O(n log n)
而方法2是
O(n)
,那么我希望看到
log n
开始产生一些效果。但这两种算法仍然基本相同。因此,我认为这可能更像是在对象中插入成本
O(logn)
,而不是
logn
太小而无法注意到。但我想这仍然很难说,因为CPU缓存等其他因素可能会妨碍您,您真的无法向它抛出足够的数据来实验性地找出日志因素。此外,当排序算法非常友好时,哈希往往对缓存很难实现。作为一个极端的例子,如果磁盘上有100GB的数据,sort将执行多个数量级的散列。我用200万个项目测试了这一点
Log2(2000000)=21
因此,如果方法1是
O(n log n)
而方法2是
O(n)
,那么我希望看到
log n
开始产生一些效果。但这两种算法仍然基本相同。因此,我认为这可能更像是在对象中插入成本
O(logn)
,而不是
logn
太小而无法注意到。但我想这仍然很难说,因为CPU缓存等其他因素可能会妨碍您,您真的无法向它抛出足够的数据来实验性地找出日志因素。此外,当排序算法不可用时,哈希往往对缓存很难实现
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;
};