文档说Data.Sequence.reverse
是O(n)。通过设置标志,有限序列类型不能“反转”吗? (实际上,从“开始计算”结束。)
答案 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所有操作都有相反的对应方(例如takeWhileL
〜takeWhileR
)或采取O(n)
(或最差;例如。zip
O(min(n1,n2))
然后,seq1
等n1<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。