【问题标题】:Is this an acceptable algorithm?这是一个可接受的算法吗?
【发布时间】:2012-12-31 23:17:08
【问题描述】:

我设计了一个算法来找到最长的公共子序列。这些是步骤:

  • 选择第一个字符串中的第一个字母。

  • 在第二个字符串中查找它,如果找到,将该字母添加到 common_subsequence 并将其位置存储在index 中,否则 比较common_subsequence 的长度和lcs 的长度 如果更大,则将其值分配给lcs。

  • 返回第一个字符串并选择下一个字母并重复 再上一步,不过这次从indexth letter开始搜索

  • 重复此过程,直到第一个字符串中没有字母 挑选。最后lcs 的值是最长公共 子序列。

这是一个例子:

X=A, B, C, B, D, A, B‬‬  
‫‪Y=B, D, C, A, B, A‬‬ 

在第一个字符串中选择A。
在Y 中寻找A。
现在第二个字符串中有一个A,将其附加到common_subsequence。
返回第一个字符串并选择下一个字母B。 这次从A的位置开始,在第二个字符串中寻找B。
在A 之后有一个B,所以将B 附加到common_subsequence。
现在选择第一个字符串中的下一个字母C。第二个字符串中的B 旁边没有C。所以将common_subsequence的值赋值给lcs,因为它的长度大于lcs的长度。 重复前面的步骤,直到到达第一个字符串的末尾。最后lcs的值就是最长公共子序列。

这个算法的复杂度是theta(n*m)。 这是我的实现:

第一个算法:

import time
def lcs(xstr, ystr):
    if not (xstr and ystr): return # if string is empty
    lcs = [''] #  longest common subsequence
    lcslen = 0 # length of longest common subsequence so far
    for i in xrange(len(xstr)):
        cs = '' # common subsequence
        start = 0 # start position in ystr
        for item in xstr[i:]:
            index = ystr.find(item, start) # position at the common letter
            if index != -1: # if common letter has found
                cs += item # add common letter to the cs
                start = index + 1
            if index == len(ystr) - 1: break # if reached end of the ystr
        # update lcs and lcslen if found better cs
        if len(cs) > lcslen: lcs, lcslen = [cs], len(cs) 
        elif len(cs) == lcslen: lcs.append(cs)
    return lcs

file1 = open('/home/saji/file1')
file2 = open('/home/saji/file2')
xstr = file1.read()
ystr = file2.read()

start = time.time()
lcss = lcs(xstr, ystr)
elapsed = (time.time() - start)
print elapsed

使用哈希表的相同算法:

import time
from collections import defaultdict
def lcs(xstr, ystr):
    if not (xstr and ystr): return # if strings are empty
    lcs = [''] #  longest common subsequence
    lcslen = 0 # length of longest common subsequence so far
    location = defaultdict(list) # keeps track of items in the ystr
    i = 0
    for k in ystr:
        location[k].append(i)
        i += 1
    for i in xrange(len(xstr)):
        cs = '' # common subsequence
        index = -1
        reached_index = defaultdict(int)
        for item in xstr[i:]:
            for new_index in location[item][reached_index[item]:]:
                reached_index[item] += 1
                if index < new_index:
                    cs += item # add item to the cs
                    index = new_index
                    break
            if index == len(ystr) - 1: break # if reached end of the ystr
        # update lcs and lcslen if found better cs
        if len(cs) > lcslen: lcs, lcslen = [cs], len(cs) 
        elif len(cs) == lcslen: lcs.append(cs)
    return lcs

file1 = open('/home/saji/file1')
file2 = open('/home/saji/file2')
xstr = file1.read()
ystr = file2.read()

start = time.time()
lcss = lcs(xstr, ystr)
elapsed = (time.time() - start)
print elapsed

【问题讨论】:

  • 在 Python 中按索引循环几乎总是一个糟糕的想法——通常有更好的方法。此外,空字符串的计算结果为None,因此不要检查len(str) 是否为0,只需检查not str。
  • 我在这里推荐动态编程。查找wikipedia
  • 动态编程的下限比你的低,执行的比较也比你的少(即使它有相同的大 O 比较)。因此,实际运行时会更快,即使它属于同一个复杂度等级
  • @Rastegar:你的教授想让你发明一种新算法?或者只是为了实现一些好的算法?如果您对研究问题感兴趣,而不仅仅是您的程序需要什么,请参阅 Bergroth、Hakonen 和 Raita 的 A survey of longest common subsequence algorithms,这将为您提供所需的背景知识。
  • @Rastegar:嗯,你总是可以去图书馆拿死树形式的日记,然后从那里复制。 :)

标签: python lcs


【解决方案1】:

如果您的教授希望您发明自己的 LCS 算法,那么您就完成了。你的算法不是有史以来最优化的算法,但它属于正确的复杂度等级,你清楚地理解它,而且你显然没有从互联网上复制你的实现。您可能希望准备好为您的算法辩护,或讨论替代方案。如果我是你的教授,如果:

  • 你上交了那个程序。
  • 您能够解释为什么没有可能的 O(N) 或 O(N log M) 替代方案。
  • 您能够参与关于可能具有更好下界(或显着更低的常数等)以及时间/空间权衡等的其他算法的合理讨论,甚至如果您事先不知道讨论的结果。

另一方面,如果您的教授希望您选择一种知名算法并编写自己的实现,您可能希望使用标准 LP 算法。它是一个标准算法是有原因的——你可能想继续阅读,直到你理解为止。 (即使不参加考试,你也是来学习这门课的,而不仅仅是为了给教授留下好印象,对吧?)

Wikipedia 有基本实现的伪代码,然后是常见优化的英文描述。我很确定根据该页面上的内容编写自己的 Python 代码不会被视为抄袭,甚至不会被视为微不足道的移植,尤其是如果您可以证明您了解您的代码在做什么、为什么以及为什么这是一个很好的算法。另外,你是用 Python 编写的,它的记忆方式比那篇文章中演示的要好得多,所以如果你理解它的工作原理,你的代码实际上应该比维基百科给你的要好得多。

不管怎样,正如我在 cmets 中建议的那样,我会阅读 Bergroth、Hakonen 和 Raita 的 A survey of longest common subsequence algorithms,并在网上搜索类似的论文。

【讨论】:

    【解决方案2】:
    maxLength = 0
    foundString = ""
    for start in xrange(len(str1)-1):
         for end in xrange(start+1, len(str1)):
            str1Temp = str1[start:end]   
            maxLengthTemp = len(str1Temp)
            if(str2.find(str1Temp)):
                 if(maxLengthTemp>maxLength):
                     maxLength = maxLengthTemp
                     foundString = str1Temp
    
    print maxLength
    print foundString
    

    【讨论】:

    • OP 询问他的算法是否良好。你通过发布一个完全不同的算法来回应,没有讨论为什么它更好,它有什么不同等等。那有什么意义?特别是因为,乍一看,您的算法看起来充其量是O(N^2*M),而不是他的O(N^2) 或DP 的O(N*M)……
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-19
    • 2017-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多