【问题标题】:c - empirical run-time analysisc - 经验运行时分析
【发布时间】:2018-01-03 02:56:15
【问题描述】:

嘿,所以我很难掌握经验运行时分析。所以我有这个问题:

假设我们有一个 O(1) 函数,在 n = 2,000 的情况下执行需要 8 秒。如果 n = 10,000,您希望运行时是多少?

现在这是我认为是错误的思考过程: 如果 T(n) 为 O(1) 则 T(n) = c * O(1) 其中 c 只是某个常数。所以求解 c 我们得到 c = T(n) / O(1)。现在,因为我们知道 T(2000) = 8s 并且 n = 2000,我们可以将其代入求解 c, c = (8s) / (2000),得到 0.004。 现在我们必须对 n = 10,000 进行相同的处理,所以在这种情况下 T(10,000)= c * O(1) 在这种情况下,我们可以插入我们之前找到的同一个 c,因为它只是一个常数,并为 O(1) 插入 10,000,这将给我们 T(10,000) = (0.004) * 10,000,等于 40 秒。我只是不确定我的思维过程是否正确。

【问题讨论】:

  • O(1) 不是函数,您不能将c 乘以O(1)O(f) 是一组行为类似于 f 的函数(如果没有 LaTeX,我不会在这里写完整的定义,请查看维基百科)。如果你的TO(1) 中,那么T(n) 将(几乎总是)对所有n 保持不变,否则它将在O(n) 中。所以对我来说,将T(2000)T(10000) 进行比较是没有意义的。那么它也需要大约 8 秒
  • 为什么将O(1) 替换为2000n2000O(1)n 有什么关系。事实上O(1) 甚至不是一个真正的术语。你不能只在方程式中使用它。

标签: c runtime computer-science


【解决方案1】:

正确的答案是我们不能期待任何特定的时间。

说一个函数的运行时间为 O(1) 就是说有某个数字 C 使得该函数的运行时间总是少于 C 秒。如果函数在 8 秒内运行一次,我们知道 C 至少为 8。但它可能是一百万。或者是一个 septillion。

O(1) 告诉我们的是,无论 n 是多少,运行时间都是有限的。运行时间可以随不同的 n 上下浮动,但总是受 C 的限制。

相比之下,将其与运行时间 O(n) 的函数进行比较。这意味着有一个数字 C 使得函数的运行时间总是小于 C*n 秒(除了允许有限数量的异常)。所以我们没有一个恒定的限制;我们有一个取决于 n 的限制。随着 n 的增长,这个函数可能花费越来越多的时间。

或者它可能不会。 O 表示法仅告诉您对函数运行时间的限制。它不会告诉您实际的运行时间。如果我知道你昨天的车里有 1 加仑汽油,而你跑了 30 英里,我知道你的车每加仑至少能跑 30 英里。如果你今天有两加仑,我知道你至少可以走 60 英里,但也许你可以走更多,我不知道你实际上会走多远。我知道你的旅行是有限制的,但我不知道它实际上是什么,也不知道你会走多远。

