在工业界工作时,big-O真的相关吗?


65

在我参加的每次采访中,我都对复杂性的数学分析(包括big-O符号)进行了测验。

大O分析与行业发展之间的相关性如何?您真正使用它的频率是多少?对问题有一个稳定的思维定式是多么必要?


5
@ MM01我在高中和大学学习过。尽管我认识到它是程序员知识的基础,但我从未在任何工作中使用它。
systempuntoout 2010年

27
什么确切的行业,你打算问这是什么时候?您是否正在为月球车或博客平台编写控制代码?
Tim Post

14
@systempuntoout,您从未选择过比另一个算法更快的算法,因为它更快吗?

3
@ MM01-如果您正在为此而苦苦挣扎,可以在这里找到最简单的一种解释(尽管有些
Tim发表

6
@Systempuntoout,理解和使用O标记并不意味着严格的数学证明,但可以通过简单的表达式传达算法的行为。如果需要一维排序,则需要O(n log n)算法。如果要使用斐波那契数实现,请选择一个在O(n)中运行的实现。即使您没有明确说出来,这仍然是循环和递归数的精简版本,非常有用。节省了大量的单词。(对于挑剔的人-是的,如果k很大或很小,k也很重要)。

Answers:


76

我的问题是,这种检验与工业发展有何关联?

对计算复杂性理论(例如,大O表示法)的扎实理解对于设计可伸缩算法,应用程序和系统至关重要。由于可伸缩性与行业中的计算高度相关,因此大的O符号也是如此。

您多久使用一次它,对问题有一个磨练的心态有多必要?

取决于您“实际使用它”的意思。一方面,我从未为所写软件做过计算复杂性的形式证明。另一方面,大多数日子里,我不得不处理可扩展性是潜在问题的应用程序,并且设计决策包括根据其复杂性特征选择(例如)适当的收集类型。

(我不知道如果不对复杂性理论有扎实的理解,是否有可能一致地实施可扩展系统。我倾向于认为事实并非如此。)


+1,因为原则很重要。以我的行业经验来看,这是要考虑的因素,而不是很多。就是说-您被问到(示例)列表插入与数组插入的比较,或气泡排序与快速排序的比较,然后面试官的目标是掌握您的知识。如果您甚至考虑到复杂性/运行时/可伸缩性/性能,也将获得赞赏。如果您不/无法考虑这些事情,那么将会有一些您不会做得很好的工作。很少,但确实会不时出现。
quick_now 2010年

6
好吧,有可能,在漆黑的黑暗中射击目标也是可能的。如果有足够的子弹,您最终将击中靶心。然后,体验各种设计和实现因素的结果,这将导致下次需要的子弹更少。打个比方可能很差,但它准确地描述了某些软件的编写方式。我投票赞成你的答案。
Tim Post

但也请注意,“较晚”的性能通常受到与复杂性无关但黑匣子无法控制的问题的影响。这些盒子的心理模型对于优化任何事物都是必须的。当N接近无穷大时,这些考虑可能会变得无效,而这从来就不会发生。
belisarius博士10年

@Tim Post-我说过“ ... 始终实施可扩展的系统...”。当然,您可以得到幸运,但不能始终保持幸运。但是我也准备接受一个真正聪明/有经验的人可以发展对复杂性的直觉理解,而无需走近课本或计算机科学课程。
Stephen C

旁注,当一位男性同事告诉一位女性同事“听起来像您遇到了一个大O问题”时,却引起了一些工作的笑声,却没有意识到该术语的其他含义。她本着其本意就接受了,但不能停止咯咯笑。
Paul

36

这样做的原因是因为它表示可伸缩性

O(n ^ 2)的过程将比O(n log n)的过程更差,但比O(n ^ 3)甚至O(n!)的过程更好。

如果您不知道这些差异以及它们何时适用,则不适合选择正确的功能实现,以及将测试性能外推到生产性能中。


编辑:从http://www.codinghorror.com/blog/2007/09/everything-is-fast-for-small-n.html(又来自Programming Pearls)比较48n与n ^ 3

在此处输入图片说明


8
+1:发现流程无法扩展的最糟糕方法是让一大堆尖叫的客户同时出现。
拉里·科尔曼

22
@Larry,至少尖叫声与客户数量成线性比例!

10
好吧,我想这只是说明big-O的重要性:声音实际上是O(log Customers)dB。
MSalters 2010年

4
@MSalters,好吧,我认错道:“ NUMBER惨叫的客户的数量成线性比例”。声音水平是另一回事。

1
@ThorbjørnRavn Andersen:我读过一些研究,这暗示它更多是对数尺度的,这就是为什么某些类别的客户投诉如此重要的原因!他们指出,客户群大,很多更多的人有这个问题,只是不说什么或将要竞争。
史蒂文·埃弗斯

32

这取决于你在做什么。

对于Web开发人员(例如我),这通常很重要。您要扩展Web应用程序。如果您的应用程序有一个可以扩展为O(n ^ 2)的瓶颈,并且您认为这很好,因为您的服务器可以同时处理1000个用户,那么您似乎不需要在意。事实是,要处理两倍的数量(这很可能会在一夜之间发生),您将需要4倍的计算能力。理想情况下,您希望Web应用程序按O(n)进行扩展,因为硬件在合理的恒定用户/服务器比率下很便宜。

通常,在拥有10万个对象的应用程序中,大O会吃掉你。您极易受到高峰攻击。例如,我目前正在开发3D游戏,这是一个处理数据负载的应用程序。除了渲染外,您还需要进行碰撞检查,导航等操作。您需要高效的算法,需要大量的缓存,因此效率较低的算法会摊销。等等。

当然,如果您要做的是像通过在界面设计器中组合一个GUI来制作移动应用程序,然后将其与某些Web服务结合在一起,那么您永远不会遇到复杂性问题。因为您调用的Web服务已经解决了这一问题。


制作移动应用程序不只是将GUI组合在一起的情况,而且我会原谅您在2010年发表的声明:)在移动设备中,体系结构,线程,数据存储,网络队列非常复杂。但是原始Big O无关紧要(至少在iOS中是无关紧要的),因为您应该使用本机数据结构和算法。
PostCodeism

