List 列表筛选+;计数与直接计数?奇怪的测试结果

List 列表筛选+;计数与直接计数?奇怪的测试结果,list,racket,counting,List,Racket,Counting,这是关于计算列表中有多少元素完成了给定的测试 我看到了这个函数 (define (numsof p lst) (length (filter p lst))) 并认为这将是低效的,因为它必须检查两个列表,第一个用于过滤,然后使用长度计算结果。因此,我实现了这一点,以直接计算有多少元素完成了测试p (define (amount p lst [acc 0]) (if (empty? lst) acc (amount p (cdr lst) (if

这是关于计算列表中有多少元素完成了给定的测试

我看到了这个函数

(define (numsof p lst)
  (length (filter p lst)))
并认为这将是低效的,因为它必须检查两个列表,第一个用于过滤,然后使用
长度
计算结果。因此,我实现了这一点,以直接计算有多少元素完成了测试
p

 (define (amount p lst [acc 0])
  (if (empty? lst)
      acc
      (amount p (cdr lst) (if
                           (p (car lst))
                           (add1 acc)
                           acc))))
接下来,我使用helper函数运行了一些测试,如下所示:

; 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 ""))
现在,我得到的结果对我来说是出乎意料的,因为我认为我的定义
数量
应该是
numsof
的两倍,但是我还没有真正了解算法性能,所以这个猜测对你来说显然是错误的

在这里,给大家一些测试结果:

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

有人能解释一下我的功能是快还是慢吗;无论如何,为什么?测试结果到底是怎么回事,我搞不懂。更新:大部分都是错误的。跳到结尾处的“更新:只需阅读此内容”

我得到了类似的结果

一种可能的解释是,如果Racket
list
s(记住,它们是不可变的)存储它们的长度,那么
length
只是像struct成员一样查找一个值,而不是遍历所有列表元素。(我隐约记得在Racket邮件列表上读到过类似的内容,但不幸的是现在找不到。)

is的可能证据是,随着列表大小的增加,
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),因为球拍列表存储它们的长度

至于您提供的原始测试代码为什么在调用
随机列表
时显示如此不同的结果?我认为这只是因为内存分配和垃圾收集时间不太可预测


更新:只需阅读此 我在这个答案中说的几乎每句话都是错的。具体而言:

  • length
    不缓存<代码>列表?是

  • 我打破了第一条貌相规则。我没有使用简单的命令行技巧。因此,我的测量结果受到
    errortrace
    注释的影响

  • 此外,我可能应该在每次
    时间之前使用
    (for([\u3])(collect garbage))
    ,以关注与垃圾收集时间无关的算法


在我最初的回答中,唯一的价值是,对于足够小的列表,编写
length
filter
这样的操作可能是好的。但事实上,这是一个很明显的答案,回答了这样一个问题:“有没有过这样的时候,人们倾向于表达的清晰而不是速度?”。对这个问题的简短回答是,“是的,这取决于情况。”

我记得HTDP在实现列表时讨论了在使用列表存储长度和不计算长度之间的权衡。总之,教学语言不存储长度。也许球拍的处理方式有所不同。我会调查的,谢谢。