簡體   English   中英

算法的運行時間復雜度 - 我如何計算並進一步優化算法?

[英]Running Time Complexity of my Algorithm - how do i compute this and further optimize the algorithm?

我設計了一個遞歸算法並用Python寫下來。 當我用不同的參數測量運行時間時,它似乎需要指數時間。 此外; 以50這樣的小數字結束需要半個多小時。(我沒等到它完成,但它似乎沒有在合理的時間內完成,猜測它是指數級的)。

所以,我很好奇這個算法的運行時復雜性。 有人可以幫我推導出方程T(n,m)嗎? 或者計算大哦?

算法如下:

# parameters:
# search string, the index where we left on the search string, source string, index where we left on the source string,
# and the indexes array, which keeps track of the indexes found for the characters
def find(search, searchIndex, source, sourceIndex, indexes):
    found = None
    if searchIndex < len(search): # if we haven't reached the end of the search string yet
        found = False
        while sourceIndex < len(source): # loop thru the source, from where we left off
            if search[searchIndex] == source[sourceIndex]: # if there is a character match
                # recursively look for the next character of search string 
                # to see if it can be found in the remaining part of the source string
                if find(search, searchIndex + 1, source, sourceIndex + 1, indexes):
                    # we have found it
                    found = True # set found = true
                    # if an index for the character in search string has never been found before.
                    # i.e if this is the first time we are finding a place for that current character
                    if indexes[searchIndex] is None:
                        indexes[searchIndex] = sourceIndex # set the index where a match is found
                    # otherwise, if an index has been set before but it's different from what
                    # we are trying to set right now. so that character can be at multiple places.
                    elif indexes[searchIndex] != sourceIndex: 
                        indexes[searchIndex] = -1 # then set it to -1.
            # increment sourceIndex at each iteration so as to look for the remaining part of the source string. 
            sourceIndex = sourceIndex + 1
    return found if found is not None else True

def theCards(N, colors):
    # allcards: a list 1..N of characters where allcards[i] is 'R' if i is a prime number, 'B' otherwise.
    # so in this example where N=7, allcards=['B','R','R','B','R','B','R']
    allcards = ['R' if isPrime(i) else 'B' for i in range(1, N + 1)]
    # indexes is initially None.
    indexes = [None] * len(colors)

    find(colors, 0, allcards, 0, indexes)
    return indexes    

if __name__ == "__main__":
    print theCards(7, list("BBB"))

我不知道是否必須理解問題和算法才能得出最壞情況下的運行時間,但這是我試圖解決的問題:

問題:

給定源字符串SRC和搜索字符串SEA,在SRC中找到子序列SEA並返回在SRC中找到SEA的每個字符的索引。 如果SEA中的字符可以位於SRC中的多個位置,則為該字符位置返回-1。

例如; 如果源字符串是BRRBRBR(N = 7)並且搜索字符串是BBB:那么'BBB'中的第一個'B'可以出現在搜索字符串中的索引0處。 第二個“B”可以位於搜索字符串的索引3處,而最后一個“B”可以位於第5個位置。 此外; 對於字符'BBB'的位置沒有其他選擇,因此算法返回[0,3,5]。

在另一種情況下,源字符串是BRRBRB(N = 6)並且搜索字符串是RBR:'RBR'的第一個'R'可以在位置1或2.這只留下'B'的位置3和位置4為最后'R'。 然后,第一個'R'可以在多個地方,它的位置是不明確的。 另外兩個字符B和R只有一個地方。 所以算法返回[-1,4,5]。

算法沒有完成並永遠占用的情況是源字符串是['B','R','R','B','R','B','R','B' ,'B','B','R','B','R','B','B','B','R','B','R','B',' B','B','R','B','B','B','B','B','R','B','R','B','B' ,'B','B','B','R','B','B','B','R','B','R','B','B',' B','R','B','B','B','B','B','R','B','B','B','B','B' ](N = 58),搜索字符串為RBRRBRBBRBRRBBRRBBBRRBBBRR。 它應該返回[-1,-1,-1,-1,-1,-1,-1,-1,17,18,19,23,-1,-1,-1,-1,-1,-1 ,-1,-1,-1,-1,-1,-1,-1,47,53],但遺憾的是它不=(

優化:

我想在'索引'列表完全充滿-1時停止搜索。 但這只會影響最佳情況(或平均情況),但不會影響最壞情況。 如何進一步優化該算法。 我知道存在這個問題的多項式解決方案。

比優化更重要的是,我真的很好奇運行時間的T(n,m)方程,其中n和m是源和搜索字符串的長度。

如果你能夠在這里閱讀, 非常感謝! =)

