算法比编程语言重要吗?


35

在当前(2013年)的Google Code Jam竞赛中,与仅使用40行代码即可解决同一问题的Python人员相比,C ++和Java人员花费了200多行代码。

Python不能直接与C ++和Java相提并论,但是我认为冗长程度的差异可能会对算法的效率产生影响。

与选择语言相比,知道正确的算法有多重要?能否以更好的方式(使用相同的算法)以C ++或Java来实现出色实现的Python程序,这是否与某些编程语言的自然冗长性有关?


16
曾经有人说过(我相信),您使用的编程语言会影响您对问题的思考方式。这意味着非常不同的编程语言可能适用于不同类别的问题。
Joris Timmermans

1
这在很大程度上取决于您在LOC之外的工作规模。有些语言只是不支持速度或并发需求。算法很重要,但是有时如果x语言的速度比z语言的y慢y倍,那么无论冗长,您都无法使用x。
钻机

值得注意的是,我在学校中学到的唯一一件事是,每个人每行代码都有一个错误,该错误与所使用的代码无关而保持不变。因此,如果一种允许您用较少的代码行执行的语言导致较少的错误,那么您可以更快地将其投放市场,并且当用户使用它时,出现错误的机会就更少。因此,在我看来,我将为公司中的其他所有人都知道进行该项目所需的工作选择最佳的语言。
Travis Pessetto

5
@Travis:“不管语言如何,每个SLOC的缺陷率都保持不变”,但事实并非如此。见约翰的回答。
Ben Voigt

现在,您让我考虑使用F#作为语言参加下一场比赛!
code4life 2013年

Answers:


73

显然,如果您在Google Code Jam之类的背景下考虑此问题,那么在必须解决算法问题时,算法思考显然更为重要。

但是,在日常生活中,还必须考虑大约一百万个其他因素,这使问题相对于白人少了很多。

只是一个反例:如果您需要在Java上再增加200行,但是公司中的每个人都知道Java,那么这没什么大不了的。如果您可以用5行Python或任何其他语言编写它,但是您将是公司中唯一知道该语言的人-这很重要。实际上如此之大,以至于您甚至都不被允许这样做,而不得不用Java编写它。

从工艺师的角度来看,我们总是尝试使用适合该工作的工具,但是其中的正确一词是如此棘手,以至于人们很容易就将其弄错了。

相反,我发现公司中几乎没有算法思维。仅有少数人拥有它,而普通的joe通常已经很难估计循环,搜索等的运行时复杂性。

但是,就算法竞赛而言,我多年的竞赛经验清楚地告诉我,您应该坚持使用一种语言。速度是一个主要因素,您根本不能浪费时间在工具上,而应将其专用于在期限内解决问题。还要考虑一下,不加思考地编写200行Java代码仍然比手工绘制50行需要大量思考的复杂python代码要快得多,而两者却或多或少地解决了相同的问题。

哦,最后,请确保您了解算法竞争代码与公司生产代码之间的主要区别。我见过出色的算法编码器,他们编写了我在产品中永远不会接受的可怕代码。


1
+1表示“还有数百万个其他因素需要考虑”
ozz 2013年

1
我还要补充一点,如果这是您要解决的功能性问题,那么请天天使用功能性语言!因此,我认为您应该在每种主要编程范例中真正使用一种语言。
Martijn Verburg

6
+1为最后一句话。
Shivan Dragon

4
+1行代码本身就是一个糟糕的指标。我们需要衡量可维护性,而不是代码行。200行的类型安全代码可能比50行的Python更易于维护。
Phil

2
@Phil:200行Python可能比50行类型安全代码更具可维护性。假设编写良好的代码,我从未见过在类型安全的语言中有那么多的清晰度优势。
David Thornley

17

我会说,即使在外部比赛中,算法思维也比了解每种特定语言的技巧更为重要。

当然,您希望尽可能地了解您使用的语言,但是语言来来往往,而根据算法进行抽象思考的能力是一种高度可移植的技能。

恰当的例子:如果我没记错的话,前一段时间在Programmers上有一篇文章,有人在采访中抱怨FizzBu​​zz失败,并将其归咎于他对Java的模运算符缺乏了解。这个结论是错误的-缺乏关于模运算原理的知识,即使没有专用的模运算符,他也无法通过算法思考问题并解决问题。更进一步:Java具有Tree类-如果将来您必须使用不实现该类的语言怎么办?同样,思考问题的能力胜于特定于语言的细节。

我承认这些例子很简单,但它们有助于阐明这一点。


14

语言很重要。

DARPA和美国海军在大约20年前进行了枪战实验。黑马失控的赢家是Haskell。Ada和C ++都参加了会议。Java不是。

