【问题标题】:More efficient algorithm for shortest superstring search更高效的最短超字符串搜索算法
【发布时间】:2013-12-03 00:21:28
【问题描述】:

我下面的问题是 NP 完全的,但是,我试图找到至少一个稍微快一点的字符串搜索函数或模块,与现在相比,它可能有助于减少一些计算时间。任何建议将不胜感激。

连接的(可能最长的)超字符串是:

AGGAGTCCGCGTGAGGGAGGTGTAGTGTAGTGG

以下代码生成 16m 内最短的超字符串:

CCGTAGGTGGAGT

import itertools as it

def main():
    seqs = ['AGG', 'AGT', 'CCG', 'CGT', 'GAG', 'GGA', 'GGT', 'GTA', 'GTG', 'TAG', 'TGG']
    seq_perms = [''.join(perm) for perm in it.permutations(seqs)]
    for i in range(0, len(''.join(seqs))):
        seq_perms = [''.join(perm)[:i] for perm in it.permutations(seqs)]   
        for perm in seq_perms:   
            if all(perm.find(seq) != -1 for seq in seqs) == True:
                print 'Shortest superstring containing all strings:\n{}'.format(perm)
                return


if __name__ == '__main__':
    main()

在我的系统上在更短的时间内完成的任何重构都将被标记为已解决。

【问题讨论】:

  • return 看起来很不合适,您能否提供示例输入(如果有)和预期输出。
  • 一旦生成第一个超字符串(在这种情况下是最短的),返回提供函数退出。生成所有可能的超字符串,然后对每个可能的超字符串进行切片,搜索每个切片以查找它们是否包含每个字符串 - 每次迭代都会变大。输入是'seqs'列表对象,输出如上,'CCGTAGGTGGAGT'。
  • 请检查my answer,我更新了代码,现在只需要5分钟(在我的机器上)。
  • 一个新的更新,感谢this comment - 现在只需要~2分钟!
  • @Alfe 在您上次回复后一个多小时就回复了这个问题,但您没有回复?哇,好吧。最好、最快和最聪明的人应该得到一些荣誉,尤其是分别快 19 倍和 29 倍。

标签: python algorithm optimization


【解决方案1】:

应该这样做。

import itertools as it

SEQUENCES = ['AGG', 'AGT', 'CCG', 'CGT', 'GAG', 'GGA', 'GGT', 'GTA', 'GTG', 'TAG', 'TGG']
LONGEST_SUPERSTRING = ''.join(SEQUENCES)

def find_shortest_superstring():
    current_shortest = LONGEST_SUPERSTRING
    trim = len(current_shortest)-1
    seen_prefixes = set()
    for perm in it.permutations(SEQUENCES):
        candidate_string = ''.join(perm)[:trim]
        if candidate_string in seen_prefixes:
            continue
        seen_prefixes.add(candidate_string)
        while is_superstring(candidate_string):
            current_shortest = candidate_string
            candidate_string = candidate_string[:-1]
            trim = len(current_shortest)-1
    return current_shortest

def is_superstring(s):
    return all(seq in s for seq in SEQUENCES)

def main():
    print 'Searching for shortest superstring containing all strings.'
    ss = find_shortest_superstring()
    print 'Found shortest superstring containing all strings:\n{}'.format(ss)

if __name__ == '__main__':
    main()

代码运行大约需要 15 秒,并产生以下输出:

Searching for shortest superstring containing all strings.
Found shortest superstring containing all strings:
CCGTAGGTGGAGT

【讨论】:

  • 你不能在循环之前将candidate_string 截断为len(current_shortest)-1,然后简单地截断while is_superstring(...吗?
  • @JanneKarila 好点,我将candidate_string = ''.join(perm) 切换为candidate_string = ''.join(perm)[:len(current_shortest)] 现在只需约2 分钟! :)
  • 我还意味着您可以通过更改为 while is_superstring(... 来消除 if/elif/else。您需要再截断一个字符才能正常工作。
  • 是的,如果您将candidate_string = ''.join(perm)[:len(current_shortest)] 更改为...[:len(current_shortest)-1],则无需检查循环内的长度,因此您可以只使用while is_superstring(candidate_string): current_shortest = candidate_string; candidate_string = candidate_string[:-1]
  • 另外,你不需要为每个排列计算len(current_shortest),但只有当你实际检测到一个新的current_shortest值时,所以我建议引入一个新的current_shortest_len变量来跟踪的那个。
【解决方案2】:

我应用了 Dijkstra 算法(宽度搜索),并在不到一秒的时间内找到了解决方案。我在内存使用方面对其进行了一些优化,但我认为就算法而言,这是一种比另一个答案中更好的方法。除非我们内存不足,否则这应该是一个更好的解决方案。

from collections import defaultdict

def dijkSuperstring(originalSeqs):
  paths = defaultdict(set)
  paths[0] =  { '' }
  while paths:
    minLength = min(paths.keys())
    while paths[minLength]:
      candidate = paths[minLength].pop()
      seqAdded = False
      for seq in originalSeqs:
        if seq in candidate:
          continue
        seqAdded = True
        for i in reversed(range(len(seq)+1)):
          if candidate.endswith(seq[:i]):
            newCandidate = candidate + seq[i:]
            paths[len(newCandidate)].add(newCandidate)
      if not seqAdded:  # nothing added, so all present?
        return candidate
    del paths[minLength]

print dijkSuperstring(
  [ 'AGG', 'AGT', 'CCG', 'CGT', 'GAG', 'GGA', 'GGT', 'GTA', 'GTG', 'TAG', 'TGG' ])

我也尝试使用随机序列作为输入:

seqs = [ ''.join(random.choice('GATC')
  for i in range(3))
    for j in range(11) ]
print dijkSuperstring(deqs)

我很快发现求解时间很大程度上取决于结果的大小(!)而不是输入的大小(所以它是不可预测的)。这并不奇怪,但它使比较不同的算法有点困难,因为其他算法不一定也有这个属性。特别是,来自 OP 的序列集似乎构成了一个相对轻量级的问题。其他 11 个 3 个字符序列的集合更难解决。

所以我做了一些统计测量;我解决了 1000 组 8 个序列。这是我为 3 个和 4 个字符的序列所做的。然后我将持续时间分为 100 组(从 0 到最高持续时间间隔相等),并计算每组有多少。为了平滑图表,我总是使用三个相邻组的总和。

下面的图表分别显示了两个这样的实验,使用我的算法的早期(非优化)版本(但曲线的形状与现在相同);我做了两次,至少知道图中的一个奇怪的沟是有原因的还是纯属偶然的。

我很想看到其他算法的相同类型输入的类似图表。这可能很有趣,因为我的算法显然存在内存问题。由于内存耗尽,解决 11 个 3 个字符的序列使我的机器多次停止,因此即使速度较慢,使用另一种算法也是有意义的。

3 个字符的 8 个序列

4 个字符的 8 个序列

【讨论】:

  • 为什么不给这个解决方案更多的支持?听起来它要快得多,也能得到正确的答案。有人尝试同时运行两者并比较时间吗?
  • 我想我只是迟到了一点,现在没有人感兴趣了 ;-) 我可以忍受。
  • 我尝试了 Alfe 的版本和 Inbar 的版本。他们都找到了相同的解决方案,分别在 1s 和 29s 中。阿尔夫,我向你致敬。
  • 非常聪明和快速!
  • 这从未产生预期的结果,因此不会获得正确答案。最快的解决方案,而不是最快的输出。
【解决方案3】:

只是回溯,但总是首先检查最重叠的部分。在得到一个好的候选答案之后,当字符串中的当前路径结果的长度大于或等于这个候选答案时,我们不需要再沿着这条路径走得更远。

在我的 Jupyter 笔记本中测试。它似乎比这里的其他两个答案要快得多(11/18/2018)

def shortestSuperstring(A):
    """
    :type A: List[str]
    :rtype: str
    """

    if len(A)==1:
        return A[0]
    dic={}
    for i in xrange(len(A)):
        for j in xrange(len(A)):
            if i!=j:
                ol=0
                for k in xrange(1,min(len(A[i]),len(A[j]))):
                    if A[j][:k]==A[i][-k:]:
                        ol=k
                dic[(i,j)]=ol
    if max(dic.values())==0:
        return "".join(A)
    else:
        ret="".join(A)
        l=len(ret)
        stack=[]
        for i,wd in enumerate(A):
            tmp=set(range(len(A)))
            tmp.remove(i)
            stack.append((wd,i,tmp))
        while stack:
            ans,cur,remain=stack.pop()
            if len(ans)<l:
                if not remain:
                    ret=ans
                    l=len(ret)
                else:
                    tmp=[[dic[cur,idx],idx] for idx in remain] # [#overlap,idx]
                    tmp.sort()
                    for ol,idx in tmp:
                        nans=ans+A[idx][ol:]
                        nremain=set(remain)
                        nremain.remove(idx)
                        stack.append((nans,idx,nremain))
        return ret

问题中的测试用例取L

1.93 s ± 160 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

运行并给出答案:

'CCGTGGTAGGAGT'

