在当前(2013年)的Google Code Jam竞赛中,与仅使用40行代码即可解决同一问题的Python人员相比,C ++和Java人员花费了200多行代码。
Python不能直接与C ++和Java相提并论,但是我认为冗长程度的差异可能会对算法的效率产生影响。
与选择语言相比,知道正确的算法有多重要?能否以更好的方式(使用相同的算法)以C ++或Java来实现出色实现的Python程序,这是否与某些编程语言的自然冗长性有关?
在当前(2013年)的Google Code Jam竞赛中,与仅使用40行代码即可解决同一问题的Python人员相比,C ++和Java人员花费了200多行代码。
Python不能直接与C ++和Java相提并论,但是我认为冗长程度的差异可能会对算法的效率产生影响。
与选择语言相比,知道正确的算法有多重要?能否以更好的方式(使用相同的算法)以C ++或Java来实现出色实现的Python程序,这是否与某些编程语言的自然冗长性有关?
Answers:
显然,如果您在Google Code Jam之类的背景下考虑此问题,那么在必须解决算法问题时,算法思考显然更为重要。
但是,在日常生活中,还必须考虑大约一百万个其他因素,这使问题相对于白人少了很多。
只是一个反例:如果您需要在Java上再增加200行,但是公司中的每个人都知道Java,那么这没什么大不了的。如果您可以用5行Python或任何其他语言编写它,但是您将是公司中唯一知道该语言的人-这很重要。实际上如此之大,以至于您甚至都不被允许这样做,而不得不用Java编写它。
从工艺师的角度来看,我们总是尝试使用适合该工作的工具,但是其中的正确一词是如此棘手,以至于人们很容易就将其弄错了。
相反,我发现公司中几乎没有算法思维。仅有少数人拥有它,而普通的joe通常已经很难估计循环,搜索等的运行时复杂性。
但是,就算法竞赛而言,我多年的竞赛经验清楚地告诉我,您应该坚持使用一种语言。速度是一个主要因素,您根本不能浪费时间在工具上,而应将其专用于在期限内解决问题。还要考虑一下,不加思考地编写200行Java代码仍然比手工绘制50行需要大量思考的复杂python代码要快得多,而两者却或多或少地解决了相同的问题。
哦,最后,请确保您了解算法竞争代码与公司生产代码之间的主要区别。我见过出色的算法编码器,他们编写了我在产品中永远不会接受的可怕代码。
我会说,即使在外部比赛中,算法思维也比了解每种特定语言的技巧更为重要。
当然,您希望尽可能地了解您使用的语言,但是语言来来往往,而根据算法进行抽象思考的能力是一种高度可移植的技能。
恰当的例子:如果我没记错的话,前一段时间在Programmers上有一篇文章,有人在采访中抱怨FizzBuzz失败,并将其归咎于他对Java的模运算符缺乏了解。这个结论是错误的-缺乏关于模运算原理的知识,即使没有专用的模运算符,他也无法通过算法思考问题并解决问题。更进一步:Java具有Tree类-如果将来您必须使用不实现该类的语言怎么办?同样,思考问题的能力胜于特定于语言的细节。
我承认这些例子很简单,但它们有助于阐明这一点。
语言很重要。
DARPA和美国海军在大约20年前进行了枪战实验。黑马失控的赢家是Haskell。Ada和C ++都参加了会议。Java不是。
大约在同一时间,普惠公司对喷气发动机控制器项目进行了数据挖掘研究,研究了时间卡和错误跟踪器数据。他们发现Ada使程序员的生产率提高了一倍,缺陷密度是他们使用的任何其他语言的1/4。
Atari曾经使用FORTH来开发视频游戏,而他们使用FORTH的事实被认为是极其专有的。
Paul Graham 关于使用LISP 的评论是众所周知的。Erann Gat 在JPL上对LISP 的评论同样具有说服力,尽管并不为人所知。
在波音777个航电软件是几乎所有的Ada。即使一位主要的分包商必须从中途重新开始,他们的经验也非常好。
语言很重要。
一些要点:
最高的位置通常是C ++ / C / Java,无论该语言与某些其他语言之间的区别是多少行。可能更多的是顶级编码器倾向于选择这些语言而不是其他语言,这可能是因为它们的原始速度。
不幸的是,您不能轻易地在Google Code Jam上看到编程语言,但是我下载了一些顶级语言,据我所记得,这些语言大多是C / C ++。TopCoder(一个流行的在线编程竞赛托管网站)大多具有相似的结果。
因为他们是相当低的水平,我敢肯定你不会在原运行时间(和Java的不落后方面轻松击败C / C ++ 太落后)。根据我的经验,动态类型的语言往往比静态类型的语言要慢得多。最佳解决方案在某些语言中甚至可能还不够快,但这不应该是一般规则。
正确的算法至关重要。如果您从一开始就知道如何解决所有问题(非常详细),并且您是一名优秀,快速的编码员,那么无论您使用哪种语言编写编码,都将很可能会获胜(假定使用该语言的最佳解决方案)足够快)。
直线数不是什么大问题。一旦获得足够的编程经验,您就会知道可以花10分钟编程10行或200行,这全都取决于行的复杂程度。另外,如果您已经对相似的代码进行了数百次编码,那么您将可以很快完成。不太提及顶级C / C ++编码人员经常用来优化其编码时间的所有宏。
弗兰克(Frank)提出了一个很好的观点-(在编程竞赛之外),如果您的公司的整个代码库都使用C或其他语言,而您需要遵循他们的语言,那么您就无法为公司使用Python进行编码。
在语言之间进行切换相当容易,建立多年的算法思维知识也不容易。我愿意打赌,几乎所有优秀的程序员都可以在一周内切换到另一种(大致相似)的语言。也许他/她不够好,不能赢得那种语言的编程比赛(再给它2周),但会降低基础知识。
可以在C ++中更好地实现相同的逻辑吗?当然可以,如果更好,您的意思是更快,内存效率更高。问题是这样做所需的工作量明显更高。此外,从理论上讲,您仍然可以降低级别,并以纯C甚至ASM来实现它,这将花费更长的时间,但是您可能拥有更多的优化代码。
当然,在像Code Jam或TopCoder这样的竞赛中,这也不算大事,因为它只有40行与200行。另一方面,在这种竞赛中,最重要的是算法的时间/空间复杂度。虽然在现实生活中的应用,因人而异,在这些类型的比赛O(n)的算法写在最慢的语言,总是打败O(N²)写在最快的语言。特别是在最糟糕的情况下,将进行多次测试。
但是除了比赛之外,如果我们谈论的是现实生活中的大型项目,那么它不再是40行对200行。在大型项目中,庞大的代码库开始成为问题。此时您将获得:
纯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#时,您可能会认为他/她在谈论语言语法(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的答案)。
总结一下,了解正确的算法会有所帮助。在编码竞赛中,这一点至关重要,尤其是在故意限制执行时间和内存等资源时。在应用程序开发中,它是受欢迎的,但通常并不重要。那里的可维护性更重要。可以通过应用正确的设计模式,具有良好的体系结构,可读代码和相关文档来实现,而所有这些方法都严重依赖于内部和第三方库。因此,我发现更重要的是要知道已经发明了哪种车轮,如何安装以及如何制造自己的车轮。
Main
并且在Main
方法内部没什么东西(我的输入类的实例以及输出流和case循环)。
如果要参加定时编程竞赛,则应学习竞赛中允许的最具表现力的语言。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行程序之间的差异更大时,差异甚至更大。然后是一个由五个人和一个经理组成的团队与一个或两个人组成的团队之间的区别。
在推断“ 40 LoC vs 200 LoC”的示例时,他说:“好吧,总LoC中只有五分之一的速度明显更快,因此它必须更好”,这似乎很诱人,但我确实认为在此几乎找不到真相。
在我看来,针对最少的LoC进行优化几乎不是一个好主意。是的,编写的每个LoC都有潜在的错误,您不必调试从未编写的内容等。重点是针对可读性和去耦性进行优化。与编写1k LoC模块相反,使用20行大的正则表达式解决问题并不重要。正则表达式将是一堵不透明的墙,极易出现错误,难以理解,噩梦般地改变而不会以无法理解的方式改变其行为等。
摆脱不会增加任何价值的样板和冗长是一件好事,但另一方面,使用Java或C#这样的东西,对设计模式和工具如reshaper有所了解,可以使您在重构代码时具有很大的灵活性,随着时间的流逝清理它,分解东西等等,如果您将它编写为一个更小的python脚本或ruby应用程序,这将更加困难。
一个非常有说服力的比较:我宁愿有10万个经过测试覆盖的解耦C#代码的LoC,其中填充了“过大”的东西,例如策略模式,工厂等,而不是一个20k的python应用程序,只是“完成工作”。不管代码是否多出5倍,该体系结构每天都能胜出。
我完全同意某些语言在某些语言中会更轻松,更方便,但是我相信更多的是根据需要的工具和需求(并可能在不久的将来)选择语言。