javascript透明度_用JavaScript进行时间复杂度的实验

javascript透明度

If you are interviewing for your first software engineering role after finding this field through an untraditional path, like myself, you will soon reach the point where you need a solid understanding of Big O notation and performance. With Big O notation, understanding goes many layers deep. There is the surface level understanding of “Oh yes, that’s the one with all the O’s and n’s”, the deeper academic understanding, and a more concrete understanding of how the concepts presented in Big O actually impact your code.

如果像我一样,在通过非常规途径找到该领域后正在面试您的第一个软件工程职位,您很快就会到达需要对Big O表示法和性能有扎实了解的地步。 有了Big O表示法,理解就可以深入很多层。 对“哦,是的,这是一个同时包含所有O和n的事物”有表面的理解,对学术的理解更深入,并且对Big O中呈现的概念如何影响您的代码有更具体的理解。

If you’re looking for a deeper academic understanding of Big O and time complexity, this isn’t the article you want. There’s plenty of fantastic resources out there that cover this very topic. In this article, I’ll instead provide a simple do-it-yourself experiment that helped me to develop a more concrete understanding of measuring performance by timing code with different input sizes using performance.now().

如果您想对Big O和时间复杂性有更深入的学术了解,那么这不是您想要的文章。 有很多很棒的资源可以涵盖这个主题。 相反,在本文中,我将提供一个简单的自己动手做的实验,该实验通过使用performance.now()对具有不同输入大小的代码进行计时,帮助我对测量性能有了更具体的了解。

Interview Cake gives a great definition of Big O notation:

面试蛋糕对Big O符号做了很好的定义:

“With big O notation, we express the runtime in terms of — brace yourself — how quickly it grows relative to the input as the input grows arbitrarily large.”

“使用大的O表示法,我们用-准备自己-当输入随心所欲地增大时相对于输入的增长速度来表示运行时。”

The reason I love Interview Cake’s explanation of Big O is that it also cautions against ONLY looking at Big O when analyzing performance.

我之所以喜欢Interview Cake对Big O的解释,是因为它也警告不要在分析性能时只看BigO。

“Big O ignores constants, but sometimes constants matter. If we have a script that takes 5 hours to run, an optimization that divides that runtime by 5 might not affect big O, but it still saves you 4 hours of waiting… A great engineer (startup or otherwise) knows how to strike the right balance between runtime, space, implementation time, maintainability, and readability.”

“大O会忽略常量,但有时常量很重要。 如果我们有一个需要5个小时才能运行的脚本,那么将运行时间除以5的优化可能不会影响大的O,但仍可以为您节省4个小时的等待时间……优秀的工程师(无论是初创公司还是其他公司)都知道如何正确应对在运行时,空间,实现时间,可维护性和可读性之间取得平衡。”

Now that you have the definition and link to a great resource to learn more, how can we run experiments to test these concepts on actual code? This is where performance.now() comes in when working with JavaScript. With performance.now(), we can create a simple timer to measure how many milliseconds our code takes to execute.

现在您已经有了定义,并链接到了很多资源来了解更多信息,我们如何运行实验以在实际代码上测试这些概念? 这是在使用JavaScript时进入performance.now()的地方。 使用performance.now(),我们可以创建一个简单的计时器来测量代码执行所需的毫秒数。

First, let’s look at the basic set up:

首先,让我们看一下基本设置:

//make sure you're able to access performance.now()
const {performance} = require('perf_hooks');//write out your function
const functionName = () => {
//code goes here}//time your function
const startTimer = performance.now();
functionName();
const endTimer = performance.now();//log how many milliseconds it took your function to execute
console.log(`This function took ${endTimer - startTimer} milliseconds`);

You can place the start and end of your timer anywhere, so this can also be a handy way to test sections of your function to see what’s dragging you down.

您可以将计时器的开始和结束放置在任何地方,因此这也可以方便地测试函数的各个部分,以查看将您拖下的原因。

Let’s practice this with an actual example, solving the level “medium” Matching Strings problem from HackerRank using two different approaches.

让我们以一个实际的示例进行练习,使用两种不同的方法解决HackerRank中的“中级” 匹配字符串问题。

HackerRank’s Matching Strings problem asks us to create a function which takes two parameters: “strings”, an array of strings to search, and “queries”, an array of queries. With these two parameters, we are asked to create a function that returns an array of integers representing the frequency of occurrence of each query in the strings array.

HackerRank的“匹配字符串”问题要求我们创建一个带有两个参数的函数:“字符串”,一个要搜索的字符串数组,以及“查询”,一个查询数组。 使用这两个参数,要求我们创建一个函数,该函数返回一个整数数组,该整数数组表示字符串数组中每个查询的出现频率。

We could solve this problem using a nested loop. Nested loops can (but do not always) run in O(n²) time or quadratic time. If a function runs in quadratic time and 1 input is passed in, it will run 10 times. If 10 inputs are passed in, it will run 100 times. You can see how that time complexity might get real big real fast.

我们可以使用嵌套循环解决此问题。 嵌套循环可以(但不总是)以O(n²)时间或二次时间运行。 如果某个函数以二次时间运行并且传入了1个输入,则它将运行10次。 如果传递了10个输入,它将运行100次。 您可以看到时间复杂度如何快速变大。

If I solved the Matching String problem using a nested loop, my solution might look like this:

如果我使用嵌套循环解决了Matching String问题,那么我的解决方案可能如下所示:

const matchingStrings = (strings, queries) => {
const results = new Array(queries.length).fill(0);
for (let i = 0; i < strings.length; i++) {
for (let j = 0; j < queries.length; j++) {
if (strings[i] === queries[j]) {
results.splice(j, 1, results[j] + 1)
}
}
}
return results;
};

Now, let’s optimize this solution just a bit by storing possible string matches in an object rather than using a nested loop, then comparing our queries to object keys and pushing the value (representing the number of times that query appeared in our strings array) into the results array.

现在,让我们通过在对象中存储可能的字符串匹配而不是使用嵌套循环,然后将我们的查询与对象键进行比较,然后将值(代表查询出现在字符串数组中的次数)推送到对象中,以稍微优化此解决方案。结果数组。

const fasterMatchingStrings = (strings, queries) => { 
const condensedStrings = {};
const results = [];
for (let string of strings) {
condensedStrings[string] = condensedStrings[string] + 1 || 1;
}
for (let query of queries) {
if (condensedStrings.hasOwnProperty(query)) {
results.push(condensedStrings[query]);
} else {
results.push(0);
}
}
return results;
};

Now, the time for using our timer has come. First, lets time one of the smaller tests that HackerRank uses.

现在,使用计时器的时间到了。 首先,让我们关注一下HackerRank使用的较小的测试之一。

//First attempt at Matching Strings
const startTime = performance.now();
matchingStrings(['def', 'de', 'fgh'], ['de', 'lmn', 'fgh']);
const endTime = performance.now();console.log(`Time to run was ${ endTime - startTime} milliseconds`);//Second attempt at Matching Strings
const fasterStartTime = performance.now();
fasterMatchingStrings(['def', 'de', 'fgh'], ['de', 'lmn', 'fgh']);
const fasterEndTime = performance.now();console.log(`Time to run was ${ fasterEndTime - fasterStartTime} milliseconds`);

If I run this three times (and you shouldn’t rely on just one result when using performance.now()), I get the following (rounded) results.

如果我运行了三次(并且在使用performance.now()时您不应该仅依赖一个结果),我将得到以下(四舍五入)结果。

First Matching Strings Solution (ms): 7.326, 7.846, 7.870

第一个匹配字符串的解决方案(毫秒): 7.326、7.846、7.870

Second Matching Strings Solution (ms): 0.158, 0.152, 0.160

第二个匹配字符串解决方案(ms): 0.158、0.152、0.160

Wow, that’s a big difference! But remember, time complexity in Big O is not about how one input size performs, its about how quickly runtime grows relative to the input as it gets arbitrarily large. Let’s try a slightly larger input to see what this means in actual code.

哇,那有很大的不同! 但是请记住,Big O中的时间复杂度与一个输入大小的执行方式无关,而与运行时间相对于输入任意增长的增长速度有关。 让我们尝试一个更大的输入,看看这在实际代码中意味着什么。

//First attempt at Matching Strings
const startTime = performance.now();
matchingStrings(['def', 'de', 'fgh', 'aba', 'bb', 'tig', 'app', 'ghe', 'igh', 'ii'], ['de', 'lmn', 'fgh', 'ii', 'bbb', 'ba', 'tig', 'pa', 'pap']);
const endTime = performance.now();console.log(`Time to run was ${ endTime - startTime} milliseconds`);//Second attempt at Matching Strings
const fasterStartTime = performance.now();
fasterMatchingStrings(['def', 'de', 'fgh', 'aba', 'bb', 'tig', 'app', 'ghe', 'igh', 'ii'], ['de', 'lmn', 'fgh', 'ii', 'bbb', 'ba', 'tig', 'pa', 'pap']);
const fasterEndTime = performance.now();console.log(`Time to run was ${ fasterEndTime - fasterStartTime} milliseconds`);

If I run this three times, I get:

如果我运行三次,将得到:

First Matching Strings Solution (ms): 9.412, 8.194, 8.102

第一个匹配字符串的解决方案(毫秒): 9.412、8.194、8.102

Second Matching Strings Solution (ms): 0.271, 0.256, 0.232

第二个匹配字符串解决方案(ms): 0.271、0.256、0.232

While the first solution grew by about 0.3 to over 1 ms with just a few more inputs, our second solution grew by about 0.1 ms. Imagine how that difference in growth might continue as the input grows arbitrarily large!

尽管第一个解决方案仅增加了几个输入,但增长了约0.3到1 ms以上,而第二个解决方案却增长了约0.1 ms。 想象一下,随着投入的任意增加,这种增长差异可能会继续下去!

As you optimize this solution further and continue to time your functions with increasing input size, you’ll see more and more of a difference between your “slowest” and “fastest” functions. This can be a fun and satisfying way to develop a concrete understanding of how different choices in your code can impact performance!

随着您进一步优化该解决方案并随着输入大小的增加继续计时您的功能,您会发现“最慢”和“最快”功能之间的差异越来越大。 这是一种有趣而令人满意的方式,可以使您对代码中的不同选择如何影响性能有一个具体的了解!

翻译自: https://medium.com/javascript-in-plain-english/experiments-with-time-complexity-in-javascript-56643e20ca12

javascript透明度

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值