0

我如何知道算法是否在 O(n) 时间或 O(nlogn) 中运行?

4

3 回答 3

2

那么这是我的第一个答案......

算法的 O() 表示法(尽管它可以应用于任何函数)有点像算法增长率的上限。您可能关心的情况是与您的算法对给定输入大小执行的操作数量相关的函数。你似乎要求一步一步的方法,所以我会尝试一个:

  1. 编写一个函数,描述您的算法针对输入大小为 n(或现有的 n 个元素的数据结构等)执行的操作数。

  2. 使用 Big-O 表示法的定义简化该函数。大多数情况下,这很简单。然而,有些场合需要理解数学形式和大 O 的真正含义。我不会在这里深入讨论,因为这对教科书或您的 CS 教授来说是一个更好的工作。忽略细节,您将术语(如果它是术语的总和)保留在增长最大的函数中并摆脱其他所有内容。如果该术语有任何常数系数,也请去掉它们。例如: f(n) = 4n^2 + 10000n + 500 将按照前面描述的方式修改为 n^2,因为 4n^2 项的增长速度快于其他两项,并且 4 的系数下降;离开n^2。

这是一个示例:假设List是一个数字数组,而lSize是数组中的项目数。

for (int i=0; i < lSize; ++i){
  for (int j = i+1; j<=lSize; ++j){
    if (List[i] > List[j]){ //swap the elements
      int temp = List[i];
      List[i] = List[j];
      List[j] = temp;
    }
   }
}

让我们计算使用上面显示的选择排序算法对包含 n 个项目的列表进行排序所需的操作次数。

步骤1

对于 n 的列表大小,该算法需要多少次操作才能排序(以 n 为单位)?这里 lSize 取 n 的值。我们有两个 for 循环,一个嵌套在另一个内部。外层循环很简单,它对 List 中的每个元素执行一次;或n次。在该循环内部是另一个 for 循环,其操作的数量并不立即那么明显。此循环的迭代次数取决于外部 for 循环中 i 的当前值。如果您坐下来使用一些测试值并写出出现的系列,您会看到内部循环首先运行 n 次,然后 (n-1) 次,然后 (n-2) 次,依此类推,总计数描述为: n + (n-1) + (n-2) ... 2 + 1。你会注意到这是从 1 到 n 的所有数字的总和。如果 n = 100,则内部循环的总迭代次数将为 100 + 99 + 98 + 97 ... 2 + 1。这是一个算术级数,可以将 n 项简化为:n*(n-1)/2。这就是内部循环内的操作被评估的次数。由于内部循环中有 3 个操作,这意味着 n 项的操作总数为 3 * n*(n-1)/2。为下一部分重写为更容易的形式,(3/2)n^2-(3/2)n。

第2步

我们现在有了我们的功能。其中 f() 是我们的算法 f(n) = (3/2)n^2-(3/2)n 执行的操作数。显然,增长最快的项是 (3/2)n^2,因此我们删除了 (3/2)n。这留下了 (3/2)n^2,我们去掉了 (3/2) 系数。这留下了 n^2。因此,我们可以说 f(n) 是 O(n^2)。

这基本上是用 O() 表示法描述算法的秘诀。这忽略了重要的原因,并描述了产生答案的无脑配方。为什么要这样总结算法?为什么可以忘记一个术语或系数?这些问题的答案可以在更严格的教科书描述中找到。

我希望我没有犯任何可怕的错误,这有点晚了。

于 2013-08-25T06:59:30.360 回答
1

复杂性取决于您的输入是什么以及您的算法使用哪些变量来计算输出。

几个简单的例子可以帮助你理解基本的算法复杂度:

  • 以下是 O(n)

    // This is O(n)
    for (int i = 0; i < array.length; i++) {
      sum += array[i];
    }
    
    // This is O(n)
    for (int i = 0; i < array.length / 2; i++) {
      sum += array[i];
    }
    
    // This is O(n)
    for (int i = 0; i < array.length / 200; i++) {
      sum += array[i];
    }
    

    因为您所做的迭代次数始终取决于数组的大小(此处为 N)。

  • 但这是 O(1):

    // This is O(1)
    array[i]; // where i is an int
    
    // This is O(1)
    for (int i = 0; i < 10000; i++) {
      sum += array[i % array.length];
    }
    

    无论数组的大小是:1 或 10,000 或 10 teras...

  • 这是 O(n^2):

    // O(n) that contain O(n) operation => O(n^2)
    for (int i = 0; i < matrix.length; i++) {      
      for (int j = 0; j < matrix[i].length; j++) { // O(n)
        sum += matrix[i][j];
      }
    }
    

    假设这是一个方阵(否则内部循环将在 O(m) 中运行)

有很多著名的算法需要研究,以了解如何很好地计算算法复杂度。例如,对于 O(log(n)) 复杂度,请查看:http ://en.wikipedia.org/wiki/Binary_search_algorithm

在评估现实世界的算法时,某些操作的复杂性可能隐藏在您用于存储值的表示中。例如,在 Java 中,您有 ArrayList (a) 和 LinkedList (ll),它们可以与接口 List (l) 一起使用。所以下面一行:

l.get(i); // 其中 i 是 int,l 是 List。

根据列表的实际结构,可能需要 O(1) 时间或 O(n) 时间。\

您可以阅读此页面以获取更多信息: http: //en.wikipedia.org/wiki/Analysis_of_algorithms http://en.wikipedia.org/wiki/Computational_complexity_theory 算法复杂度时间

于 2013-08-25T05:47:21.470 回答
0

首先,您可以通过以下方式学习这种东西:

  • 阅读描述算法并分析其复杂性的教科书(或研究论文或其他资源),或

  • 自己分析算法;即做数学。

当您获得足够的经验时,您可以选择一些直观的捷径来估计算法的复杂性。


有些人建议使用一种技术来衡量问题大小变量(或多个变量)的一系列值的性能,将它们绘制成图表并尝试拟合曲线。但是,这样做存在问题:

  • 通常很难知道变量实际上是什么。
  • 通常变量之间的关系非常复杂,以至于不可能进行可靠的曲线拟合。
  • 通常很难获得有效的(即有代表性的)测量值。
  • 一些算法对最佳情况、平均情况和/或最坏情况行为具有不同的复杂性度量。

简而言之,这种方法通常不能给出可靠的答案。

于 2013-08-25T05:49:07.807 回答