其他一些测试用例(较长的字符串并且开始击败其他两种方法,大约 1~5 秒):

    ****************************************************************************************************


    case: 

     ['mftpvodataplkewcouz', 'krrgsoxpsnmzlhprsl', 'qhbfymytxzbmqma', 'hunjgeaolcuznhpodi', 'kewcouzbwlftz', 'xzbmqmahunjgeaolcu', 'zlhprslqurnqbhsjr', 'rrgsoxpsnmzlhprslqur', 'diqukrrgsoxpsnmz', 'sjrxzavamftpvoda']


    ****************************************************************************************************


    ans:  qhbfymytxzbmqmahunjgeaolcuznhpodiqukrrgsoxpsnmzlhprslqurnqbhsjrxzavamftpvodataplkewcouzbwlftz


    ****************************************************************************************************


    case: 

     ['cedefifgstkyxfcuajfa', 'ooncedefifgstkyxfcua', 'assqjfwarvjcjedqtoz', 'fcuajfassqjfwarvjc', 'fwarvjcjedqtozctcd', 'zppedxfumcfsngp', 'kyxfcuajfassqjfwa', 'fumcfsngphjyfhhwkqa', 'fassqjfwarvjcjedq', 'ppedxfumcfsngphjyf', 'dqtozctcdk']


    ****************************************************************************************************


    ans:  zppedxfumcfsngphjyfhhwkqaooncedefifgstkyxfcuajfassqjfwarvjcjedqtozctcdk


    ****************************************************************************************************


    case: 

     ['ekpijtseahvmprvefkgn', 'yyevvcmeekpijtseahvm', 'vsfcyyevvcmeekp', 'xwmkoqhxvrovlmmvsfcy', 'cmeekpijtseahvmpr', 'oqhxvrovlmmvsfcyy', 'zpuemtclxbxwsypfxevx', 'clxbxwsypfxevxw', 'fkgnjgdvfygnlckyiju', 'xevxwmkoqhxvrovlmm']


    ****************************************************************************************************


    ans:  zpuemtclxbxwsypfxevxwmkoqhxvrovlmmvsfcyyevvcmeekpijtseahvmprvefkgnjgdvfygnlckyiju


    ****************************************************************************************************


    case: 

     ['ppgortnmsy', 'czmysoeeyugbiylso', 'nbfzpppvhbjydtx', 'rnzynedhoiunkpon', 'ornzynedhoiunkpo', 'ylsomoktkyfgljcf', 'jtvkrornzynedhoiunk', 'hvhhihwdffmxnczmyso', 'ktkyfgljcfbkqcpp', 'nzynedhoiunkponbfz', 'nedhoiunkponbfzpppvh']


    ****************************************************************************************************


    ans:  hvhhihwdffmxnczmysoeeyugbiylsomoktkyfgljcfbkqcppgortnmsyjtvkrornzynedhoiunkponbfzpppvhbjydtx


    ****************************************************************************************************


    case: 

     ['amefulhsdgvjvoab', 'giqxpqszaitzfzvtalx', 'cyqeolfgkihssycmiodg', 'glhhcfuprwazet', 'cmiodgiqxpqszaitzf', 'lhsdgvjvoabdviglhhcf', 'ssycmiodgiqxpqsza', 'bxtdqnamefulhsdg', 'namefulhsdgvjvo', 'ihssycmiodgiqxp', 'itzfzvtalxfybxtdqn']


    ****************************************************************************************************


    ans:  cyqeolfgkihssycmiodgiqxpqszaitzfzvtalxfybxtdqnamefulhsdgvjvoabdviglhhcfuprwazet


    ****************************************************************************************************


    case: 

     ['yobbobwqymlordokxka', 'jllfoebgbsrguls', 'rgulsnatnpuuwiyba', 'ordokxkamymamofefr', 'wqymlordokxkamy', 'fycxifzsjllfoebgbsrg', 'lordokxkamymamofe', 'kxkamymamofefrmfycx', 'frmfycxifzsjllf', 'srgulsnatnpuuwiy']


    ****************************************************************************************************


    ans:  yobbobwqymlordokxkamymamofefrmfycxifzsjllfoebgbsrgulsnatnpuuwiyba


    ****************************************************************************************************


    case: 

     ['jnbbbbsczcscxawcze', 'bsczcscxawczeumyyr', 'lyofvbhvjmquhkgz', 'quhkgzyzdwtjnbbb', 'kgzyzdwtjnbbbbsczc', 'uouxnfplptpkgnronf', 'pqgyfqglyofvbhv', 'kgnronftgswvpqgy', 'marvhdxtbmkcpnli', 'qgyfqglyofvbhvjmquhk', 'xtbmkcpnliz']


    ****************************************************************************************************


    ans:  marvhdxtbmkcpnlizuouxnfplptpkgnronftgswvpqgyfqglyofvbhvjmquhkgzyzdwtjnbbbbsczcscxawczeumyyr


    ****************************************************************************************************


    case: 

     ['qrwpawefqzfjsan', 'jsanzdukfkdlmyox', 'neaxnkedjxbpgsyq', 'nqjvzryhfjdsxmwolwo', 'hfjdsxmwolwomeeewvi', 'lmyoxbpvkneaxnkedjxb', 'qbhpqrwpawefqzfjsa', 'pawefqzfjsanzdukfk', 'bqbhpqrwpawefqzfj', 'dlmyoxbpvkneaxnk', 'xnkedjxbpgsyqovvh']


    ****************************************************************************************************


    ans:  bqbhpqrwpawefqzfjsanzdukfkdlmyoxbpvkneaxnkedjxbpgsyqovvhnqjvzryhfjdsxmwolwomeeewvi


    ****************************************************************************************************


    case: 

     ['vgrikrnwezryimj', 'umwgwvzpsfpmctzt', 'pjourlpgeemdjor', 'urlpgeemdjorpzbkbz', 'jorpzbkbzcqyewih', 'xuwkzvoczozhhvf', 'ihbumoogibirbsvch', 'nwezryimjivvpjourlp', 'kzvoczozhhvfwgeplv', 'ezryimjivvpjourlpgee', 'zhhvfwgeplvqngglu', 'rikrnwezryimjivvp']


    ****************************************************************************************************


    ans:  xuwkzvoczozhhvfwgeplvqngglumwgwvzpsfpmctztvgrikrnwezryimjivvpjourlpgeemdjorpzbkbzcqyewihbumoogibirbsvch


    ****************************************************************************************************


    case: 

     ['nbsgonqmpreelpbr', 'hnysjajtiguehrokus', 'udgzbzmevnkzzba', 'axtbmcpbmoubyoscn', 'vqnbsgonqmpreel', 'xvqnbsgonqmpree', 'ajtiguehrokustktudgz', 'brgkgihuetpqrhhbhn', 'dgzbzmevnkzzbaxtbmcp', 'ehrokustktudgzbzmevn', 'uetpqrhhbhnysjaj', 'vnkzzbaxtbmcpbmo']


    ****************************************************************************************************


    ans:  xvqnbsgonqmpreelpbrgkgihuetpqrhhbhnysjajtiguehrokustktudgzbzmevnkzzbaxtbmcpbmoubyoscn


    ****************************************************************************************************


    case: 

     ['orugbsuuxowmhjh', 'zjyxzmpduthlsioor', 'qtxocgehmhfqnstl', 'tlrlcnnrsyryfrywuebq', 'hozjyxzmpduthlsio', 'hjhdmnqtxocgehm', 'mjhzwdudlnbfkjawqacf', 'hfqnstlrlcnnrsyryfry', 'yfrywuebqhvwewzmq', 'zzieemjhzwdudlnbfkj', 'nnrsyryfrywuebqhvw', 'acfgaihbhozjyxzmpdut']


    ****************************************************************************************************


    ans:  zzieemjhzwdudlnbfkjawqacfgaihbhozjyxzmpduthlsioorugbsuuxowmhjhdmnqtxocgehmhfqnstlrlcnnrsyryfrywuebqhvwewzmq


    ****************************************************************************************************


    case: 

     ['phuutlgczfspygaljkv', 'fspygaljkvahvuii', 'csywjodtnkynkjckq', 'poyykqyrhbvcwvjl', 'xijupvzzwphuutlg', 'aljkvahvuiivqbqrw', 'vahvuiivqbqrwryd', 'wjodtnkynkjckqurgu', 'ecdmbshotqbxjqgbou', 'hvuiivqbqrwrydgnr', 'ivqbqrwrydgnrubcsywj', 'wphuutlgczfspyga']


    ****************************************************************************************************


    ans:  ecdmbshotqbxjqgbouxijupvzzwphuutlgczfspygaljkvahvuiivqbqrwrydgnrubcsywjodtnkynkjckqurgupoyykqyrhbvcwvjl

另见动态规划方法: https://leetcode.com/problems/find-the-shortest-superstring/solution/

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-12-09
    • 2011-12-21
    • 1970-01-01
    • 2018-05-22
    • 1970-01-01
    • 1970-01-01
    • 2012-01-24
    • 1970-01-01
    相关资源
    最近更新 更多