2016-02-19 50 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