大约在同一时间,普惠公司对喷气发动机控制器项目进行了数据挖掘研究,研究了时间卡和错误跟踪器数据。他们发现Ada使程序员的生产率提高了一倍,缺陷密度是他们使用的任何其他语言的1/4。

Atari曾经使用FORTH来开发视频游戏,而他们使用FORTH的事实被认为是极其专有的。

Paul Graham 关于使用LISP 的评论是众所周知的。Erann Gat 在JPL上对LISP 的评论同样具有说服力,尽管并不为人所知。

波音777个航电软件是几乎所有的Ada。即使一位主要的分包商必须从中途重新开始,他们的经验也非常好。

语言很重要。


显然,java是在您链接到该实验之后发布的。
toasted_flakes 2013年

该文章于1994年发布。Java的首次公开发布是1995
。– Alessandro Teruzzi 2016年

关键不是说您在某个特定实验中是否曾使用过您最喜欢的语言。关键是该语言很重要。大量的传闻研究证明了这一点。值得注意的是,尽管Ada尽管遭到美国程序员的普遍拒绝,但Ada仍在欧洲大量使用,特别是对于高可靠性系统,并且仍在美国的某些现场系统中使用。
约翰·斯特罗姆

7

一些要点:

  • 最高的位置通常是C ++ / C / Java,无论该语言与某些其他语言之间的区别是多少行。可能更多的是顶级编码器倾向于选择这些语言而不是其他语言,这可能是因为它们的原始速度。
    不幸的是,您不能轻易地在Google Code Jam上看到编程语言,但是我下载了一些顶级语言,据我所记得,这些语言大多是C / C ++。TopCoder(一个流行的在线编程竞赛托管网站)大多具有相似的结果。

  • 因为他们是相当低的水平,我敢肯定你不会在原运行时间(和Java的不落后方面轻松击败C / C ++ 落后)。根据我的经验,动态类型的语言往往比静态类型的语言要慢得多。最佳解决方案在某些语言中甚至可能还不够快,但这不应该是一般规则。

  • 正确的算法至关重要。如果您从一开始就知道如何解决所有问题(非常详细),并且您是一名优秀,快速的编码员,那么无论您使用哪种语言编写编码,都将很可能会获胜(假定使用该语言的最佳解决方案)足够快)。

  • 直线数不是什么大问题。一旦获得足够的编程经验,您就会知道可以花10分钟编程10行或200行,这全都取决于行的复杂程度。另外,如果您已经对相似的代码进行了数百次编码,那么您将可以很快完成。不太提及顶级C / C ++编码人员经常用来优化其编码时间的所有宏。

  • 弗兰克(Frank)提出了一个很好的观点-(在编程竞赛之外),如果您的公司的整个代码库都使用C或其他语言,而您需要遵循他们的语言,那么您就无法为公司使用Python进行编码。

  • 在语言之间进行切换相当容易,建立多年的算法思维知识也不容易。我愿意打赌,几乎所有优秀的程序员都可以在一周内切换到另一种(大致相似)的语言。也许他/她不够好,不能赢得那种语言的编程比赛(再给它2周),但会降低基础知识。


错误:从某些代码竞赛站点下载几种解决方案是一项权威的科学研究,足以得出结论,您肯定知道某个职位的最高职位。
Lie Ryan

@LieRyan是的,但是参加了(可能是)最受欢迎的此类网站(TopCoder)上的几十场编程竞赛(据我所知),并且始终将C / C ++ / Java的大多数顶级职位视为相当重要。另外,我说“倾向于”而不是“总是”。
Dukeling 2013年

不同意的是“直线数不是很大”。代码是敌人
jk。

6
@jk。我应该突出显示“这样”吗?这很重要,但不是alpha和omega。您更喜欢几行而不是可读性?我当然不会。我将把一些简单的if语句带到一个非常混乱,不可读,位移,乘法,除法,加法,减法,XORing,ANDing的多条件表达式中。可能不是您在说什么,但这是减少行数的原因。我更多地是在谈论在几行中实现复杂的事情,或者在多行中实现简单的事情;后者通常需要更少的时间。
2013年

5

可以在C ++中更好地实现相同的逻辑吗?当然可以,如果更好,您的意思是更快,内存效率更高。问题是这样做所需的工作量明显更高。此外,从理论上讲,您仍然可以降低级别,并以纯C甚至ASM来实现它,这将花费更长的时间,但是您可能拥有更多的优化代码。

