【问题标题】:How to speed up Python string matching code如何加速 Python 字符串匹配代码
【发布时间】:2013-06-29 15:26:13
【问题描述】:

我有这段代码,它计算随机字符串之间的Longest Common Subsequence,以查看重建输入的未知区域的准确度。为了获得好的统计数据,我需要对其进行多次迭代,但我当前的 python 实现太慢了。即使使用pypy,目前运行一次也需要 21 秒,理想情况下我希望运行 100 次。

#!/usr/bin/python

import random
import itertools
#test to see how many different unknowns are compatible with a set of LCS answers.
def lcs(x, y):
    n = len(x)
    m = len(y)
#    table is the dynamic programming table
    table = [list(itertools.repeat(0, n+1)) for _ in xrange(m+1)]
    for i in range(n+1):     # i=0,1,...,n
        for j in range(m+1):  # j=0,1,...,m
            if i == 0 or j == 0:
                table[i][j] = 0
            elif x[i-1] == y[j-1]:
                table[i][j] = table[i-1][j-1] + 1
            else:
                table[i][j] = max(table[i-1][j], table[i][j-1])

    # Now, table[n, m] is the length of LCS of x and y.
    return table[n][m]

def lcses(pattern, text):
    return [lcs(pattern, text[i:i+2*l]) for i in xrange(0,l)]

l = 15
#Create the pattern
pattern = [random.choice('01') for i in xrange(2*l)]

#create text start and end and unknown. 
start = [random.choice('01') for i in xrange(l)]
end = [random.choice('01') for i in xrange(l)]
unknown = [random.choice('01') for i in xrange(l)]

lcslist= lcses(pattern, start+unknown+end)

count = 0
for test in itertools.product('01',repeat = l):
    test=list(test)
    testlist = lcses(pattern, start+test+end)
    if (testlist == lcslist):
        count += 1

print count

我尝试将其转换为 numpy,但我一定做得很糟糕,因为它实际上运行得更慢。这段代码能以某种方式加速吗?

更新。在下面的评论之后,如果lcses 直接使用循环会更好,它会在patterntext 的所有子列表之间提供相同长度的 LCS。是否有可能以某种方式修改经典的动态规划 LCS 算法来做到这一点?

【问题讨论】:

  • 使用 C 版本并用 ctypes 或 cython 包装它。 rosettacode.org/wiki/Longest_common_subsequence#C
  • 这是一个可怕的算法错误解决方案。
  • @doukremt 这听起来是个好主意,尽管我从来没有做过这样的事情。你介意告诉我这在我的情况下是如何工作的吗?
  • 关于您上面的更新:我们不会在 StackOverflow 上为人们编写代码。尝试实施它,然后如果你不能正确地提出一个新问题。
  • @msw 我不确定改进的重复频率会是什么。

标签: python performance algorithm dynamic-programming lcs


【解决方案1】:

重复表 table 在每次调用 lcses() 时都会重新计算 15 次,此时它仅依赖于 mn,其中 m 的最大值为 2*ln最多为3*l

如果您的程序只计算一次表,那将是动态编程,而目前不是。一个 Python 习惯用法是

table = None
def use_lcs_table(m, n, l):
    global table
    if table is None:
        table = lcs(2*l, 3*l)
    return table[m][n]

除了使用类实例之外,它会比全局表声明更简洁、更具可扩展性。但这会让您了解为什么要花这么多时间。

在回复评论时添加:

动态编程是一种优化,需要以更少的时间换取额外的空间。在您的示例中,您似乎正在lcs() 中进行表格预计算,但是您在每次调用时都构建了整个列表,然后将其丢弃。我并没有声称了解您尝试实现的算法,但是您对其进行编码的方式也可以:

  1. 没有递归关系,因此没有理由进行 DP 优化,或者
  2. 有一个重复关系,你搞砸了它的实现。

【讨论】:

  • 'table' 取决于列表 'pattern' 和它被调用的 'text' 的特定子列表。我不确定你的意思。
  • 你是对的,直接找到模式和相同长度文本的每个子列表之间的 LCS 的循环会更好。问题已编辑。
猜你喜欢
  • 2020-05-17
  • 1970-01-01
  • 2014-09-21
  • 2011-03-22
  • 2013-04-29
  • 2012-07-24
  • 1970-01-01
  • 2016-06-09
  • 1970-01-01
相关资源
最近更新 更多