繁体   English   中英

JavaScript函数的速度差异,找到数组中最常见的元素

[英]Speed differences in JavaScript functions finding the most common element in an array

我正在面试,并一直在练习一些练习题。 问题是:

找到数组中重复次数最多的整数。

这是我创建的功能和他们创建的功能。 他们被恰当地命名。

 var arr = [3, 6, 6, 1, 5, 8, 9, 6, 6] function mine(arr) { arr.sort() var count = 0; var integer = 0; var tempCount = 1; var tempInteger = 0; var prevInt = null for (var i = 0; i < arr.length; i++) { tempInteger = arr[i] if (i > 0) { prevInt = arr[i - 1] } if (prevInt == arr[i]) { tempCount += 1 if (tempCount > count) { count = tempCount integer = tempInteger } } else { tempCount = 1 } } console.log("most repeated is: " + integer) } function theirs(a) { var count = 1, tempCount; var popular = a[0]; var temp = 0; for (var i = 0; i < (a.length - 1); i++) { temp = a[i]; tempCount = 0; for (var j = 1; j < a.length; j++) { if (temp == a[j]) tempCount++; } if (tempCount > count) { popular = temp; count = tempCount; } } console.log("most repeated is: " + popular) } console.time("mine") mine(arr) console.timeEnd("mine") console.time("theirs") theirs(arr) console.timeEnd("theirs") 

这些是结果:

most repeated is: 6
mine: 16.929ms
most repeated is: 6
theirs: 0.760ms

是什么让我的功能比他们慢?

看起来这不是一个公平的考验。 首先运行函数时,它会对数组进行排序。 这意味着它们的功能最终使用已经排序的数据,但不会花费执行排序的时间成本。 我尝试交换测试运行的顺序,并得到几乎相同的时间:

console.time("theirs")
theirs(arr)
console.timeEnd("theirs")
console.time("mine")
mine(arr)
console.timeEnd("mine")

most repeated is: 6
theirs: 0.307ms
most repeated is: 6
mine: 0.366ms

此外,如果您使用两个单独的阵列,您将看到您的功能和他们的功能大约在相同的时间内运行。

最后,请参阅Anders的回答 - 它表明较大的数据集揭示了函数的O(n * log(n))+ O(n)性能与其函数的O(n ^ 2)性能。

我的测试结果

当我测试( JSFiddle )它为一个包含50 000个元素的随机数组时,我得到以下结果:

mine:     28.18 ms
theirs: 5374.69 ms

换句话说,您的算法似乎要快得多。 这是预料之中的。

为什么你的算法更快?

您首先对数组进行排序,然后循环一次。 Firefox使用合并排序 ,Chrome使用快速排序的变体(根据此问题 )。 两者平均花费O(n*log(n))时间。 然后循环遍历数组,花费O(n)时间。 总共得到O(n*log(n)) + O(n) ,可以简化为O(n*log(n))

另一方面,它们的解决方案有一个嵌套循环,其中外部循环和内部循环都在所有元素上进行迭代。 那应该是O(n^2) 换句话说,它更慢。

为什么您的测试结果不同?

那么为什么你的测试结果与我的不同呢? 我看到了许多可能性:

  • 你使用了一个小样本。 如果您只是在代码中使用了九个数字,那肯定就是这种情况。 当你在测试中使用短数组时,开销(比如Gundy在评论中建议运行console.log )占据了它所花费的时间。 这可以使结果看起来完全随机。
  • neuronaut建议它与它们的代码在已经按代码排序的数组上运行的事实有关。 虽然这是一种不好的测试方式,但我看不出它会如何影响结果。
  • 浏览器的某种差异。

关于.sort()的注释

另请注意:您不应使用.sort()来排序数字,因为它会按字母顺序对事物进行排序。 相反,使用.sort(function(a, b){return ab}) 在这里阅读更多。

关于进一步说明的进一步说明:在这种特殊情况下,仅使用.sort()实际上可能更聪明。 由于您不关心排序,只关心分组,因此将数字排序错误无关紧要。 它仍然会将具有相同值的元素组合在一起。 如果没有比较功能(我怀疑它是)更快,那么没有一个排序是有意义的。

一种更快的算法

你在O(n*log(n))解决了这个问题,但是你可以在O(n) 这样做的算法非常直观。 循环遍历数组,并跟踪每个数字出现的次数。 然后选择出现次数最多的数字。

比方说有m阵列中不同的数字。 循环遍历数组需要O(n)并找到max取O(m) 由于m < n这使得O(n) + O(m)简化为O(n)

这是代码:

function anders(arr) {

    //Instead of an array we use an object and properties.
    //It works like a dictionary in other languages.
    var counts = new Object();

    //Count how many of each number there is.
    for(var i=0; i<arr.length; i++) {
        //Make sure the property is defined.
        if(typeof counts[arr[i]] === 'undefined')
            counts[arr[i]] = 0;
        //Increase the counter.
        counts[arr[i]]++;
    }

    var max;             //The number with the largest count.
    var max_count = -1;  //The largest count.

    //Iterate through all of the properties of the counts object
    //to find the number with the largerst count.
    for (var num in counts) {
        if (counts.hasOwnProperty(num)) {
            if(counts[num] > max_count) {
                max_count = counts[num];
                max = num;
            }
        }
    }

    //Return the result.
    return max;

}

在一个随机数组上运行它,在0到49之间有50 000个元素,在我的计算机上只需3.99毫秒。 换句话说,它是最快的。 背面是你需要O(m)内存来存储每个数字出现的时间。

这里的其他答案已经很好地解释了为什么theirs的速度更快 - 以及如何优化你的。 使用大型数据集(@Anders)实际上更好。 我设法优化了theirs解决方案; 也许这里有一些有用的东西。


通过采用一些基本的JS微优化,我可以获得始终如一的更快结果。 这些优化也可以应用于您的原始函数,但我将它们应用于theirs

  • 预增量比后增量稍快 ,因为该值不需要首先读入内存
  • 反向循环比我尝试过的任何东西都快得多 (在我的机器上),因为JS被翻译成操作码, 并且保证>= 0非常快。 对于这个测试,我的计算机得分为514,271,438 ops / sec,而下一个最快的得分为198,959,074
  • 缓存的结果length -对于更大的阵列,这将使better更明显快于theirs

码:

function better(a) {
    var top = a[0],
        count = 0,
        i = len = a.length - 1;
    while (i--) {
        var j = len,
            temp = 0;
        while (j--) {
            if (a[j] == a[i]) ++temp;
        }
        if (temp > count) {
            count = temp;
            top = a[i];
        }
    }
    console.log("most repeated is " + top);
}

[ 小提琴 ]

theirstheirs非常相似(如果不是相同的话),但是具有上述微优化。

以下是运行每个功能500次的结果。 在运行任何函数之前对数组进行预排序,并从mine()删除sort

mine: 44.076ms
theirs: 35.473ms
better: 32.016ms

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM