【问题标题】:How to optimize this python script further?如何进一步优化这个 python 脚本?
【发布时间】:2012-07-20 10:27:00
【问题描述】:

我创建了这个脚本来计算 python 中的string similarity。有什么办法可以让它运行得更快吗?

tries = input()
while tries > 0:
    mainstr = raw_input()
    tot = 0
    ml = len(mainstr)
    for i in xrange(ml):
        j = 0
        substr = mainstr[i:]
        ll = len(substr)
        for j in xrange(ll):
            if substr[j] != mainstr[j]:
                break
            j = j + 1
        tot = tot + j
    print tot
    tries = tries - 1

编辑:经过一些优化后,这是代码,但这还不够!

tries = int(raw_input())
while tries > 0:
    mainstr = raw_input()
    tot = 0
    ml = len(mainstr)
    for i in xrange(ml):
        for j in xrange(ml-i):
            if mainstr[i+j] != mainstr[j]:
                break
            j += 1
        tot += j
    print tot
    tries = tries - 1

EDIT 2:第三版代码。还是不行!

def mf():
    tries = int(raw_input())
    for _ in xrange(tries):
        mainstr = raw_input()
        tot = 0
        ml = len(mainstr)
        for i in xrange(ml):
            for j in xrange(ml-i):
                if mainstr[i+j] != mainstr[j]:
                    break
                j += 1
            tot += j
        print tot
mf()

【问题讨论】:

  • 或许更适合Code Review
  • 嗯,j = j + 1 语句当然可以删除,因为j 将立即被覆盖。 j = 0 语句也是如此。
  • 使用tries = int(raw_input())
  • 你可以用for _ in xrange(tries)代替while循环
  • @EdwinDrood:我的机器和 ideone 都这么说 xrange() is faster than while loop。尽管在您的情况下这无关紧要(测试次数

标签: python optimization micro-optimization


【解决方案1】:

如果您使用i = mainstr.find(mainstr[0], i+1) 而不是检查所有i,您可以通过一个常数因子来改进它。 i==0 的特殊情况也有帮助。

将代码放入函数中。它还可能以恒定的速度加快速度。

使用for ... else: j += 1 避免在每一步增加j

尝试找到一个比 O(n**2) 更好的算法,该算法利用您比较字符串的所有后缀这一事实。

straight-forward C implementation比CPython快100倍(Pypy快10-30倍)并通过挑战:

import os

def string_similarity(string, _cp=os.path.commonprefix):
    return sum(len(_cp([string, string[i:]])) for i in xrange(len(string)))

for _ in xrange(int(raw_input())):
    print string_similarity(raw_input())

上述优化只提供了百分之几的改进,不足以通过 CPython 中的挑战(Python 时间限制仅大 8 倍)。

两者之间几乎没有区别(在 CPython 中):

def string_similarity(string):
    len_string = len(string)
    total = len_string # similarity with itself
    for i in xrange(1, len_string):
        for n, c in enumerate(string[i:]):
            if c != string[n]:
                break
        else:
            n += 1

        total += n
    return total

还有:

def string_similarity(string):
    len_string = len(string)
    total = len_string # similarity with itself
    i = 0
    while True:
        i = string.find(string[0], i+1)
        if i == -1:
            break
        n = 0
        for n in xrange(1, len_string-i):
            if string[i+n] != string[n]:
                break
        else:
            n += 1

        total += n
    return total

【讨论】:

  • @larsmans:在模块级别和函数内部尝试一些简单的操作,例如:i = 0 \n while i < 1000000: i += 1。自己看吧。
  • 我立刻相信了你,我只是想知道它是如何工作的 :) 顶级代码的编译方式不同吗?
  • @larsmans:我猜访问全局名称比访问本地慢。
【解决方案2】:

您可以跳过循环内的内存分配。 substr = mainstr[i:] 不必要地分配了一个新字符串。你只在substr[j] != mainstr[j]中使用,相当于mainstr[i + j] != mainstr[j],所以不需要构建substr

内存分配很昂贵,因此您需要避免在紧密循环中使用它们。

【讨论】:

  • 仍然超过时间限制0.2s
  • @EdwinDrood:好吧,我无法打开您发布的链接,因为它不接受我的旧 Firefox。但通常,在计算字符串相似度时,您会使用某种动态编程算法,例如维基百科上列出的Levenshtein distance
【解决方案3】:

对于如此简单的数字脚本,您只需要做两件事:

  • 使用 PyPy(它没有复杂的依赖关系,速度会大大提高)

  • 将大部分代码放在一个函数中。这极大地加快了 CPython 和 PyPy 的速度。而不是:

    一些代码

做:

def main():
    some_code

if __name__ == '__main__':
    main()

差不多了。

干杯, 菲哈尔

【讨论】:

  • 是的,使用 PyPy 大大减少了时间,但正如我所说,我正在尝试将代码提交到比赛中,并且只使用了 CPython。将代码放入函数中并没有多大帮助。
  • 抱怨他们不支持 PyPy,这听起来很糟糕:)
【解决方案4】:

这是我的。它通过了测试用例,但可能不是绝对最快的。

import sys

def simstring(string, other):
    val = 0
    for l, r in zip(string, other):
        if l != r:
            return val
        val += 1
    return val


dsize = sys.stdin.readline()

for i in range(int(dsize)):
    ss = 0
    string = sys.stdin.readline().strip()
    suffix = string
    while suffix:
        ss += simstring(string, suffix)
        suffix = suffix[1:]
    sys.stdout.write(str(ss)+"\n")

【讨论】:

  • 你的似乎比我的慢!我用10000 chars 测试了它,而你的用了大约 8 秒。我的大约是 4.8 秒。
  • 啊,就像我说的可以改进。但是很难比较不同机器的时间。也许我会在我的机器上看看你的比较。
  • 一般来说你是对的,但我在我的机器上测试了这两个代码。
猜你喜欢
  • 2011-12-08
  • 1970-01-01
  • 2018-07-13
  • 1970-01-01
  • 1970-01-01
  • 2021-08-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多