【发布时间】:2013-11-04 18:58:35
【问题描述】:
我正在寻找类似#'delete-duplicates 的东西,但我知道列表中的所有元素都已经排序,或反向排序,或至少已安排重复项彼此相邻。我希望利用这些知识来确保执行速度与列表中元素数量的平方不成比例。使用#'maplist 来开发我自己的解决方案是微不足道的,但是语言中已经有一些东西了吗?重新发明轮子会很尴尬。
需要明确的是,对于较大长度的列表,我希望删除的运行时间与列表的长度成正比,而不是与该长度的平方成正比。这是我希望避免的行为:
1 (defun one-shot (cardinality)
2 (labels ((generate-list (the-count)
3 (let* ((the-list (make-list the-count)))
4 (do ((iterator 0 (1+ iterator)))
5 ((>= iterator the-count))
6 (setf (nth iterator the-list) iterator))
7 the-list)))
8 (let* ((given-list (generate-list cardinality))
9 (stripped-list)
10 (start-time)
11 (end-time))
12 (setf start-time (get-universal-time))
13 (setf stripped-list (delete-duplicates given-list :test #'eql))
14 (setf end-time (get-universal-time))
15 (princ "for n = ")
16 (princ cardinality)
17 (princ ", #'delete-duplicates took ")
18 (princ (- end-time start-time))
19 (princ " seconds")
20 (terpri))))
21 (one-shot 20000)
22 (one-shot 40000)
23 (one-shot 80000)
for n = 20000, #'delete-duplicates took 6 seconds
for n = 40000, #'delete-duplicates took 24 seconds
for n = 80000, #'delete-duplicates took 95 seconds
【问题讨论】:
-
我认为语言中没有类似的东西,但这几乎是run-length encoding,没有保留出现次数。另外,为什么不直接使用
time宏来做你的计时(在确保编译你的函数之后)? -
您正在使用一种非常糟糕的方式来创建数字列表。您的子功能 GENERATE-LIST 在 Lisp 中使用了至少三个大的反模式。
-
Rainer,感谢您的提醒。您能解释一下您看到的前三个反模式吗?
-
您列出了 NIL - 第一个循环。然后你在一个单独的循环中计数 - 第二个循环。然后在列表中设置第 n 个项目 - 每个项目的另一个循环。就这样遍历列表 1/2 N^2 次。 反模式:1)多个循环。 2)首先创建列表,然后设置内容。 3) 重复访问第 n 个元素。 更好:1)一个循环。 2)在列表创建期间设置项目。 3) 避免 NTH 并且只使用一个循环,这里是初始循环。 记住:列表是单独链接的 cons 单元格。列表不是数组。
-
还值得指出的是,正在构建的列表对于
delete-duplicates来说并不是一个好的测试用例。一个实现可以有效地通过列表建立一个元素的哈希表来检查是否有任何重复的元素,如果没有,则简单地返回列表。这在列表的长度上是线性的,并且是正确的。您的测试列表没有重复的元素,因此它并没有真正测试delete-duplicates的主要功能。
标签: lisp common-lisp clisp gnu-common-lisp