21

在工作生涯中,我从未真正正式应用过该规则。

但是,您必须熟悉该概念,并在每次设计算法时以直观的方式应用它。

规则是:

您应该对O表示法足够熟悉,以便能够确定给定任务是否需要对其进行正式计算,或者足以直观地对其进行评估,或者是否可以完全跳过它。就像许多其他基本数学概念一样。


10

好吧,也许有一个小故事可以启发您为什么一定要这样做:

在我从事的一个项目中,有一个程序负责打印所有类型的文档(标签,选择列表等)。该程序由两部分组成,一个部分从数据库中读取所有必需的数据并将其写入到计算机中。 .ini样式的文件,以及读取这些文件并将其填充到模板中的另一部分。这对于标签和小列表(只有几个字段)效果很好,但是当它必须打印约20页的“大”列表时,它运行了将近10分钟。因为访问这些ini文件会导致O(n²)访问时间,所以n是要打印的字段数。

如果该程序的原始程序员理解O表示法,他们将永远不会那样做。用哈希表代替愚蠢的东西使它变得更快了。


8

Big-O的性能很重要,但是已经被内部化了。

排序和搜索的Big-O性能无关紧要,因为人们通常使用系统提供的功能,并且这些功能将尽可能地好(假设它们通常需要有用)。有些数据结构对于不同的事物更有效,但是通常可以根据一般原则进行选择(并且通常内置于现代语言中)。有某种意义上的算法可扩展或不可扩展。

结果是形式问题很少在实践中出现,但是实践是建立在相同的原则上的。


当您查看由尚未内部化Big-O 的人编写的代码时,您真正注意到的地方是,他们的子系统在生产中的表现如此惊人,这感到惊讶。即使是基本的理解也足以使您质疑在相同的两个巨大数组上的四个嵌套的foreach循环...
eswald 2010年

6

恕我直言,许多计算机科学程序使许多学生在杂草丛中徘徊。这些程序从未完全传达出计算科学的全部内容。学生进入该行业,努力学习如何应用所学的概念,而对它们与现实世界的关系知之甚少。

我要说的是,计算科学的核心是推理能力。然后,您将学习各种方法和技术来执行此操作,并将其应用于抽象的问题,这些问题是在许多现实世界中发现的原型原型。诀窍是发现现实世界中的这些原型原语,然后推理诸如正确性,复杂性,时间等事物,您可能会同意,这是您需要担心的真实问题。深入了解各部分的行为方式,通常可以使您深入了解整体的行为方式。而且,相同的通用方法和技术也可以应用于整个过程,只是对较小的,抽象的,定义明确的部分所具有的严格性不相同。但最后,计算科学使您拥有合理的能力 决定如何安排计算,并真正了解计算在各种条件下的行为。


5

给自己的备忘录!:

我和许多其他人经常问自己这个问题。

我认为我们问这的真正原因是因为我们变得懒惰了。

这种知识永远不会过时或过时。您可能不会每天直接应用它,但会下意识地使用它,它将对您的设计决策产生积极影响。有一天它可以为您或其他人节省数小时和数天的编码时间。

