最長共通サブシーケンス-Python-ダイナミックプランニング


1.テーマ内容
題名:最長共通サブシーケンス.テーマ:1.動的計画アルゴリズム思想を用いて最長共通サブシーケンス問題解アルゴリズムを設計し,与えられたデータ(統一と自己選択の2種類に分けて)を検証した.2.分析アルゴリズムの時間的複雑性が要求される.3.窮挙アルゴリズム、直接再帰法、覚書法と比較し、分析報告を形成する.
2.アルゴリズム分析
1.アルゴリズムの原理:
文字列X、長さm、1から数;文字列Y、長さn、1から数;Xi=すなわちXシーケンスの前i文字(1≦i≦m)Yj=すなわちYシーケンスの前j文字(1≦j≦n)LCS(X,Y)は、文字列XおよびYの最長共通サブシーケンス、すなわちZ=である
xm=yn(最後の文字が同じ)の場合、XmとYnの最長共通サブシーケンスZkの最後の文字は必ずxm zk=xm=yn LCS(Xm,Yn)=LCS(Xm-1,Yn-1)+xmである
xm≠ynの場合、LCS(Xm,Yn)=max{LCS(Xm-1,Yn),LCS(Xm,Yn-1)}
2.アルゴリズム実装手順:1)まずLCSの長さを探す:
a=b=(2つのシーケンス)
LCSの長さc[0][j]を格納する2次元配列c[i][j]を作成し、c[i][0]は任意の文字列と空の文字列のlcsが0であるため、0に初期化します.a[0]から順にb[i]と比較し、
(1)a[j]とb[j]が等しい場合、a[i][j]==a[i-1]j-1
(2)a[j]とb[i]が等しくなければ、a[i][j]はa[i-1][j]、a[i][j-1]と比較し、その大きさはその値を記入する.
このようにして、最後に記入した表の右下隅の値は、LCSの長さ[0,0,0,0,0,0,0,0,0,0[0,1,1,1,1,1[0,0,1,1,2,2,2,2[0,2][0,0,1,1,2,2,2,2,2,2,2,2,2,2,2,3,[0,1,1,2,2,3,3,3,3,4][0,1,2,2,3,4][0,1,2,3,4]である
2)LCS自体を再検索:検索の便宜上,a[i]b[j]を比較する際に,各位置の動作を別の配列で記録する.等しい場合は「ok」、等しくない場合は「up」、左の大きい場合は「left」となります.
最後にc[i][j]から見て、flag[i,j]「ok」であればlcsの最後の1つがa[i-]、このときa[i-]b[j-]、flag[i,j][up]、j-1がflag[i,j]の値を見直し、flag[i,j]「left」であればi-1は、flagを見ている.
3.アルゴリズムの時間的複雑性
aシーケンスbシーケンスLCS時間18 25 6 0.0004454 15 20 5 0.00013.38 50 11 0.0012632 100 30 0.0046937 500 161 0.139399999 1000 1000 320 0.650319700
3.アルゴリズムのデバッグ1.計算時間の複雑さでは、pythonのデフォルトの再帰深さは限られている(デフォルトは1000)ため、再帰深さが999を超えると、このような異常が発生します.このとき,コードヘッダに再帰深さを変更するコードを加えると解決する.import sys sys.setrecursionlimit(100000)
4.アルゴリズム比較
1.穷挙法:a[],b[]の2つの文字列のすべての文字列を求め、これらの文字列を互いに比较して最も长い共通のサブシーケンスを见つけるまで、文字列の长さが大きい场合、かかる时间は非常に大きい.2.直接再帰法:a[]b[]の2つの文字列に対応する位置の文字列が同じである場合、2つの場合の最大値を同時に取らない場合、直接次の位置を求める.時間の複雑さは線形に増加する.3.メモアルゴリズムは、最適解を上から下へ計算する考え方であり、メモ方法の制御構造は直接再帰方法の制御構造と同じであるが、メモ方法は解決したサブ問題の答えを1つの表で保存し、同じ問題の繰り返し解を回避する.
5.コード
import time
import random
import string
import sys
sys.setrecursionlimit(100000)

#     lcs
q= []
def lcs(a, b):
    lena = len(a)
    lenb = len(b)
    c = [[0 for i in range(lenb + 1)] for j in range(lena + 1)]
    flag = [[0 for i in range(lenb + 1)] for j in range(lena + 1)]
    for i in range(lena):
        for j in range(lenb):
            if a[i] == b[j]:
                c[i + 1][j + 1] = c[i][j] + 1
                flag[i + 1][j + 1] = 'ok'
            elif c[i + 1][j] > c[i][j + 1]:
                c[i + 1][j + 1] = c[i + 1][j]
                flag[i + 1][j + 1] = 'left'
            else:
                c[i + 1][j + 1] = c[i][j + 1]
                flag[i + 1][j + 1] = 'up'
    return c, flag
#  lcs
def printLcs(flag, a, i, j):
    if i == 0 or j == 0:
        return
    if flag[i][j] == 'ok':
        printLcs(flag, a, i - 1, j - 1)
        q.append(a[i - 1])
        print(a[i - 1], end='')
        print(len(q))
    elif flag[i][j] == 'left':
        printLcs(flag, a, i, j - 1)
    else:
        printLcs(flag, a, i - 1, j)

if __name__ == '__main__':
    # a = 'BDCABA'
    # b = 'ABCBDAB'
    s = string.ascii_uppercase  #       (A-Z)
    a=[random.choice(s) for i in range(1000)]
    b=[random.choice(s) for i in range(1000)]
    print(a)
    print(b)
    t1 = time.perf_counter()
    c, flag = lcs(a, b)
    printLcs(flag, a, len(a), len(b))
    t2 =time.perf_counter()
    print()
    print(t2-t1)

6.悟り
LCSの長さを計算するときは、自分で紙の行ごとに1列ずつ計算したほうがいいです.そうしないと、ぼんやりしてしまいます.今回の実験によって、以前爬虫類に連絡したときに遭遇した問題も解決され、爬虫類のページが多くなるたびに報告が間違ってしまい、再帰制限の問題だとは知らなかった.