“算法序列也可产生状态转移,但评价目标不同。在线算法在请求到达后立刻选择动作并与离线最优比较,动态数据结构则按 update/query 操作序列报告成本;追溯数据结构甚至允许编辑过去操作并重…”
形式陈述
时间线模型
固定一个ADT及初态,设其更新按全序时间戳组成序列
部分追溯允许在任意过去修改更新,但只查询当前末端状态;完全追溯还允许 query
直觉
追溯结构允许修改过去的操作日志,并要求所有后续状态像历史原本如此发生一样被重算。它维护的不是若干冻结版本,而是一条操作依赖时间线;一次旧时刻编辑可能改变之后每个查询,所以可组合摘要与因果传播是核心成本来源。
例子与边界
队列例子
原时间线在
实现图像与证明责任
通用朴素实现把时间线存成有序序列,每次追溯更新后重放后缀,若一次原 ADT 操作成本至多
能力边界
rollback 只按栈顺序撤销最近修改;partial/full persistence 决定从哪些版本查询和分叉;retroactivity 决定能否改写过去的操作。四者没有简单的同成本包含关系。完全追溯还可能比部分追溯严格更难;必须单独报告 query
推论与应用
可组合操作与难例
一个明确可组合的版本固定幺半群
“状态属于某个群”本身不足以保证更新具有这种形式,任意状态变换仍须另行分析。队列与优先队列的过去删除可能改变后续被删元素,因此不能直接把它们的全部操作替换成上述固定乘数;它们可以有各自更专门的追溯算法。
因此复杂度来自操作语义,而非时间线容器本身。历史树能在
时间编辑的执行轨迹
考虑时间线 enqueue(a)@1, enqueue(b)@4, dequeue()@7。在时刻 3 插入 dequeue()@3 后,原来时刻 7 返回的元素会从 a 改为 b;这说明修改历史不能只修补时刻 3 的快照,而要重新建立其后的因果关系。
部分追溯结构只允许询问“现在”的状态:
- 按时间键把操作放入可分裂、可合并的搜索树;
- 每个子树保存该段操作对抽象状态的可组合摘要;
- 插入或删除历史操作后,只沿搜索路径重算摘要;
- 根摘要给出当前状态,却不一定支持任意过去时刻查询。
完全追溯还要在任意时间切开前缀并返回当时答案。若整段操作没有已证明的定长摘要,或组合与查询解码不能在所需成本内完成,就不能直接套用这一高效区间聚合;这正是“操作日志可排序”与“追溯接口可高效实现”之间的鸿沟。
参考资料
- Erik Demaine, John Iacono, Stefan Langerman, Retroactive Data Structures, ACM Transactions on Algorithms, 2007.
- James Driscoll et al., Making Data Structures Persistent, JCSS, 1989.