随着越来越多的问题被第三方库和工具封装,并且越来越多的开发人员可以使用它们,您将需要了解此知识,以将自己与他人区分开来并帮助解决新问题。


5

并不是的。基本上,我唯一想过的就是访问数据库。我通常会看一下代码,然后说“正在执行n + 1个查询,您应该将其更改为只执行1个或2个”

因为我的所有数据都是从数据库中读取并显示给用户,所以我尝试将正在处理的数据量减至最小,以至于线性算法与O(n ^ 2)算法之间的差异非常明显微不足道。

如果有问题,我们将在稍后进行分析和修复。


1
我实际上认为这种随意的“ n + 1”查询有点危险。特别是,我已经看到使n ^ d个查询(其中d> = 2)被视为“ n + 1”的代码,这使真正可怕的情况看起来只是糟糕的。
philosodad 2012年

3

您提出了三个问题,我认为简短的答案可以帮助到目前为止给出的更长的论点。

该测试与行业发展之间的相关性如何?

取决于行业。

在代码速度或代码空间有问题的任何地方,它都与所涉及的行业完全相关。通常,您需要知道一个例程将花费多长时间,或需要多少内存(在线/离线)。

您多久使用一次?

取决于行业。

如果性能和扩展与手头的工作无关,那么只有在严重的性能不足的情况下,才很少。如果您是高度使用的关键系统的工程师,那么也许每天都可以。

对问题有一个磨练的心态是多么必要?

完全必要。

您可能每天都必须使用它,或者仅在严峻的情况下才使用它;但有时会需要。最好是在设计过程中,在问题到来之前,比拼命地分析扼流系统。


3

我会说这很频繁。我们通常不会证明某些东西具有特定的big-O,但我们已将其内在化,并记住/熟悉了特定数据结构和算法的big-O保证,并且我们为特定用途选择了最快的保证。拥有一个包含所有选项的库会很有帮助,例如Java集合库或C ++ STL。当您选择使用(查找)而不是(查找)并肯定选择不对(查找)进行线性搜索来查找不需要排序访问权限的内容时,您每天都会隐式地自然使用big-O 。java.util.HashMapO(1)java.util.TreeMapO(lg n)java.util.LinkedListO(n)

当某人选择了次优的实现,并且知道更多的人出现并看到了他们的代码时,纠正它们是我们词汇的一部分:“您的实现花费了二次时间,但是我们可以做到这一点到n-log-n这种方式”与我们使用英语订购比萨一样自然而自然地自动进行。


3

您可能不必进行形式分析,但至少要对算法复杂性的顺序有一个全面的了解,以及如何比较两种算法,这对于您要进行非常规的工作并使其顺利进行至关重要。

我已经开发了两个不同的系统,这些系统在早期开发中似乎还不错,但是由于有人使用了O(n ^ 2)算法,所以在生产测试中就把硬件屈服了。而且在这两种情况下,解决方案都是对O(n)算法的不重要改变。


1

他们可能正在开发用于消费的API的地方。C ++ STL是为数不多的对其算法施加复杂性限制的API之一。但是对于日常工作的程序员/高级程序员/设计师/架构师来说,他们的想法并不多。


任何良好的collection API都会做出这些保证,例如Java collections API在其文档中也具有这些保证。
肯·布鲁姆

1

除了交流思想外,我还没有发现它那么重要,并且我在性能至关重要的领域(光线跟踪,图像和网格处理,粒子系统,物理引擎等)工作,并且不得不设计出许多专有算法和数据结构在研发工作时。在这些领域中,通常只有少数几个非常高效的数据结构和算法可以产生全新的尖端产品,而昨天的算法却使现有产品过时了,因此人们总是追求使工作效率更高。需要说明的是,我从未发表过有关我设计的算法的论文。它们都是专有的。如果我这样做了,我将需要数学家的帮助来拟定证明等等。

但是在我看来,除非算法的伸缩性很差,否则每次迭代的计算工作量通常比算法的可伸缩性更重要。如果有人提出了一种先进的射线追踪技术,那么我对计算技术(例如它们如何表示和访问数据)比算法复杂性更感兴趣,因为在这种竞争性和创新性的场景中已经有了合理的可伸缩性。提出无法扩展的算法将无能为力。

当然,如果您要比较二次复杂度和线性运算,那是一个巨大的差异。但是我领域的大多数人都有能力避免在史诗输入上应用二次复杂度算法。因此,可扩展性通常被深深地暗示着,更有意义和更有趣的问题变成:“您是否使用过GPGPU?SIMD?它是否并行运行?您如何表示数据?您是否针对高速缓存友好的访问模式重新组织了它?如何它需要多少内存?它能可靠地处理这种情况吗?您要推迟某些处理还是一次完成?”

