2014-09-04 108 views
2

我解决这个蟒蛇挑战http://coj.uci.cu/24h/problem.xhtml?abb=2634,这就是我的回答如何提高Python代码的速度

c = int(input()) 
l = [] 
for j in range(c) : 
    i = raw_input().split()[1].split('/') 
    l.append(int(i[1])) 
for e in range(1,13) : 
    print e , l.count(e) 

但它并不是最快的蟒蛇解决方案,所以我试图找到如何提高速度,我发现该xrange比范围更快。但是,当我尝试下面的代码它实际上是慢

c = int(input()) 
l = [] 
for j in xrange(c): 
    i = raw_input().split()[1].split('/')[1] 
    l.append(i) 
for e in xrange(1,13) : 
    print e , l.count(`e`) 

,所以我有2个问题:

  1. 我怎样才能提高我的脚本
  2. 的速度,我在哪里可以找到如何信息提高蟒速度

当我一直在寻找这个信息,我发现这样一个https://wiki.python.org/moin/PythonSpeed/PerformanceTips 网站,但它没有指定例如,我F IT是快/慢到使用上面提到的脚本的一部分多次分割的字符串在一行或多行,例如:

i = raw_input().split()[1].split('/')[1] 

VS

i = raw_input().split() 
i = i[1].split('/') 
i = i[1] 

编辑:我有尝试了所有的建议,但我的第一个答案仍然是最快的,我不知道为什么。我的冷杉答案是151毫秒和@ Bakuriu的答案是197毫秒,我的答案使用collections.Counter是188毫秒。

编辑2:请忽略我的上次编辑,我只是​​发现在上述网站中检查您的代码性能的方法不起作用,如果您每次上传相同的代码多次,有些时候,它更慢,有时更快

+7

试图微观优化而不是试图改进整体算法,一般来说,做错了。任何有关“提高Python速度”的公式文档都必然主要由微优化组成,因为大规模算法改进不是Python特有的。如果您非常关心实现细节的重要改进,Python无论如何都是错误的语言。 – 2014-09-04 18:35:50

+4

您可能只是在寻找操作系统时序工件,13个元素上'range'和'xrange'之间的差异很小。你的脚本很慢的原因是你在循环中调用'count'。你应该建立一个合适的计数器(通过使用'collections.Counter')来代替。 – roippi 2014-09-04 18:37:14

+0

@ChalesDuffy谢谢你的回答,我使用python进行这项挑战仅仅是因为我喜欢使用python :) – Xirion11 2014-09-04 18:42:57

回答

6

假设你使用CPython的,金科玉律是尽可能多的工作,可能推到内置的功能,因为这些都是用C语言编写,因此避免解释开销

这就是说,你应该:

  • 避免显式循环时,有一个函数/方法已经做了你要
  • 避免在内部循环昂贵查找什么。在极少数情况下,您可以尽量使用局部变量来存储内置函数。
  • 使用正确的数据结构。不要简单地使用list s和dict s。标准库包含其他数据类型,并且有许多库。考虑哪些应该是解决问题的高效操作,并选择正确的数据结构
  • 避免元编程。如果你需要速度,你不希望简单的属性查找在幕后触发具有复杂逻辑的10个方法调用。 (然而,你并不需要速度元编程真的很酷!)
  • 配置你的代码以找到瓶颈并优化瓶颈。我们通常认为某些具体代码的性能是完全错误的。使用dis模块反汇编字节码。这给你一个简单的方法来看看解释器真的会做什么。如果你真的想知道口译员的工作方式,你应该尝试阅读PyEval_EvalFrameEx的源代码,其中包含口译员的主循环(注意:hic sunt leones!)。

关于CPython,您应该阅读Guido Van Rossum的An optimization anecdote。它提供了许多关于性能如何随各种解决方案而改变的见解。另一个例子可能是this答案(免责声明:它是我的)最快的解决方案对于不习惯CPython工作的人可能非常直观。

另一件好事是研究所有最常用的内置和stdlib数据类型,因为每个数据类型都有正面和负面的比例。在这种特定的情况下,调用list.count()是一项沉重的操作,因为每次执行时都必须扫描整个列表。这很可能是您的解决方案耗费大量时间。

的一种方式,以尽量减少解释的开销是用collections.Counter,这也避免了扫描数据多次:

from collections import Counter 

counts = Counter(raw_input().split('/')[-2] for _ in range(int(raw_input()))) 

for i in range(1, 13): 
    print(i, counts[str(i)]) 

注意,没有必要月份转换为整数,这样就可以避免那些函数调用(假设月份总是以相同的方式写入),不是077)。

此外,我不明白你为什么要分割空白,然后在/时,你可以简单地拆分/并从列表中的一个到最后一个元素。

其他(重要的)优化可能是读取所有stdin以避免多个IO调用,但是这可能不适用于这种情况,因为他们告诉你有多少员工在那里可能意味着他们没有发送EOF。


需要注意的是不同版本的Python有优化代码的完全不同的方式。例如,当您在JIT能够分析和优化的循环中执行简单操作时,PyPy的JIT效果最佳。所以这与你在CPython中所做的相反。

+0

谢谢!它并没有让我想起使用这个-2,我想我很想在每个输入中有一对,我没有看到这种可能性。我刚刚开始迎接这些挑战,所以我有很多要学习。 – Xirion11 2014-09-04 19:36:49