【讨论】:

  • 但在这种情况下期望 8s不是合理的吗?
  • @Jasen 没有。 n=2000 花了 8s 并不意味着常数是 8s。它可以是任何大于或等于8s 的数字。
  • 统计上有一些预期值解释为什么不是8s
  • @Jasen 如果没有进一步的信息,Eric 是对的,对于 n 的其他值的预期运行时间没有任何有用的说法。也就是说,很容易定义使T(k') 成为您想要的任何东西的函数,只有T(k) = cT(n) = O(1)。你需要更多关于T 的信息——更多的数据点,保证单调性等等——做的不仅仅是疯狂的猜测。但是,如果您想大胆猜测,仅考虑此信息,8s 并不是一个糟糕的猜测。也就是说,这是一个不好的猜测,但你所掌握的信息不足以做出好的猜测。
  • @Jasen:语句“函数的运行时间是 O(1)”和“n = 2000 的函数的运行时间是 8 秒”相当于“这辆车有一个有限的油箱”和“这辆车昨天开了 8 英里。”从这些陈述中,你无法判断这辆车明天会开多远。
【解决方案2】:

big-O 表示法的问题通常是带有大量假设的非正式(不是说草率)表示法。

首先,Ο 符号1 期望在其括号之间有一个函数,所以像Ο这样的符号>(1) 起初不清楚(因为 1 是数字)。
在某些数学领域习惯用“1”表示函数 f:ℕ→ℕ 定义为 f(n) = 1 ∀n ∊ ℕ。
这只是一个为每个输入返回 1 的函数2

要考虑的另一点是,经典定义的 Ο 对单变量函数进行操作,为避免歧义,应始终明确写入两个函数的自变量。
然而,这通常不会这样做,因为从上下文中可以清楚地看出我们只处理特定类型的函数。

为了读者的警觉性并以繁琐的符号为代价,我不会删除自变量,从而将 Ο(1) 写为 Ο(1 (n))。

最后,我更喜欢 f(n) ∊ Ο(1(n)) 表示法,而不是使用等号的常见表示法。


我们希望通过符号 f(n) ∊ Ο(1(n)) 来阐明函数的属性 f(n) w.r.t.另一个为每个输入返回 1 的函数。

big-O 表示法是一种简写(就像极限表示法一样),在我们的特定情况下转换为

f(n) ∊ Ο(1(n))

         ∃N ∊ ℕ: ∀n > N |f(n)| ≤ M · |1(n)| (根据定义)
∃N ∊ ℕ: ∀n > N |f(n)| ≤ M · 1(n)       (1(n) 始终为正)
∃N ∊ ℕ: ∀n > N |f(n)| ≤ M · 1           (对于所有 n,1(n) = 1)
∃N ∊ ℕ: ∀n > N |f(n)| ≤ M               

所以符号简化为 f(n) 被一个(未指定!)常量 M 限制在绝对值上。

这给我们留下了很多函数,即使给定函数在某一点的值。
例如,即使我们知道 f(2000) = 8,这两个函数 g(n) = 8 cos(n-2000) 和 h(n) = 8 en-2000 两者都在 Ο(1(n)) 但它们在 n = 10000 时的值不同!


big-O 表示法很有用,因为它简化了 事情,但到目前为止,它似乎失败了很多。

缺少的部分是我们正在处理算法及其时间复杂性3,所以我们感兴趣的函数 f 是:

  • 非分段
    我们认为分段函数来自两个算法,而不是单个算法的表达式。

  • 单调非递减。 我们假设对于同样的问题,更大的实例需要更长的时间来解决。

在这个假设下,我们可以看到,如果一个函数是有界的,那么在某个时刻它必须停止增加并无限期地保持在那个值上。

这就是 f(n) ∊ Ο(1(n)) 表示法所表达的:f 是一个常数函数(渐近)。

如果 f 是常数,则 f(n) = c,如果 f(2000) = 8,则 c em> = 8,因此 f(10000) = 8。

Big-O 符号的最佳用法如下:取 Ο 括号之间的函数,乘以常数 c 并将其用于f 的位置。

有时我们甚至不关心常量:Ο(1(n)) 意味着如果我们将 n 加倍输出保持不变,Ο (n) 意味着如果我们加倍 n 输出加倍(n = 2 时 n 为 2),Ο(n2) 表示如果我们将 n 加倍,则输出为四倍(对于 n = 2,n2 为 4)等等。


您的方法存在两个问题:说 T(n) = c * O(1) 是正确的但没用,我们没有取得任何进展,因为我们所说的只是 T(n) 是乘以常数由另一个常数(记住 O(1) 是一个常数函数),因此它是一个常数。
另一个错误是将 O(1) 替换为 n 以获得 c 的值。
O(1) 是一个 set 函数,即使我们把它写成 c' (意思是所有可能的常数),这也不是 nn 是输入值,即问题实例的编码大小,而 c' 是一个 任意 常数。

这就是常量函数的全部意义:它们不依赖于输入,如果你愿意,它们在这方面是任意的


1 不是严格意义上的operator。如果我们将注意力集中在定义在集合 CC 值上的函数上(例如 fCC) 然后 ΟCC 函数映射到 set 的 C→C 函数(例如 Ο:(CC)→(CC)) 因此不是来自和去同一个集合的映射。

2 符号故意模糊,让数学家在不同的上下文中使用它;我们将它用于自然值序列,但它可以扩展到任何集合以及返回任何上下文隐含代数结构的“统一性”的含义。

3请注意,运行时是一个非常容易误导的术语!

【讨论】:

  • 不错的答案,我不知道为什么有人会否决这个。
  • 谢谢@Pablo。也许它有问题,但我非常有信心我的推理是正确的(如果我自己这么说的话)。
猜你喜欢
  • 2023-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-31
  • 2014-12-12
  • 2015-07-30
  • 1970-01-01
相关资源
最近更新 更多