当然,在像Code Jam或TopCoder这样的竞赛中,这也不算大事,因为它只有40行与200行。另一方面,在这种竞赛中,最重要的是算法的时间/空间复杂度。虽然在现实生活中的应用,因人而异,在这些类型的比赛O(n)的算法写在最慢的语言,总是打败O(N²)写在最快的语言。特别是在最糟糕的情况下,将进行多次测试。

但是除了比赛之外,如果我们谈论的是现实生活中的大型项目,那么它不再是40行对200行。在大型项目中,庞大的代码库开始成为问题。此时您将获得:

C ++与Python?

在此处输入图片说明

纯Python很慢。这就是为什么用C语言编写标准Python解释器(CPython)的原因。实际上,所有内置函数都以高度优化的C语言编写。Python还可以轻松地与C库(通过ctypes或作为本地cpython模块)和C ++库结合使用。通过Boost :: Python。这样,您就可以使用Python(一种灵活的语言)编写高级逻辑,从而可以快速进行原型设计和自适应(这意味着您可以花费更多时间来调整和改进算法)。OTOH,您可以在C或C ++模块中编写较低级的库函数。这种方法的一个很好的例子是SciPy,它是Python库,但实际上它使用了高度优化的数字库,例如ATLAS,LAPACK,Intels MKL或AMD的ACML。


您所写的内容只能刮擦表面。您假设并非所有人都共享“更好”的概念。质量始终是适合自己目标的问题。C ++编程并不总是适合每个目标。
reinierpost

1
@reinierpost:这就是为什么我写了很多文章。在某些情况下,您提到C ++不太适合,但不是因为它做不到。不合适,因为这会占用过多的开发人员资源。
vartec

而且,在这种情况下并没有更好。
reinierpost 2013年

2
实际上,这就是在许多行业中发生的事情,例如,游戏中有很多Lua代码将C ++代码粘合在一起,从而提高了性能和生产力。
gbjbaanb

4

在我看来,人们通俗地认为“编程语言”实际上是三件事:

  1. 语言类型和语法
  2. 语言IDE
  3. 语言的可用库

例如,当有人在讨论中提出C#时,您可能会认为他/她在谈论语言语法(1),但是95%的确定该讨论将涉及.Net框架(3)。如果您不打算设计一种新语言,则很难隔离(1)并忽略(2)和(3),这通常是毫无意义的。这是因为IDE和标准库是“舒适因素”,这些因素直接影响使用特定工具的体验。

最近几年,我也参加了Google Code Jam。我第一次选择C ++,因为它对读取输入有很好的支持。例如,从C ++中的标准输入读取三个整数看起来像这样:

int n, h, w;
cin >> n >> h >> w;

在C#中,情况如下:

int n, h, w;
string[] tokens = Console.ReadLine().Split(' ');
n = int.Parse(tokens[0]);
h = int.Parse(tokens[1]);
w = int.Parse(tokens[2]);

对于一个简单的功能,这要花很多精力。在多行输入的C#中,事情变得更加复杂。也许我只是当时还没有想出更好的方法。无论如何,我没有通过第一轮比赛,因为我有一个无法在第一轮比赛结束前纠正的错误。具有讽刺意味的是,输入读取方法混淆了该错误。问题很简单,输入的数字对于32位整数来说太大了。在C#int.Parse(string)中将引发异常,但在C ++中,文件输入流将设置特定的错误标志,并且无提示地失败,使毫无怀疑的开发人员不知道问题。

两个示例都演示了如何使用库而不是语言语法。第一个演示了详细程度,另一个演示了可靠性。许多库都移植到了多种语言,并且某些语言可以使用不是专门为它们构建的库(请参阅@vartec关于带有C库的Python的答案)。

总结一下,了解正确的算法会有所帮助。在编码竞赛中,这一点至关重要,尤其是在故意限制执行时间和内存等资源时。在应用程序开发中,它是受欢迎的,但通常并不重要。那里的可维护性更重要。可以通过应用正确的设计模式,具有良好的体系结构,可读代码和相关文档来实现,而所有这些方法都严重依赖于内部和第三方库。因此,我发现更重要的是要知道已经发明了哪种车轮,如何安装以及如何制造自己的车轮。


1
可能的话,准备很重要。使用Google Code Jam,我有一个小型图书馆,可以读取输入并根据需要显示输出,并将该代码包含在提交的内容中。
马克·赫德

第二次我做了类似的事情,但作为项目模板。它创建一个源文件,该文件的下面有一个输入类,Main并且在Main方法内部没什么东西(我的输入类的实例以及输出流和case循环)。
Orionii皇帝'13年

我不记得我上次从stdin阅读的内容。给我一个可以粘贴在JSON解析器中的文件。
gnasher729

2

如果要参加定时编程竞赛,则应学习竞赛中允许的最具表现力的语言。Perl可能是最好的选择,其次是Ruby或Python。您仍然需要使用算法的良好工具,但是至少您不会陷入类似这样的麻烦

Integer prev = b.get(k)
if (prev == null) prev = 0
Integer v = a.get(k);
if (v == null) v = 0;
b.put(prev + v);

代替

b[k] += a[k]

不用担心学习几个库。它们非常相似,并且文档在线。精通更富表现力的语言将使您成为一个不那么富表现力的语言的更好(但可能很沮丧)的程序员。相反的说法是不正确的。

NB

200行代码和40行代码之间的差异是巨大的,当200,000行程序和40,000行程序之间的差异更大时,差异甚至更大。然后是一个由五个人和一个经理组成的团队与一个或两个人组成的团队之间的区别。


3
(a)我知道C / C ++ / Java往往是编程竞赛中的头等大事。(b)许多人认为C / C ++是“最强大的语言”(绝对高于Perl / Ruby / Python)。(c)由于运算符重载,C ++代码看起来几乎与第二个示例相同。(d)仅在以下情况下才需要进行这种广泛的检查(在Java中是吗?):(1)您不知道自己在做什么。(2)数据的性质是必需的(在编码竞赛中不会发生)。(3)您正在编写供其他人使用的代码(不适用)。
2013年

1
@Dukeling:根据这项研究(page.mi.fu-berlin.de/prechelt/Biblio/jccpprtTR.pdf),脚本语言允许更快的开发和更小的源代码。根据另一项研究(flownet.com/gat/papers/lisp-java.pdf),Lisp比脚本语言提供了更高的生产力。根据上面引用的第二项研究,事实证明Lisp代码几乎与C ++代码一样快,而编写时间却更少。
Giorgio

“在200,000行程序和40,000行程序之间”:我认为您必须区别对待。由于编程语言的详细程度(语法)而引起的差异不会增加代码的复杂性,因此对所需的维护工作几乎没有影响。另一方面,由于语言功能的不同,您可以具有不同的行数。例如,在Python中,您不必管理内存,而在C语言中,您必须自己实现所有内存管理。然后,我同意您的观点,即在C代码中您具有更多功能,并且您肯定需要额外的维护时间。
Giorgio

@Giorgio我不是在争论开发时间或源代码的大小,纯粹是基于大量经验,在编程竞赛中实际上会发生什么。
2013年

1
我引用的是两篇科学论文(IMO值得一看),我并不是在谈论网页上的人们对此的看法。观点广泛传播的事实并不自动意味着它是有效的。:-)至少,必须以某种严格的方式进行验证。
Giorgio

2

可以用任何编程语言实现任何算法。毕竟,语法并不重要。但是使用像Python这样的高级语言确实有其自身的优势。更少的工作和更少的编码量。因此,要在Python中实现算法,您需要的行数应少于C等低级语言所要求的行数。

Python在其库中内置了大多数数据结构。但是在C语言中,我们需要从头开始,并使用一种结构来构建它们。当然,高级语言和低级语言之间是有区别的,但是该语言不应阻止您实现任何算法。


2

在推断“ 40 LoC vs 200 LoC”的示例时,他说:“好吧,总LoC中只有五分之一的速度明显更快,因此它必须更好”,这似乎很诱人,但我确实认为在此几乎找不到真相。

在我看来,针对最少的LoC进行优化几乎不是一个好主意。是的,编写的每个LoC都有潜在的错误,您不必调试从未编写的内容等。重点是针对可读性和去耦性进行优化。与编写1k LoC模块相反,使用20行大的正则表达式解决问题并不重要。正则表达式将是一堵不透明的墙,极易出现错误,难以理解,噩梦般地改变而不会以无法理解的方式改变其行为等。

摆脱不会增加任何价值的样板和冗长是一件好事,但另一方面,使用Java或C#这样的东西,对设计模式和工具如reshaper有所了解,可以使您在重构代码时具有很大的灵活性,随着时间的流逝清理它,分解东西等等,如果您将它编写为一个更小的python脚本或ruby应用程序,这将更加困难。

一个非常有说服力的比较:我宁愿有10万个经过测试覆盖的解耦C#代码的LoC,其中填充了“过大”的东西,例如策略模式,工厂等,而不是一个20k的python应用程序,只是“完成工作”。不管代码是否多出5倍,该体系结构每天都能胜出。

我完全同意某些语言在某些语言中会更轻松,更方便,但是我相信更多的是根据需要的工具和需求(并可能在不久的将来)选择语言。

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.