【问题标题】: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

标签: javascript arrays ecmascript-6 time-complexity fill


【解决方案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) 之类的简单答案更深入。任何性能问题都必须基于操作上下文,有时没有区别。
猜你喜欢
  • 1970-01-01
  • 2017-03-19
  • 2021-02-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-09
  • 1970-01-01
  • 2016-05-03
相关资源
最近更新 更多