成为一个不了解计算复杂性的程序员是否有问题?


30

我在大学里分配了一个练习。我把它带回家并尝试编写一种算法来解决它,我想这与图有关,可以找到连接的组件。

然后,我想到了最琐碎的事情,然后告诉我的讲师。经过简短的观察,他认为我的解决方案的运行时复杂性是不可行的,并显示出一些更有效的方法。而且有一种传统的程序员不知道什么是计算复杂性(我就是其中之一),所以如果程序员不知道什么是计算复杂性会存在问题吗?


3
主持人通知:请不要使用评论进行扩展讨论或发表简短的答案。您可以在聊天室中讨论这个问题;以前的评论已移至该处。
吉尔斯(Gilles)'所以

4
您的头衔是程序员,而您的问题是学生。通常,“程序员”暗含“专业程序员”-那么您是否在问不具备计算复杂性知识的专业程序员是否有问题?还是对没有编程知识的学生来说还可以吗?这两个是不同的问题,即使事实证明它们有相同的答案。
corsiKa

Answers:


42

是的,我想说的是,对于任何认真的程序员来说,都必须了解计算复杂性。只要您不处理庞大的数据集,您就不会知道复杂性就可以了,但是如果您想编写一个解决严重问题的程序,则需要它。

在您的特定情况下,查找连接组件的示例可能适用于多达节点的图形。但是,如果您尝试使用具有节点的图,那么讲师的算法可能会在1秒钟内完成管理,而您的算法(取决于复杂性的程度)将花费1小时,1天甚至是1个永恒。100100.000

学生在我们的算法课程中犯的一个常见错误是遍历如下数组:

while array not empty
    examine first element of array
    remove first element from array

这可能不是最漂亮的代码,但是在复杂的程序中,可能会出现这样的情况,而程序员不会意识到这一点。现在,这个程序有什么问题?

假设我们在元素的数据集上运行它。与以下程序相比,前一个程序的运行速度要慢。100.00050.000

while array not empty
    examine last element of array
    remove last element from array

我希望您同意,对于程序员来说,拥有使程序运行速度快倍的知识可能是一件重要的事情。要了解这两个程序之间的差异,就需要一些有关复杂性理论的基础知识,以及一些有关要使用的编程语言的知识。50.000

用我的伪代码语言,“从数组中删除元素”会将所有元素向右移,而要删除的元素则从左侧移至一个位置。这使得删除最后一个元素成为操作,因为要做到这一点,我们只需要与1个元素进行交互即可。删除第一个元素是因为要删除第一个元素,我们需要将所有其他元素也向左移动一个位置。O(1)O(n)n1

一个非常复杂的基本练习是证明第一个程序将执行操作,而第二个程序仅使用操作。如果您插入您将看到一个程序比另一个程序效率更高。12n2nn=100.000

这只是一个玩具示例,但是已经需要对复杂性有基本的了解,才能分辨出这两个程序之间的区别,如果您实际上是在尝试调试/优化有此错误的更复杂的程序,则需要更深入的了解才能找到找出错误所在。因为类似这样的错误可以通过代码中的抽象很好地隐藏,以这种方式从数组中删除元素。

在比较两种解决问题的方法时,对复杂性有很好的了解也将有所帮助。假设您自己想出了两种解决连接组件问题的方法:为了在它们之间做出决定,如果您能够(迅速)估计它们的复杂性并选择更好的方法,那将非常有用。


10
"So long as you are not dealing with huge data sets you will be fine not knowing complexity"这通常是正确的,但并非总是如此。例如,O(n!)即使对于相对较小的数据集,算法也将不可行。如果O(n!)在可能使用的位置使用算法,则O(n^2)在数据大小为10的情况下执行程序将花费36,288倍的时间。在数据量为20的情况下,您正在查看2.4亿亿次运算。
reirab 2015年

1
我认为答案中应包含@reirab的示例。它更具戏剧性,并能更果断地证明您的观点。在我了解计算复杂性之前,我个人就曾被此类算法所困扰。
任思远

2
我认为还有一个更大的问题在起作用。如果您只是不知道自己会选择不需要的任务。因此,您可以说我几乎需要知道X最终会遇到的所有问题,这可能会很有用。因此,不管它的关键是否仍然值得人们了解,还是最终会刺伤您。
joojaa

