为什么Data.Sequence.reverse O(n)?

时间:2016-05-30 15:07:18

标签: haskell

文档说Data.Sequence.reverse是O(n)。通过设置标志,有限序列类型不能“反转”吗? (实际上,从“开始计算”结束。)

4 个答案:

答案 0 :(得分:4)

是的,它可以。

但是,这会给每次查询增加一个小的价格(首先必须检查标志!),而且只会给那些使用reverse的用户带来很大回报。后一组可能被认为是小的。由于在类型上抛出适当的标志可以在客户端完成,因此在每个函数的每次使用中,在库中支付这个价格是有意义的。相反,打算频繁调用reverse的程序员如果想要的话,就不得不做出这种性能权衡选择,这对我来说似乎是完全合理的。

答案 1 :(得分:3)

如果你只有一面旗帜,你就会遇到问题。假设我想要

xs <> reverse xs

我怎么能算出来?只有一个标志,我必须在追加之前执行昂贵的反向操作。要实际执行此操作正确,您需要更多标记,遍布树。树周围的全部。树中的每个节点都需要携带反转信息。这绝对是可能的,但它有点贵。更糟糕的是,每个操作的每一步都必须适当地管理反转标志,这对于任何必须编写和维护代码的人来说都是一场噩梦。这个想法已经在某个地方的论文中得到了解决(我不记得哪一个),但在实践中它并不好玩。

如果您的算法主要依赖于反转,那么您应该使用带有大量标记的自定义序列类型,并且只包含您需要的操作(如果您将类型基于Data.Sequence ,你应该从每个大小的字段中窃取一点)。但我并不认为这些算法非常普遍。

答案 2 :(得分:1)

你可以定义

data S a = S { forward :: Seq a , backward :: Seq a }

然后使用

的不变量实现所有操作
forall a :: Type, s :: S a  : reverse (forward s) == backward s . 

这使每个成本翻倍,但给出了恒定时间reverse

答案 3 :(得分:0)

我认为如果O(1)连接允许连接><而没有惩罚(在手指树结构中需要一些黑客攻击),可以在xs >< reversed ys中完成。

期待Data.Sequence所有操作都有相反的对应方(例如takeWhileLtakeWhileR)或采取O(n)(或最差;例如。zip O(min(n1,n2))然后,seq1n1<n2><以外的xs >< ys操作。

在任何情况下,O(min(|xs|,|ys|))都会将O(n)视为最坏情况(四种可能情况中的两种)。实际上这比xs >< reversed ys好一点(规范化为一个独特的方向,需要逆转)。

然后,问题是:

可以比O(min(|xs|,|ys|))更好地完成O(log(min(|xs|,|ys|)))吗?

(理想情况下为><

当然,如果您不使用O(1)操作,则反向操作需要<div>(例如,使用标记来检查何时使用左对齐或右对齐)。

另一方面,如果连接反向序列的性能至关重要,可能存在一些其他最优结构,或者您可以考虑使用@d8d0d65b3f7cf42 strategy