Algorithm 递归算法的时间复杂度分析

Algorithm 递归算法的时间复杂度分析,algorithm,time-complexity,Algorithm,Time Complexity,我想知道以下算法的复杂度是多少,最重要的是,一步一步的推导过程 我怀疑它是O(长度(文本)^2*长度(模式)),但我在求解递归方程时遇到了问题 在对递归调用进行记忆(即动态编程)时,复杂性将如何提高 此外,我希望能得到一些技术/书籍的指导,帮助我学习如何分析这种算法 在Python中: def count_matches(text, pattern): if len(pattern) == 0: return 1 result = 0 for i in xrange(len(tex

我想知道以下算法的复杂度是多少,最重要的是,一步一步的推导过程

我怀疑它是O(长度(文本)^2*长度(模式)),但我在求解递归方程时遇到了问题

在对递归调用进行记忆(即动态编程)时,复杂性将如何提高

此外,我希望能得到一些技术/书籍的指导,帮助我学习如何分析这种算法

在Python中:

def count_matches(text, pattern):
  if len(pattern) == 0: return 1

  result = 0
  for i in xrange(len(text)):
    if (text[i] == pattern[0]):
      # repeat the operation with the remaining string a pattern
      result += count_matches(text[i+1:], pattern[1:])

  return result
在C中:

int count_匹配(常量字符文本[],int text_大小,
常量字符模式[],整数模式大小){
if(pattern_size==0)返回1;
int结果=0;
对于(int i=0;i
注意:算法有意地对每个子串重复匹配。请不要只关注算法的复杂性,而关注算法执行的匹配类型


对算法中的(现已修复)错误表示歉意

恐怕您的算法不适合模式匹配。 主要是因为一旦发现第一个字符匹配,它将在文本的其余部分搜索子字符串。 例如,对于文本“abbcc”和模式“accc”,您的算法将返回等于1的结果

你应该考虑实现模式匹配的“天真”算法,它与你试图做的非常类似,但是没有递归。 它的复杂度是O(n*m),其中“n”是文本长度,“m”是模式长度。 在Python中,可以使用以下实现:

text = "aaaaabbbbcccccaaabbbcccc"
pattern = "aabb"
result = 0

    index = text.find(pattern)
    while index > -1:
        result += 1
        print index
        index = text.find(pattern, index+1)

return result

关于这方面的书籍,我最好的推荐书是Cormen's,它涵盖了关于算法和复杂性的所有材料

嗯。。。我可能错了,但在我看来,您的运行时应该关注这个循环:

for c in text:
    if (c == pattern[0]):
      # repeat the operation with the remaining string a pattern
      result += count_matches(text[1:], pattern[1:])
基本上让文本的长度为n,我们不需要图案的长度

第一次运行此循环时(在父函数中),我们将对其进行n调用。每个n调用在最坏的情况下都将调用程序的n-1实例。然后那些n-1实例将在最坏的情况下调用n-2实例,依此类推

这将导致一个方程,它将是n*(n-1)(n-2).*1,它是n。因此,最坏情况下的运行时是O(n!)。非常糟糕(:

我使用会导致最坏运行时的输入运行了您的python程序几次:

在[21]中:计数匹配(“aaaaaaa”,“aaaaaaaaa”)

Out[21]:5040

在[22]中:计数匹配(“aaaaaa”,“aaaaaa”)

Out[22]:40320

在[23]中:计数匹配(“aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa”)

Out[23]:362880

最后一个输入是9个符号和9!=362880

要分析算法的运行时,您需要首先考虑导致最差运行时的输入。在您的算法中,最佳和最差运行时相差很大,因此您可能需要平均情况分析,但这相当复杂。(您需要定义什么输入是平均的,以及最差情况出现的频率。)

动态编程有助于减轻运行时的负担,但分析比较困难。让我们首先编写一个简单的未优化动态编程版本:

cache = {}
def count_matches_dyn(text, pattern):
  if len(pattern) == 0: return 1

  result = 0
  for c in text:
    if (c == pattern[0]):
      # repeat the operation with the remaining string a pattern
      if ((text[1:], pattern[1:]) not in cache.keys()):
        cache[(text[1:], pattern[1:])] = count_matches_dyn(text[1:], pattern[1:])
        result += cache[(text[1:], pattern[1:])]
      else:
        result += cache[(text[1:], pattern[1:])]

  return result
在这里,我们将所有对count_matches的调用缓存在一个字典中,因此当我们使用相同的输入调用count matches时,我们将得到结果,而不是再次调用该函数(这称为)

现在让我们分析一下,主循环

  for c in text:
    if (c == pattern[0]):
      # repeat the operation with the remaining string a pattern
      if ((text[1:], pattern[1:]) not in cache.keys()):
        cache[(text[1:], pattern[1:])] = count_matches_dyn(text[1:], pattern[1:])
        result += cache[(text[1:], pattern[1:])]
      else:
        result += cache[(text[1:], pattern[1:])]
将在第一次调用时运行n次(我们的缓存为空)。但是第一次递归调用将填充缓存:

cache[(text[1:], pattern[1:])] = count_matches_dyn(text[1:], pattern[1:])
同一循环中的每一个其他调用都将花费(O(1)。因此,基本上,顶级递归将花费O(n-1)+(n-1)*O(1)=O(n-1)+O(n-1)=2*O(n-1)。您可以看到,从递归下一步的调用中,只有第一个会随着许多递归调用而下降(O(n-1))其余的将花费O(1),因为它们只是字典查找。考虑到所说的一切,运行时间是(2*O(n-1),摊销到O(n)

免责声明。我不完全确定动态编程版本的分析,请随时纠正我(:


免责声明2.动态编程代码包含昂贵的操作(文本[1]、模式[1])分析中没有考虑这些因素。这是故意的,因为在任何合理的实现中,您都可以大大降低这些调用的成本。关键是要说明简单缓存可以如何大大减少运行时间。

我的直觉是,复杂性为O(长度(文本)^3)是不正确的。它实际上是O(n!)纯粹是因为实现是形式化的

def do_something(relevant_length):
    # base case

    for i in range(relevant_length):
        # some constant time work

        do_something(relevant_length - 1)
如中所述

如果使用memonization,递归树只生成一次,之后每次都会进行查找

描绘递归树的形状

我们每层前进一个字符。有两个基本情况。当我们到达模式的末尾或文本中不再有任何字符可用于迭代时,递归将见底。第一个基本情况是显式的,但第二个基本情况仅在给定实现时出现

所以递归树的深度(高度)是min[长度(文本),长度(模式)]

有多少子问题?我们每层也会进步一个字符。如果文本中的所有字符都被比较,使用高斯技巧求和S=[n(n+1)]/
def do_something(relevant_length):
    # base case

    for i in range(relevant_length):
        # some constant time work

        do_something(relevant_length - 1)
PTTTTT
PTTTT
PTTT
PTT
PT
P
PTTTTTTTTT
PTTTTTTTT
PTTTTTTT
PTTTTTT
PTTTTT
PTTTT
P
 P
  P
   P
    P
     P
if len(pattern) < len(text): return 0