“了解这两个程序之间的差异需要一些有关复杂性理论的基础知识” –我认为对于这个特定示例而言,它不是必需的。您可以对其进行概要分析,观察所有时间都花在了“删除元素”上,知道(不了解复杂性理论)删除最后一个元素比删除第一个元素要快,进行更改,从而加快了程序的速度。理解复杂性理论的优点在于,它可以让您轻松地对此类问题进行定量分析,而无需对其进行概要分析,因此可以“过早”进行优化。
史蒂夫·杰索普

..并且总的来说,我怀疑在不参考复杂性理论的情况下,所有或几乎所有实际示例都可以一一解决。在这种情况下,知道复制大量数据比没有复制要慢,这并不是“复杂性理论”。但是,当然,在编程(和任何专业)中,建立一个良好的思维模型通常会有用,因为您可以按原理例行分析,讨论和解决此类问题,而不必一次即行地进行分析和讨论。
史蒂夫·杰索普

26

这是汤姆·范德赞丹Tom van der Zanden)的回答的反驳,该回答指出这是必须的。

问题是,大多数情况下,慢50.000倍是没有关系的(当然,除非您在Google工作)。

如果您执行的操作耗时一微秒,或者您的N从未超过某个阈值(如今已完成很大一部分编码),则将不再重要。在这些情况下,考虑计算复杂性只会使您浪费时间(和最有可能的金钱)。

计算复杂性是一种工具,用于了解为什么某些事物可能变慢或扩展不良,以及如何进行改进,但大多数情况下完全是过度的。

我从事专业程序员已经有五年多了,但我从未发现在循环O(M * N)中循环时需要考虑计算复杂性,因为操作总是非常快,或者M和N如此快小。

对于从事编程工作的任何人来说,还有很多更重要,更常用和更难理解的东西(线程和性能分析是性能方面的好例子)。

当然,有些事情如果不了解计算复杂性是永远无法做的(例如:在字典上查找字谜),但是大多数时候您不需要它。


3
为了扩展您的观点,在某些情况下,过分强调计算复杂性会使您误入歧途。例如,在某些情况下,“小”算法实际上对于小输入而言会更慢。探查器是真理的最终来源。
凯文·克鲁姆维德

2
@Kevin Krumwiede,我完全同意您的观点,即为琐碎的数据集优化排序是过大的。但这也说明至少了解复杂性仍然很重要。这种理解将使您做出气泡排序是合适的决定,而不是其他一些更复杂的算法。
肯特A.15年

4
当您知道在所有情况下数据集都很小时,您就可以摆脱这种情况。但是,您必须非常小心循环内的东西的过度复杂性-不久前,我以这种方式将运行时间缩短了一秒钟。我还曾经遇到过O(n ^ 8)问题(数据验证。)很多人都把它缩短到12个小时。
罗伦·佩希特尔

7
我从来没有发现在循环O(M * N)中循环时需要考虑计算复杂性,因为操作总是非常快,或者M和N非常小。–具有讽刺意味的是,您提供的论据表明您确实考虑了计算复杂性。您认为这与您正在做的事情无关,也许是正确的,但您仍然知道此问题的存在,并且,如果它可能造成问题,则可以在此问题严重后果之前做出反应。用户级别。
Wrzlprmft

4
过早的优化是万恶之源,但过早的悲观化至少是许多烦恼的用户的根源。您可能不需要求解递归关系,但是,至少,您不能分辨出O(1),O(N)和O(N ^ 2)之间的区别,尤其是当您是嵌套循环,以后有人必须清理混乱。资料来源:后来我不得不清理的烂摊子。50.000的系数是如此之大,以至于您最好知道,当您的投入增加时,以后是否仍然可以负担得起。
Jeroen Mostert

14

我从事软件开发工作已经有30年左右的时间了,无论是作为承包商还是员工,我都取得了很大的成功。我的第一语言是BASIC,但是我很快自学了机器语言,以摆脱动力不足的困扰。多年来,我在分析器中花费了很多时间,并且学到了很多有关生成快速,内存有效的优化代码的知识。

不管怎么说,我是自学成才的。直到几年前开始面试,我才遇到过O符号。除了面试期间,这在我的专业工作中从未出现过。因此,我不得不学习基础知识才能在面试中解决这个问题。

我感觉就像是爵士音乐家,不会读乐谱。我仍然可以打得很好。我了解哈希表(哎呀,在我知道哈希表已经被发明之前就发明了哈希表)和其他重要的数据结构,我什至可能知道一些它们在学校没有教授的技巧。但是我认为事实是,如果您想在这个行业中取得成功,您要么需要独立学习,要么学习他们在面试中提出的问题的答案。

