这是关于计算列表中有多少元素完成给定测试。
我看到了这个功能
(define (numsof p lst)
(length (filter p lst)))
并且认为它效率低,因为它必须经过两个列表,最初的一个用于过滤,然后用length
计算结果。所以我实现了这个来直接计算有多少元素满足测试p
。
(define (amount p lst [acc 0])
(if (empty? lst)
acc
(amount p (cdr lst) (if
(p (car lst))
(add1 acc)
acc))))
接下来,我使用辅助函数运行了一些测试:
; Creates list of natural numbers in [0, range) of given length
(define (random-list length range)
(if (zero? length)
null
(cons (random range) (random-list (sub1 length) range))))
(for ([i 10])
(display "numsof: ") (time (numsof odd? (random-list 999999 9999999)))
(display "amount: ") (time (amount odd? (random-list 999999 9999999)))
(displayln ""))
现在我得到的结果对我来说非常出乎意料,因为我认为我的定义amount
的速度应该是numsof
的两倍,但我还没有真正进入算法性能,所以这个无论如何,猜测可能显然是假的。
在这里,有一些测试结果y' all:
numsof: cpu time: 2875 real time: 2710 gc time: 2060
amount: cpu time: 2578 real time: 2590 gc time: 1872
numsof: cpu time: 1484 real time: 1494 gc time: 719
amount: cpu time: 2547 real time: 2586 gc time: 1779
numsof: cpu time: 2422 real time: 2449 gc time: 1748
amount: cpu time: 2593 real time: 2608 gc time: 1843
numsof: cpu time: 1375 real time: 1360 gc time: 658
amount: cpu time: 2641 real time: 2662 gc time: 1842
numsof: cpu time: 2609 real time: 2593 gc time: 1873
amount: cpu time: 1406 real time: 1400 gc time: 655
numsof: cpu time: 2640 real time: 2652 gc time: 1938
amount: cpu time: 1360 real time: 1384 gc time: 623
有人可以向我解释我的功能是更快还是更慢;无论如何,为什么?测试结果发生了什么,我无法理解它们。
答案 0 :(得分:3)
更新:大部分内容都是错误的。最后跳到“更新:只读这个”。
我得到了类似的结果。
一个可能的解释是,如果Racket list
- 不可变,记住 - 存储它们的长度,以便length
只是查找一个像结构成员的值,而不是遍历所有列表元素。 (我依旧回忆起在Racket邮件列表上读到这样的内容,但遗憾的是现在找不到它。)
可能的证据是,随着列表大小的增加,length
是否需要更长的时间:
(for ([len (in-list (list 100 1000 10000 100000 1000000))])
(define xs (build-list len values))
(time (length xs)))
cpu time: 0 real time: 0 gc time: 0
cpu time: 0 real time: 0 gc time: 0
cpu time: 0 real time: 0 gc time: 0
cpu time: 1 real time: 0 gc time: 0
cpu time: 4 real time: 3 gc time: 0
好的,最后两个时间非零。但它们非常小。出于实际目的,O(1),而不是O(n),即使是相当大的n。
<强>更新强>
实际上,我跳过了一大步。我的回答解释了我认为你的散文所问的内容,而不是你所展示的测试代码。我认为你想要的测试代码 - 实际上符合你的问题 - 将是这样的:
(for ([i 10])
(define xs (random-list 999999 9999))
(display "numsof: ") (time (numsof odd? xs))
(display "amount: ") (time (amount odd? xs))
(displayln ""))
这会创建一个随机列表,然后只会在该列表上运行numsof
与amount
本身的时间。
这为numsof
和amount
提供了基本相同的时间安排。
对此的解释是,如果length
实际上是O(1),因为Racket列表存储它们的长度。
至于为什么原始的,提供的测试代码在random-list
的调用中显示出如此不同的结果?我认为这仅仅是因为内存分配和垃圾收集时间不太可预测。
我在这个答案中说的几乎所有内容都证明是错的。具体做法是:
length
不会缓存。 list?
确实如此。
我打破了第一个剖析规则。我没有使用普通的命令行球拍。因此,我的测量结果受到errortrace
注释的影响。
此外,我可能应该在每个(for ([_ 3]) (collect-garbage))
之前使用time
,专注于独立于垃圾收集时间的算法。
关于我原来答案中唯一的值是,对于足够小的列表,可能很好地执行诸如撰写length
和filter
之类的事情。但实际上,这是一个明显的答案,这个问题就像是,“是否有时候可以在速度上表达清晰度?”。对问题的简短回答是“是的,这取决于”。