2016-02-19 109 views
0

我是Python新手,目前我正在解決問題以提高編程技能。目前我正在處理一個問題,我必須stable sort輸入和輸出反向穩定的排序值。我編寫了它,並在網站的在線裁判和一個測試用例(不知道測試用例)中執行代碼,我得到了Memory Limit Exceeded錯誤。因此,經過一番調查,我發現代碼中存在memory leak,代碼不完全有效。所以我已經安裝了python的memory_profiler來監視進程的內存消耗以及我的代碼的內存消耗的逐行分析。識別python中的內存泄漏 - 內存分析器

請在下面找到從memory_profiler獲取的輸入詳細信息,代碼,輸出和內存分析分析。

輸入:

8 
1 2 
16 3 
11 2 
20 3 
3 5 
26 4 
7 1 
22 4 

代碼:

from collections import OrderedDict 
@profile 
def test_1(): 
    print "Enter the number: " 
    n = raw_input() 
    k = [] 
    v = [] 
    print "Enter ID and M: " 
    for i in range(0,int(n)): 
     a, b = raw_input().split(' ') 
     k.append(a) 
     v.append(b) 
    d = OrderedDict(zip(k,v)) 
    sorted_items = sorted(d.items(), key=lambda (k,v):int(v), reverse=True) 
    for i, j in sorted_items: 
     print i, j 

if __name__ == '__main__': 
     test_1() 

輸出:

Line # Mem usage Increment Line Contents 
================================================ 
    2 10.520 MiB 0.000 MiB @profile 
    3        def test_1(): 
    4 10.531 MiB 0.012 MiB  print "Enter the number: " 
    5 10.551 MiB 0.020 MiB  n = raw_input() 
    6 10.551 MiB 0.000 MiB  k = [] 
    7 10.551 MiB 0.000 MiB  v = [] 
    8 10.551 MiB 0.000 MiB  print "Enter ID and M: " 
    9 10.551 MiB 0.000 MiB  for i in range(0,int(n)): 
    10 10.551 MiB 0.000 MiB    a, b = raw_input().split(' ') 
    11 10.551 MiB 0.000 MiB    k.append(a) 
    12 10.551 MiB 0.000 MiB    v.append(b) 
    13 
    14 10.551 MiB 0.000 MiB  d = OrderedDict(zip(k,v)) 
    15 10.555 MiB 0.004 MiB  sorted_items = sorted(d.items(), key=lambda (k,v):int(v), reverse=True) 
    16 10.555 MiB 0.000 MiB  for i, j in sorted_items: 
    17 10.555 MiB 0.000 MiB    print i, j 

預期輸出(我能獲得所需的輸出):

3 5 
26 4 
22 4 
16 3 
20 3 
1 2 
11 2 
7 1 

此代碼對於更高輸入或更高數字無效嗎?從分析中我可以看到只有更少的內存被利用,但對於那個特定的測試案例,我可以看到內存利用率超過了16MB。 有人能告訴我我在哪裏做錯了。我的方法錯誤或流程錯誤?你能告訴我爲什麼我無法按預期得到產出嗎?提前致謝。任何幫助將非常感激。

+0

預期輸出是什麼?你爲什麼認爲它應該使用更少的內存? – gil

+0

更新了問題以顯示預期的輸出 – Dev

+0

看起來您的代碼是正確的,就像我的修訂版本一樣。既然沒有辦法知道測試用例(設計這個在線課程的人應該真的重新考慮這個問題),我們來看看是否刮掉列表並將'range'改爲'xrange'會有幫助。 – gil

回答

1

我在寫評論,但時間太長了,所以我想我不妨將它升級爲答案。

首先,profile修飾器本身就使用了相當多的內存。正如你可以看到:

from memory_profiler import profile 
@profile 
def foo(): 
    pass 

,我得到

Line # Mem usage Increment Line Contents 
================================================ 
    2  28.5 MiB  0.0 MiB @profile 
    3        def foo(): 
    4  28.5 MiB  0.0 MiB  pass 

您的號碼可能會有所不同(我運行的Python 3,在IDE不會少),但它基本上是與你的函數一樣的東西。幾乎所有的內存使用來自@profile行(10.520 MiB),你的函數增加了什麼(見增量列)很簡單(0.36 MiB)。

結果是,你不應該有任何問題的外觀(如果你發佈的已經是你的整個代碼,我想它是)。我不知道測試用例可能會給你什麼Memory Limit Exceeded。我們真的需要知道該測試用例是用來調查問題的。

也就是說,一個改進可以使你的代碼更有效率,特別是對於大量的輸入。您不需要構建中間列表(代碼中的kv)。寫直接在字典:

d = OrderedDict() 
for i in range(int(n)): # Note you don't need range(0, x); just range(x) 
    a, b = raw_input().split() # No need for an argument to split, either 
    d[a] = b 

更妙的是,你可以避開for迴路,並使用更高效的發電機的表達:

d = OrderedDict(raw_input().split() for _ in range(int(n))) 

生成表達式形式(foo something_like_a_for_loop)的表達(形式描述here);如果它是函數的唯一參數,則可以省略括號括號。它在很多方面就像是一個列表:你可以使用for來迭代它,你可以使用list來得到一個列表,你可以在需要迭代器時使用它。但是,當列表很長時,它會比等效列表佔用更少的空間。 (但也有不同之處:一創expr可以在迭代一次,不能被索引並沒有len等,您可以閱讀更多關於它here

還有其他小你可以做的改進。全部納入下面的重寫

def test_1(): 
    n = int(raw_input('Enter the number: ')) 
    d = OrderedDict(raw_input().split() for _ in range(n)) 
    sorted_items = sorted(d.items(), key=lambda k_v: int(k_v[1]), reverse=True) 
    for i, j in sorted_items: 
     print i, j 
+0

謝謝你解釋我幫助我提供了一個更高效的解決方案,我會理解並學習它,我真的不知道那個測試用例是什麼,因爲它是一個在線評委,用他們自己的測試用例來檢查代碼。幾乎不可能得到那個特定的測試用例,但讓我試一試這個解決方案,看看它會導致什麼。 – Dev

+0

這就是我提供的完整解決方案 – Dev

+1

@Dev另一件事,因爲你在python 2上:使用'xrange 'range'不是'range','range'一次創建整個列表並且可能很大;'xrange'(它變成了'range' in python 3)一次返回一個值並佔用可忽略的內存:https://docs.python.org/2/library/functions.html#xrange – gil