顺便说一句,我最近采访了一位前端Web开发人员角色。他们问我一个问题,答案需要既有计算复杂性又需要对数的知识。我设法记得二十年前有足够的数学来或多或少正确地回答它,但这有点令人讨厌。我从未在任何前端开发中使用对数。

祝你好运!


2
那么,您的回答是“是”?
拉斐尔

6
TL; DR:“是”。但是,以我的经验,在您被雇用后,您将不再谈论大多数工作中的计算复杂性。是的,知道您的数据结构及其性能,但是只知道算法是O(n)或任何不好的程序员都无法做到的。最好集中精力快速编写出色的代码,然后在以后优化热点。对于大多数代码而言,可读性和可维护性通常比性能更重要。
Scott Schafer

3
我认为公司环境中可能会出现复杂性,但公司首先要真正关注的是运输:如果可行,它就足够好了,直到有足够的预算来改进应用程序,或者客户再次抱怨质量不佳表演。在临时项目的b2b情况下,这可能很少见。在b2c或竞争激烈的市场(现成的产品)中,它可能会更频繁地出现,从而直接提高了新员工的准入门槛。
didierc

4
@didierc“足够好”也总是在破坏事情。
拉斐尔

1
@didierc 1)好吧,具有CS扎实背景的人确实(正确地)对正确和不正确有很好的直觉,而临时问题解决者可能会犯“简单的”错误。确保乘法编译后的执行恰好是指定的,这是非常重要的,并且是未解决的问题。2)没有
拉斐尔

9

这个问题是很主观的,所以我认为答案取决于它

如果您处理少量数据,则没关系。在这些情况下,通常可以使用任何语言(例如,您的语言提供的标准库)。

但是,当您处理大量数据时,或者由于某些其他原因而坚持认为程序运行速度很快,则必须了解计算的复杂性。如果不这样做,您如何知道应该如何解决问题,或者甚至有多快可以解决问题?但是,仅仅了解理论还不足以成为一名真正的优秀程序员。我相信,要生成极快的代码,您还必须了解机器的工作方式(缓存,内存布局,指令集)以及编译器的工作方式(编译器会尽力而为,但并不完美)。

简而言之,我认为理解复杂性显然会使您成为更好的程序员。


1
我认为您通常有正确的想法,但“主观”不能充分描述此问题;“间接”将是一个更好的词。而且,但是,可以编写非常慢的程序,这些程序不能处理大量数据。我最近在math.se上回答了有关多项式表示/存储的问题。这通常涉及很少的数据,例如,约1000项多项式是典型的。但是实际的性能差异很大(取决于实现的情况,几百或几千秒与几秒钟的乘积)。
嘶嘶声

4

如果正在开发重要算法的人不了解算法复杂性,肯定是一个问题。算法的用户通常依赖于具有良好性能特征的良好实现质量。虽然复杂度不是影响算法性能的唯一因素,但它是一个重要的因素。不了解算法复杂性的人不太可能开发具有有用性能特征的算法。

假设可用的算法质量很好,对于算法用户来说这不是什么大问题。对于使用具有重要的,明确指定的标准库的语言的开发人员而言,这是正确的-他们只需要知道如何选择满足那里需求的算法即可。问题出在库中它们有多种可用的某种类型的算法(例如,排序),因为复杂性通常是两者之间进行选择的标准之一。然后,不了解复杂性的开发人员将无法理解为手头的任务选择有效算法的基础。

还有一些开发人员专注于(需要更好的描述)非算法问题。例如,他们可能专注于开发直观的用户界面。这样的开发人员通常不必担心算法的复杂性,尽管它们可能再次依赖于高质量开发的库或其他代码。


3

它取决于开发的程序,但不取决于您正在使用的数据量,而是取决于您所做的工作类型。

让我们打电话给不了解概念复杂性的程序员。

笨拙的程序员可以执行以下操作:

  • 开发大数据数据库-他不必知道它如何在内部工作,他只需要了解有关开发数据库的规则即可。他知道诸如此类的事情:应该建立索引的地方,...在哪里进行数据冗余更好,在哪里呢?
  • 制作游戏-他只需要研究某些游戏引擎的工作原理并遵循其范例,游戏和计算机图形学就是一个很大的数据问题。考虑单个图片/帧的1920 * 1080 * 32bit = cca 7.9MB ... @ 60 FPS至少为475MB / s。考虑一下,仅一张不必要的全屏图片副本将浪费大约每秒500MB的内存吞吐量。但是,他不需要担心,因为他只使用引擎!

