所以基本上我在我的ocaml程序中使用list,原来它是这样的:
val mutable ll : string list = []
.....
ll <- ll@[(foo ar1 ar2)]
然后,当测试相对较大的数据集(超过50k)时,我的程序运行速度太慢。
我原以为是因为上面的代码中有整个列表副本进程,(每次ll <- ll@[]
时)
所以我用这种方式改变了我的代码:
ll <- (foo ar1 ar2)::ll (* extend the head for N times *)
.....
List.rev ll
然而,令我惊讶的是,似乎没有明显的性能提升。
然后我尝试了这样的数组:
let arr = Array.make len "" in
arr.(counter) <- (foo ar1 ar2);
counter := !counter + 1
.....
Array.to_list arr
根据我的理解,它应该比第一种方法更好,但是,可能是因为我的代码中可能存在其他低性能错误,即使我更改了list
,我仍然无法明确改善性能上述方法中的操作代码..
从理论上讲,这是我的问题,在上述三种策略中哪一种具有最佳性能?
我应该能够自己做一些实验,但作为一个更普遍的问题,在处理相关问题时是否有更好的绩效策略?
答案 0 :(得分:2)
在列表末尾添加元素在列表的长度上是线性的,它确实需要复制整个列表。如果以这种方式构建整个列表,则会出现二次复杂性。
如果通过反转列表添加到最后,然后添加到前面,然后再次反转,则同样如此。撤消列表需要复制列表。
通常的技术是以相反的顺序构建列表完全,然后在完成后反转一次。这总体上只有线性复杂性。添加到列表的前面是一个恒定时间操作。
如果您不需要增加数组的大小(需要数组的副本),则在显示时使用数组也具有线性复杂性。
您的代码中可能还有其他慢点掩盖了您对这一点的更改。
在我看来,如果你没有大部分完成编码,你应该考虑基本的复杂性(线性,n log n,二次等)。如果您真的需要担心性能,可以稍后改进。你不想最终抛出你花了很多时间调整的代码。