[英]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)
。 换句话说,它更慢。
那么为什么你的测试结果与我的不同呢? 我看到了许多可能性:
console.log
)占据了它所花费的时间。 这可以使结果看起来完全随机。 另请注意:您不应使用.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
。
>= 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);
}
[ 小提琴 ]
theirs
与theirs
非常相似(如果不是相同的话),但是具有上述微优化。
以下是运行每个功能500次的结果。 在运行任何函数之前对数组进行预排序,并从mine()
删除sort
。
mine: 44.076ms
theirs: 35.473ms
better: 32.016ms
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.