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.mapArray.prototype.filterArray.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() 有啥区别? [复制]

JavaScript基础总结

JavaScript基础总结

JavaScript基础总结