JavaScript 中 new Array(n).fill('apple') 的时间复杂度是多少?
Posted
技术标签:
【中文标题】JavaScript 中 new Array(n).fill(\'apple\') 的时间复杂度是多少?【英文标题】:What is the time complexity of new Array(n).fill('apple') in JavaScript?JavaScript 中 new Array(n).fill('apple') 的时间复杂度是多少? 【发布时间】:2021-11-18 23:28:45 【问题描述】:我正在寻找这个问题的答案,但找不到任何答案。
new Array(n).fill('apple')
的时间复杂度是多少?
对于n=5
,这将创建一个包含 5 个“苹果”字符串的数组:['apple', 'apple', 'apple', 'apple', 'apple']
我的假设是 new Array(5) 将首先创建一个包含 5 个空槽的数组,然后遍历它以将“苹果”放入每个槽中。 这种情况下,时间复杂度是O(N),N是数组的长度?
不过,我也听到有人说,既然是内置方法,所以只需要 O(1)。
【问题讨论】:
首先,填充一个 N 长度的数组没有免费的午餐。在某种程度上它将是 O(n)。如果它是使用本机代码非常有效的操作,它可能是一个非常快的 O(n),但它在某种程度上与数组的长度成正比。其次,实际性能完全取决于实施,您了解实际性能的唯一方法是测量/基准测试。这是回答任何性能问题的一个组成部分。最后,你为什么想知道?基于了解这一点,你会做些什么不同的事情?真正的问题是什么? 你的假设是正确的,Array(5)
在未定义的.fill('apple')
上创建一个数组来填充它们,通过像Array(5).fill('apple')
这样调用不会神奇地让它做其他事情
@LawrenceCherone 它不会创建一个数组“of undefined's”。 OP 的描述“一个有 5 个空槽的数组”要准确得多。
"因为它是一个内置方法,所以只需要 O(1)" - 这种概括是错误的。有很多“内置”方法,例如 O(n)、Array.prototype.map
、Array.prototype.filter
、Array.prototype.find
等等。
@Bergi 语义playcode.io/815709
【参考方案1】:
时间复杂度应该是O(N)
,因为它应该随着数组的长度线性缩放。
为了验证这个理论,我运行了一些基准测试,看看它是否真的是 O(n)
。
对于较大的数组,nodejs 在您测量它时显示大约 O(n)
(请参阅下面的代码和结果)。
我使用 5 种大小的数组运行这个测试应用程序。
const sizes = [1000, 10000, 100000, 1000000, 1000000];
还有,用process.hrtime.bigint()
计算需要多长时间。然后我输出每个大小数组的总时间(以纳秒为单位)和每个元素的时间。
这是我得到的输出:
Array sizes:
[ 1000, 10000, 100000, 1000000, 1000000 ]
Ns per pass:
[ 36700n, 48600n, 553000n, 5432700n, 5268600n ]
Ns per element:
[ 36.7, 4.86, 5.53, 5.4327, 5.2686 ]
您可以看到,最后三个大小非常接近 O(n)
,与每个元素的固定时间(恰好是 O(n)
)相比,有大约 5% 的变化。第一个离得很远,第二个在每个元素上比其他的要快一些,尽管与最后三个在同一个球场。
第一遍必须有某种解释器开销(可能优化代码路径),或者可能只是操作的总体开销远远超过实际填充数组,以至于它扭曲了我们试图测量的内容。
代码如下:
class Measure
start()
this.startTime = process.hrtime.bigint();
end()
this.endTime = process.hrtime.bigint();
deltaNs()
return this.endTime - this.startTime;
deltaNumber()
return Number(this.deltaNs());
const sizes = [1000, 10000, 100000, 1000000, 1000000];
const benchmark = new Measure();
const times = sizes.map(size =>
benchmark.start();
new Array(size).fill('apple');
benchmark.end();
return benchmark.deltaNs();
);
console.log('Array sizes:\n', sizes);
console.log('Ns per pass:\n', times);
let factors = times.map((t, index) =>
return Number(t) / sizes[index];
);
console.log('Ns per element:\n', factors);
【讨论】:
我已经运行这个例子 3 次并且收到的结果不同: 数组大小:[ 1000, 10000, 100000, 1000000, 1000000 ] Ns per pass: [ 55921n, 41718n, 937870n, 18406736n, 15594361 ]每个元素20.271241 ] 很好的例子,但它的精度很一般 我认为 OP 要求的是时间复杂度,而不是基准 @Bergi - 但是除了时间复杂度应该是多少的理论O(N)
,基准测试难道不能让您了解现实生活中的时间复杂度吗?
@jfriend00 只有这样才称为“性能”、“速度”或“运行时间”而不是“复杂性”:-)
@Bergi - 所以,对你来说,“复杂性”完全是理论上的,从来没有在一堆不同的大小上进行测试,看看理论上的复杂性是否真的正确?【参考方案2】:
当您使用长度为 5 项的字符串数组时,您应该忽略算法的复杂性。但在这种情况下,您使用相同的值填充数组。在这种情况下:
Array(5).fill('apple')
由于代码风格原因是更优雅的解决方案
附: O(1) + O(n) => O(n)
【讨论】:
这个问题的答案如何? 这个问题比 O(1) + O(n) 之类的简单答案更深入。任何性能问题都必须基于操作上下文,有时没有区别。以上是关于JavaScript 中 new Array(n).fill('apple') 的时间复杂度是多少?的主要内容,如果未能解决你的问题,请参考以下文章
JavaScript 中“new Array(..)”和“[..]”的区别? [复制]
JavaScript Array vs new Array区别
在 JavaScript 中,myArray.length = 0 与 myArray = new Array() 有啥区别? [复制]