如何优化这个python问题?

时间:2011-03-21 16:20:42

标签: python optimization sorting

Here是问题所在。我的解决方案是:

import sys
LIST_ITEM = []
NUMBER_OF_TEST_CASES = int(raw_input())
def non_decreasing(my_list):
    if len(my_list) < 2:
        return my_list
    my_list.sort()
    return my_list

if __name__ == "__main__":
    if NUMBER_OF_TEST_CASES >= 1000001 or NUMBER_OF_TEST_CASES <= 0:
        sys.exit()

    for val in range(1, NUMBER_OF_TEST_CASES+1):
        x = int(raw_input())

        if x >= 1000001 or x<0:
            sys.exit()
        else:
            LIST_ITEM.append(x)
    values =  non_decreasing(LIST_ITEM)
    for i in values:
        print i

但它告诉我Time Limit ExceededHere is my solution link

3 个答案:

答案 0 :(得分:3)

编辑:好吧,显然你不应该有骗局吗?这改变了一些事情,你当前的代码无论如何都无法正常工作。暂时离开我的旧答案......

简单的答案就是简介!我使用:

生成数据
import random
out = open('foobar.txt', 'w')

total = random.randint(100000, 1e6)
out.write('%s\n' % total)

for x in xrange(total):
    out.write('%s\n' % random.randint(0, 1e6))

然后我使用命令time python -m cProfile -o foo.profile foo.py < foobar.txt > fooout.txt && gprof2dot -f pstats foo.profile | dot -Tpng -o foo_profile.png进行测试。这会使用gprof2dot工具生成这个漂亮的图形,并报告运行所花费的时间(在我的系统上具有266k输入行的1.9秒)。 sort -n foobar.txt > foo_sorted.txt是我的黄金标准,约为0.41秒。

因此,您可以看到44.81%的时间花在基本代码上,38.82%用于raw_input,14%用于排序。

profile output

接下来我们开始优化。

首先是将代码放入方法中。只需在所有代码周围添加def main(),最后if __name__ == '__main__': main()。对我来说,削减了运行时间约5%,减少到1.8秒,并将raw_input移动到我们负载的最高百分比。

让我们看看我们是否可以减少这种情况。也许用直接使用sys.stdin替换raw_input?我假设raw_input是为交互式使用而设计的,并且可能没有很好地描述,因为它(可能)并非用于大量使用。通过用诸如sys.stdin.readline()之类的东西替换raw_input,我们应该使用更有效的代码路径。对我而言,运行时间从1.8秒降至0.952秒。节省一半!这是现在的代码和配置文件输出。

import sys 

def non_decreasing(my_list):
    if len(my_list) < 2:
        return my_list
    my_list.sort()
    return my_list

def main():
    LIST_ITEM = []
    NUMBER_OF_TEST_CASES = int(sys.stdin.readline().strip())

    if NUMBER_OF_TEST_CASES >= 1000001 or NUMBER_OF_TEST_CASES <= 0:
        sys.exit()

    for x in sys.stdin:
        x = int(x.strip())

        if x >= 1000001 or x<0:
            sys.exit()
        else:
            LIST_ITEM.append(x)
    values =  non_decreasing(LIST_ITEM)
    for i in values:
        print i

if __name__ == '__main__':
    main()

Revised 所以这是一个好的开始。我们现在不到原始运行时的一半。让我们来看看现在的速度有多慢。 main函数,sort,strip()和append。也许我们可以优化主要的东西?好吧,我注意到我们正逐一打印出这些线条。我们可以用一个sys.stdout.write()来切换它,看看是否有帮助?我试过sys.stdout.writelines([str(x) for x in values])它实际上看起来比较慢,所以我觉得打印效率很高。让我们坚持下去。

我们还能减少什么?也许if x >= 1000001 or x<0:声明?这完全是必要的吗?看起来我们可以通过删除它来轻松删除几百分之一秒。

还有什么?也许整个non_decreasing事情是不必要的,我们可以只使用LIST_ITEM.sort()?我想你的检查和额外的函数调用实际上并没有加快速度。是的,加快了一点!

理想情况下,在这一点上我们会做一些事情,比如不从输入中删除换行符,排序为字符串然后将其写出来。不幸的是,这没有得到理想的排序:(所以让我们尝试一些替代方案

  1. for x in sys.stdin: values.append(x[:-1])
  2. x.rstrip()
  3. x.rstrip('\n')
  4. values = sys.stdin.split('\n')
  5. values = sys.stdin.read().splitlines()
  6. values = sys.stdin.readlines()
  7. 在我的测试中,#1上的变体是最快的并保持正确性,在〜.783s。这是我的最终代码:

    import sys
    
    def main():
        NUMBER_OF_TEST_CASES = int(sys.stdin.readline().strip())
        if NUMBER_OF_TEST_CASES >= 1000001 or NUMBER_OF_TEST_CASES <= 0:
            sys.exit()
    
        values = [int(x) for x in sys.stdin.readlines()]
        values.sort()
        for i in values:
            print i
    
    if __name__ == '__main__':
        main()
    

    最终的gprof2dot个人资料信息...... enter image description here

答案 1 :(得分:2)

不要排序!

只需创建一个长度为1e6的零数组(也许是numpy),每当遇到数字i时,通过列表设置读取[i] = 1,然后打印出所有非零条目。

我认为这有效:

import numpy as np
import sys

nn = 1000000
a = np.zeros(nn+1, dtype=np.int)

for l in sys.stdin:
    a[np.int(l)]=1

for i in xrange(nn+1):
    if a[i]: print i

我希望有办法加快i / o。

答案 2 :(得分:1)

由于输入数据值和大小限制为10 ^ 6,因此您可以简单地初始化10 ^ 6值的数组并跟踪出现的值。排序和重复检测“免费提供”。

此方法将具有初始成本(初始化阵列),但对于大输入尺寸则值得。

实施例:     导入数组     来自sys import stdin     来自itertools import repeat

low  = 1000001
high = -1
data = array.array('i', repeat(-1, 1000000))
count = int(stdin.readline().strip())

while count:
    v = int(stdin.readline().strip())
    count -= 1
    data[v] = v
    low  = min(v, low)
    high = max(v, high)

for v in xrange(low, high+1):
    if data[v] > 0:
        print v

请注意,我使用了array,因为预先知道了大小和类型,因此我们可以绕过使用list带来的开销。

如果对内存使用有限制,可以使用位数组而不会减小data的大小,但在设置和迭代值时会产生一些额外的开销(和复杂性)。