編輯 - IVIad的解決方案實施:

def find2(search, source):
    indexes = list()
    last = 0
    for ch in search:
        if last >= len(source):
            break
        while last < len(source) and source[last] != ch:
            last = last + 1
        indexes.append(last)
        last = last + 1
    return indexes

def theCards(N, colors):
    # allcards: a list 1..N of characters where allcards[i] is 'R' if i is a prime number, 'B' otherwise.
    allcards = ['R' if isPrime(i) else 'B' for i in range(1, N + 1)]

    indexes = find2(colors, allcards) # find the indexes of the first occurrences of the characters
    colors.reverse() # now reverse both strings
    allcards.reverse()
    # and find the indexes of the first occurrences of the characters, again, but in reversed order
    indexesreversed = find2(colors, allcards)
    indexesreversed.reverse() # reverse back the resulting list of indexes 
    indexesreversed = [len(allcards) - i - 1 for i in indexesreversed] # fix the indices

    # return -1 if the indices are different when strings are reversed
    return [indexes[i] + 1 if indexes[i] == indexesreversed[i] else - 1 for i in range(0, len(indexes))]

if __name__ == "__main__":
    print theCards(495, list("RBRRBRBBRBRRBBRRBBBRRBBBRR"))

您可以在O(n + m) ,其中mSEA的字符數, nSRC的字符數:

last = 1
for i = 1 to m do
    while SRC[last] != SEA[i]
        ++last

    print last
    ++last (skip this match)

基本上,對於SEA每個角色,找到它在SRC位置,但只能在找到前一個角色的位置后掃描。

例如; 如果源字符串是BRRBRBR(N = 7)並且搜索字符串是BBB

然后:在SRC找到B :在last = 1找到last = 1打印1 ,設置last = 2

SRC找到B :在last = 4找到last = 4 ,打印4 ,設置last = 5

SRC找到B :找到last = 6 ,打印6 ,設置last = 7 完成。


至於算法的復雜性,我無法提供非常正式的分析,但我會嘗試解釋我是如何進行的。

假設SRCSEA以及它們之間的所有字符都相等。 因此,我們可以消除while循環中的條件。 另請注意,while循環執行n次。

請注意,對於第一個字符,您將調用find(1, 1), ... find(m, n) 但是這些也會啟動while循環並進行自己的遞歸調用。 對於i = 1 to n find(i, j)每個find(i, j)將在其while循環中進行O(m)遞歸調用。 但是這些反過來會自己進行更多的遞歸調用,從而產生一種導致指數復雜性的“雪崩效應”。

所以你有了:

i = 1: calls find(2, 2), find(3, 3), ..., find(m, n)
       find(2, 2) calls find(3, 3), ..., find(m, n)
       find(3, 3) calls find(4, 4), ..., find(m, n)
       find(4, 4) calls find(5, 5), ..., find(m, n)
       ...
       total calls: O(m^m)
i = 2: same, but start from find(2, 3).
...
i = n: same

總復雜度因此看起來像O(n*m^m) 我希望這是有道理的,我沒有犯任何錯誤。

這只是最常見的子序列問題。 這可以通過動態編程實現,以獲得比指數時間少很多的時間。 在您的情況下,當LCS返回SEA的長度時,您知道SRC中存在序列SEA,在修改算法時保存它們的索引是一件微不足道的事情。 這是一個很好解釋的鏈接。 http://en.wikipedia.org/wiki/Longest_common_subsequence_problem

從快速瀏覽,您正在搜索,遞歸和回溯?

我相信為你的源字符串創建一個后綴數組是個好主意。
構造后綴數組具有O(nlogn)復雜度。 定位子字符串具有O(logn)時間復雜度。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM