【问题标题】:Lisp gethash complexityLisp gethash 复杂度
【发布时间】:2019-03-11 21:12:17
【问题描述】:

gethash 函数的时间复杂度是多少? 例如,在 C++ 中,map 的搜索采用 O(log(n)),而 unordered_map 则采用 O(1)。这两件事都写在描述中,但我在 Lisp 中找不到gethash 的任何此类参考。

实际上,这扩展到所有标准库函数。我在哪里可以找到它们的复杂性,或者我可以吗?谈论 sbcl,如果这很重要的话。

【问题讨论】:

  • 在程序员可以使用“容器”、“地图”、“集合”等高级“抽象”数据类型的语言中,必须在语言规范中回答此类问题。在 Common Lisp 中,只有具有具体表示的具体数据结构:列表、数组和哈希表是如何实现的众所周知,任何关于数据结构的好书都讨论了它们的复杂性。例如,对于哈希表,您可以看到this SO question
  • 好吧,我特别问这个问题,因为正如我在问题中提到的,哈希表可以是有序的或无序的,而且这两者的时间复杂度不同。
  • 哈希表,顾名思义,是基于哈希函数的,所以没有排序。

标签: algorithm lisp common-lisp hashtable


【解决方案1】:

ANSI CL 标准没有指定算法复杂度的原因 库函数是它不是它的工作。该标准描述了行为,并将性能留给了特定于实现的文档。 假设所有实现都将提供最佳理论性能(否则没有人会使用它)。

为了回答您的具体问题,gethash 在所有实现中都是O(1)

【讨论】:

  • 我怀疑 gethash 是 O(1),而 sethash 是摊销 O(1)。
【解决方案2】:

通常的期望是 GETHASH 的 Lisp 实现在 O(1) 中运行。

但它可能有令人惊讶的隐藏成本。 复制垃圾收集器(某些 GC 就是这样)可能会在内存中复制哈希表。这可能会触发表的重新散列。

【讨论】:

  • GC 成本是摊销的,不会影响渐近行为。
【解决方案3】:

标准没有也不应该告诉您像gethash 这样的函数的复杂性。想象一下,如果它确实这样做了:这将限制语言的实现使用符合标准复杂性的功能的实现。如果有人想出了更好的散列函数,那么实现就无法使用它。

好吧,您可能会说,这很愚蠢:标准只需要指定复杂性的上限。这将允许实现使用它喜欢的任何更好的功能,对吧?但这也不是答案:可能(并且在许多情况下)算法在最坏情况下的性能很差,但预期性能要好得多。在标准中处理这个问题要么是不可能的(我认为),要么会导致它完全被复杂的描述所涵盖,即什么复杂性是可以接受的,什么时候可以接受,什么时候不可以接受。

提供复杂度上限也会排除那些想要在(例如)实现的复杂程度和规模以及在某些情况下的性能之间进行权衡的实现:应该允许实现具有哈希表,这些哈希表在内部, alists,例如:对于少量的键,它们通常非常快,但对于大量的键,它们的性能就会下降。应该允许这样的实现。


在某些情况下,事情的复杂性从标准来看是显而易见的:似乎很明显length 的时间复杂度与列表的长度呈线性关系(除非它可能不会终止,如果列表是循环的)。但这不是真的:没有什么可以阻止实现在某处保持长度值,这会使length 在某些情况下成为恒定时间。这显然是一个英勇的(我认为在实施上难以置信)和无用的优化,但它不是标准的地方来排除它。

作为一个语言(不是 CL 的实现!)执行类似操作的示例,请考虑对 Racket 的 list? 谓词的以下描述:

如果 v 是列表,则返回 #t:空列表或第二个元素是列表的对。由于内部缓存,此过程实际上需要恒定的时间(因此任何必要的对的遍历原则上都可以算作分配对的额外成本)。

我不完全理解这一点,但我认为 Racket 必须在其 cons 对象中实现一个标志,告诉您它的 cdr 是一个正确的列表,然后依靠不变性来知道这是真的吗?真的。如果 CL 有这样的功能,那么让它在恒定时间内运行会非常困难。

【讨论】:

  • > 如果有人想出了一个更好的散列函数,那么实现就不能使用它。 如果规范在“big OH”中给出,那是完全不正确的,它给出了一个上限。如果访问规范说 O(log n),并且提供了一个恒定时间的实现,那就是一致的; O(1) 函数也是 O(log n)。但是 O(n log n) 或 O(n^2) 将是不合格的。
  • @Kaz:我解释了为什么在以下段落中指定上限也会受到过度限制。
  • 确实,我无法理解为什么 C++ 人认为将这样的东西放入标准是个好主意。当然,“我不明白他们为什么把它放进去”是 C++ 的一个主要主题。
  • @Kaz:像这样的东西是 in C++ 标准吗?我现在感觉有点不舒服。
  • 问题中提到了正确的!是的; C++ 标准规定了算法操作的渐近复杂度。显然,随着时间的推移,它有tightened up 其中一些。现在std_sort 不能通过诸如快速排序之类的东西来实现,它可以在某些输入上退化为二次行为。
猜你喜欢
  • 2010-11-09
  • 2018-06-27
  • 1970-01-01
  • 1970-01-01
  • 2016-02-13
  • 2021-04-22
  • 2012-08-14
  • 2018-04-05
  • 1970-01-01
相关资源
最近更新 更多