我最近正在使用Data.Sequence
开展calculating moving average from a stream of input的实施工作。我想我可以通过使用双端队列将整个操作变为O(n)。
我的第一次尝试(在我看来)更容易阅读,但不是真正的双端队列。看起来像是:
let newsequence = (|>) sequence n
...
let dropFrontTotal = fromIntegral (newtotal - index newsequence 0)
let newsequence' = drop 1 newsequence.
...
根据Data.Sequence
的{{3}},index
应采用 O(log(min(i,ni)))而drop
还应该采用 O(log(min(i,ni)))。
这是我的问题:
如果我drop 1 someSequence
,这不意味着 O(log(min(1,(length someSequence))))的时间复杂度,在这种情况下意味着: 为O(log(1))?
如果是, O(log(1)) 是否有效?
我对index someSequence 0
有同样的问题:该操作最终不应该是 O(log(0))吗?
最终,我对我的理解有足够的怀疑,我使用Criterion
来对这两个实现进行基准测试,以证明index/drop
版本较慢(并且随着输入的增长,它的速度变慢了)。我的机器上的非正式结果可以在链接的要点上看到。
我仍然不明白如何计算这些操作的时间复杂度,我希望任何人都可以提供任何澄清。
答案 0 :(得分:2)
你的建议对我来说是正确的。
作为一个小警告,请记住这些是摊销的复杂性界限,因此单个操作可能需要不止一个时间,但是长链操作只需要链的数量的常数倍
如果您使用标准进行基准测试并在每次计算时“重置”状态,您可能会看到非恒定的时间成本,因为“重置”会阻止摊销。这实际上取决于你如何进行测试。如果你从一个序列开始,对它执行一系列操作,它应该没问题。如果使用相同的操作数重复多次单个操作,则可能不正常。
此外,我认为O(log(...))
之类的界限实际上应该被理解为O(log(1 + ...))
- 您实际上不能将O(log(1)) = O(0)
或更差O(log(0))= O(-inf)
视为复杂界限