2014-09-04 107 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