笨拙的程序员不应该这样做:

  • 无论使用的数据大小如何,都要开发非常常用的复杂程序,例如,小数据不会在开发过程中引起不正确解决方案的明显影响,因为它比编译时间要慢,等等。因此,0.5从一个笨拙的程序员的角度来看,对于一个简单的程序来说,它的时间并不是很多,那么,考虑一下服务器服务器,该服务器每秒运行该程序20次。这将需要10cores才能承受该负载!
  • 为嵌入式设备开发程序。嵌入式设备可以处理少量数据,但是它们需要尽可能提高效率,因为冗余操作会带来不必要的功耗

因此,当您只想使用技术时,noobish程序员很好。因此,在开发新解决方案,自定义技术等方面,最好雇用那些没有笨拙的程序员。

但是,如果公司不开发新技术,则仅使用已经制造的技术。雇用熟练和有才华的程序员会浪费人才。同样的道理,如果您不想使用新技术,并且可以使用已经完成的框架将客户的想法放入设计和程序中,那么这将浪费您的时间,要学习您永远不需要的东西,除了如果这是您的爱好,并且您喜欢逻辑挑战。


1
如果使用更中性的标签,或者根本不使用标签,则该答案可能会得到改善,就像使用“无能的程序员”一词的其他答案一样。
Moby Disk

1
我不确定“概念复杂性”是什么意思。我的经验是,对树或哈希表了解不足的人无法就如何索引大型数据库(的一部分)做出明智的决定。
Fizz

3

我有点犹豫在这里写一个答案,但是由于我发现自己对其他几个人不屑一顾[我的一些评论开始聊天],所以我是这样看的...

计算中许多事物都有知识的水平/程度(这个术语,我的意思是计算机科学与信息技术的结合)。计算复杂性无疑是一个广阔的领域(您知道OptP是什么吗?还是Abiteboul-Vianu定理是什么?),并且也承认了很多深度:大多数具有CS学位的人都无法提供用于研究的专家证明。有关计算复杂性的出版物。

ñ2

老实说,我敢于将知道何时应用计算复杂性概念(以及何时可以安全地忽略它们)与在C语言中实现一些对性能敏感的代码以及对性能不敏感的某种常见实践(在Java世界之外)进行比较。 (顺便说一句,这在Julia的谈话中称为“标准妥协”。)知道何时不必考虑性能可以节省编程时间,这也是相当有价值的商品。

还有一点是,了解计算复杂性并不会自动使您擅长优化程序。您需要了解更多与架构相关的内容,例如缓存局部性,[有时]流水线化,以及当今的并行/多核编程。后者既有自己的复杂性理论,又有实际考虑。后者来自2013年SOSP论文 “每个锁定方案都有15分钟的成名。在所有目标体系结构或工作负载上,我们认为这9个锁定方案都没有一个始终优于其他任何一个。严格来说,要寻求最优性,因此,应该根据硬件平台和预期的工作量来选择锁定算法。”


1
从长远来看,开发或找到更好的算法通常比更改对性能敏感的位的编程语言更有利。我同意您的看法,在缺乏对复杂性的了解与过早的优化之间存在紧密的联系-因为它们通常将对性能不太敏感的部分用于优化。
罗布(Rob)2015年

1
实际上,(无意间)Schlemiel Painter的算法比O(n ^ 2)排序要频繁得多。
彼得·莫滕森

-1

如果您不知道big-O,则应该学习它。这并不难,而且确实很有用。从搜索和排序开始。

我确实注意到,很多答案和评论都建议使用概要分析,并且它们几乎总是意味着使用概要分析工具

问题是,就其在查找所需加速内容方面的有效性而言,分析工具无处不在。 在这里,我列出并解释了探查器遭受的误解。

结果是,如果程序比学术活动大,它们可能包含沉睡的巨人,即使是最好的自动探查器也无法暴露这些巨人这篇文章展示了一些如何从探查器中隐藏性能问题的示例。

但是他们不能从这项技术中躲起来


您认为“ Big-Oh”很有用,但是您主张采用另一种方法。另外,我看不到学习“ Big-Oh”(数学)如何“从搜索和排序开始”(算法问题)。
拉斐尔

@Raphael:我不提倡不同的方法-正交的,Big-O是理解算法的基础知识,而在非玩具软件中发现性能问题则是在编写和运行代码之后而不是之前进行的工作。(有时学者对此一无所知,因此他们继续教gprof,弊大于利。)这样做时,您可能会或可能不会发现问题出在使用O(n * n)算法,因此您应该能够认识到这一点。(而且big-O只是算法的数学定义属性,而不是其他主题。)
Mike Dunlavey 2015年

“ 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.