如果前者以更优化的模式访问内存,甚至更适合于多线程和/或SIMD,则即使是线性算法也可以胜过线性时间算法。有时,由于这些原因,即使是线性算法也可以胜过对数算法,而对于小输入,线性时间算法自然也胜过对数算法。

因此,对我而言,更重要的是某些人可能称为“微优化”,例如数据表示(内存布局,具有热/冷字段拆分的访问模式等),多线程,SIMD,有时还包括GPGPU。在一个领域里,每个人都已经有足够的能力去使用像样的尖端算法来处理所有问题,并且新论文一直在发表,在击败算法向导方面的竞争优势并不是来自算法复杂性的提高,而是来自更直接的计算效率。

我的领域是由杰出的数学家主导的,但并非总是知道他们正在做的计算成本或许多加快代码速度的较低技巧的人。尽管我的复杂程度不高,但在设计更快,更紧凑的算法和数据结构时,通常这是我的优势。我正在尝试硬件喜欢的东西,朝着比特和字节的方向努力,即使我比真正复杂的算法做更多的工作迭代,也使每次迭代的工作便宜得多-在我的情况下,工作便宜得多。我编写的代码也往往更简单。如果人们认为难以直接理解算法和数据结构的微观优化版本,就很难理解和维护,

作为一个基本示例,我想出了一个简单的网格结构,该结构最终在我们公司的KD树中表现出优于碰撞检测和冗余点去除的性能。我愚蠢的原始网格在算法上没有那么复杂,在数学上和算法上都比用他的KD-tree以新颖的方式找到中点的家伙笨拙,但是我只是调整了网格的内存使用和访问模式,这足以胜过更复杂的东西。

我所拥有的另一个优势,使我能够在一个比我聪明得多的人所统治的领域中生存,只是真正地了解用户的工作方式,因为我使用软件的开发方式相同。这给了我一些算法的想法,这些算法实际上非常符合用户的兴趣。作为一个基本示例,大多数人都尝试使用空间索引来加速诸如碰撞检测之类的事情。我在大约几十年前就对有机模型进行过一次简单的职业塑造观察,例如,如果角色将手放在脸上,则空间索引结构将需要拆分节点并进行昂贵的更新(如果角色)然后将手从脸上移开。相反,如果您根据连通性数据而不是顶点位置进行分区,您可以得到一个稳定的层次结构,该结构可以非常快速地更新,并且不需要拆分或重新平衡树(只需要在动画的每个帧中更新边界框)...像这样的事情-算法不需要笨重的数学背景如果他们只是了解基本概念,就可以提出来,但是由于他们没有以如此接近用户工作方式的方式来思考事物,并且对几何的特性而不是对几何的思考太多,从而使数学家们望而却步被普遍使用。与算法巫师相比,我更多地依靠一般的计算知识和用户端知识来相处得很好。因此,无论如何,我并没有发现专注于算法复杂性那么重要。


0

是的,复杂性在行业中至关重要。如果最终设计出某个关键路径按N平方缩放的事物(事物数量加倍使系统加载四倍),那么达到缩放瓶颈的速度将比具有N缩放事物的速度快得多。

但是,通常并不能将它作为适当的正式证据来证明某事物具有给定的复杂性,因此,对操作模式的复杂性有一个很好的直觉是一个好的开始。


0

我从数学的角度从未考虑过大O,除非有要求,否则我根本不会考虑大O。我只是在脑海中看到一个算法,我可以判断出它是否不好,因为它对每个N都在内存中进行了多次循环,或者是否确实进行了分而治之。如果需要,我可以在几秒钟内将其转换为大的O表示法,但是对我而言,仅了解算法/容器如何与内存一起工作比思考数学观点要容易得多。


-3

采访中提出的问题可以找出你是否可以解释事物并以逻辑方式思考。面试官还试图找出您是否可以运用您所知道的知识来解决相关问题

每个对软件工程进行了有价值研究的人都会遇到“ Big O”的问题,要回答有关“ Big O”的好问题,您还必须对标准数据结构和算法有所了解。

在为员工进行面试时,您正在寻找的是可以快速学习工作的人,而不是已经知道一组给定详细技能的人,因此很难选择面试官和被访方都具有共同理解的问题的。

因此,有关“大O”的问题可能与面试过程非常相关。

至少在我作为计算机程序员的漫长岁月中,由于某些人不了解要使用的正确数据结构和算法,我不得不修复缓慢的代码,但是您可以在不了解Big O的情况下解决这些问题。但是,确实了解大帐篷的人并没有首先避免这些问题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.