Scheme 更好更快的方案功能?
因此,如果列表中有n个元素,则查找列表中的最大元素会增加时间复杂性。我试图实现一个看起来更快的算法Scheme 更好更快的方案功能?,scheme,complexity-theory,racket,time-complexity,asymptotic-complexity,Scheme,Complexity Theory,Racket,Time Complexity,Asymptotic Complexity,因此,如果列表中有n个元素,则查找列表中的最大元素会增加时间复杂性。我试图实现一个看起来更快的算法 (define (clever-max lst) (define (odd-half a-list) (cond ((null? a-list) (list)) ((null? (cdr a-list)) (cons (car a-list) (list))) (else (cons (car a-li
(define (clever-max lst)
(define (odd-half a-list)
(cond ((null? a-list) (list))
((null? (cdr a-list))
(cons (car a-list) (list)))
(else
(cons (car a-list)
(odd-half (cdr (cdr a-list)))))))
(define (even-half a-list)
(if (null? a-list)
(list)
(odd-half (cdr a-list))))
(cond ((null? lst) (error "no elements in list!"))
((null? (cdr lst)) (car lst))
(else
(let ((l1 (even-half lst))
(l2 (odd-half lst)))
(max (clever-max l1) (clever-max l2))))))
这真的更快吗?!您认为渐近时间复杂度是严格限制的吗?给定一个您一无所知的数据列表,如果不检查每个元素并因此占用时间,就无法找到最大元素,因为如果不检查它,您可能会错过它。所以,不,您的算法并不比日志n上的算法快,因为您基本上只是在运行merge-sort 这里有更多的数据 我想了想,意识到我可能应该做点什么,而不仅仅是把这说成是事实。所以我编写了一个快速速度测试。现在完全公开,我不是一个Scheme程序员,所以这是在Common Lisp中,但我认为我忠实地转换了您的算法
;; Direct "iteration" method -- theoretical O(n)
(defun find-max-001 ( list )
(labels ((fm ( list cur )
(if (null list) cur
(let ((head (car list))
(rest (cdr list)))
(fm rest (if (> head cur) head cur))))))
(fm (cdr list) (car list))))
;; Your proposed method
(defun find-max-002 ( list )
(labels ((odd-half ( list )
(cond ((null list) list)
((null (cdr list)) (list (car list)))
(T (cons (car list) (odd-half (cddr list))))))
(even-half ( list )
(if (null list) list (odd-half (cdr list)))))
(cond ((null list) list)
((null (cdr list)) (car list))
(T (let ((l1 (even-half list))
(l2 (odd-half list)))
(max (find-max-002 l1) (find-max-002 l2)))))))
;; Simplistic speed test
(let ((list (loop for x from 0 to 10000 collect (random 10000))))
(progn
(print "Running find-max-001")
(time (find-max-001 list))
(print "Running find-max-002")
(time (find-max-002 list))))
现在你可能会问自己,为什么我只使用10000作为列表大小,因为对于渐近计算来说,这是相当小的。事实上,sbcl认识到第一个函数是尾部递归的,因此将其抽象为一个循环,而第二个函数则没有,因此,在不破坏堆栈的情况下,这是我能得到的最大值。虽然从下面的结果可以看出,这足以说明这一点
"Running find-max-001"
Evaluation took:
0.000 seconds of real time
0.000000 seconds of total run time (0.000000 user, 0.000000 system)
100.00% CPU
128,862 processor cycles
0 bytes consed
"Running find-max-002"
Evaluation took:
0.012 seconds of real time
0.012001 seconds of total run time (0.012001 user, 0.000000 system)
[ Run times consist of 0.008 seconds GC time, and 0.005 seconds non-GC time. ]
100.00% CPU
27,260,311 processor cycles
2,138,112 bytes consed
即使在这个水平上,我们也在谈论大规模的经济放缓。在直接检查每个项目一次的方法降低到算法的10k评估之前,需要增加到大约一百万个项目
(let ((x (loop for x from 0 to 1000000 collect (random 1000000))))
(time (find-max-001 x)))
Evaluation took:
0.007 seconds of real time
0.008000 seconds of total run time (0.008000 user, 0.000000 system)
114.29% CPU
16,817,949 processor cycles
0 bytes consed
最后的想法和结论
所以接下来要问的问题是,为什么第二种算法真的要花那么长的时间。在不深入细节的情况下,有几件事真的很突出
第一个是犯人。现在是的,cons是O1,但这仍然是系统要完成的另一个操作。它要求系统分配和释放内存,并启动垃圾收集器。第二件真正让人吃惊的事情是,你基本上是在运行一个合并排序,除了抓取列表的下半部分和上半部分之外,你还要抓取奇偶节点,这也需要更长的时间,因为你每次都要迭代来构建列表。这里的算法充其量是一个On log n算法请注意,它是一种合并排序算法,非常适合排序,但它会带来很多额外的开销。给定一个您一无所知的数据列表,如果不检查每个元素,就无法找到最大元素,因此需要花费时间,因为如果您不检查它,你可能会错过它。所以,不,您的算法并不比日志n上的算法快,因为您基本上只是在运行merge-sort 这里有更多的数据 我想了想,意识到我可能应该做点什么,而不仅仅是把这说成是事实。所以我编写了一个快速速度测试。现在完全公开,我不是一个Scheme程序员,所以这是在Common Lisp中,但我认为我忠实地转换了您的算法
;; Direct "iteration" method -- theoretical O(n)
(defun find-max-001 ( list )
(labels ((fm ( list cur )
(if (null list) cur
(let ((head (car list))
(rest (cdr list)))
(fm rest (if (> head cur) head cur))))))
(fm (cdr list) (car list))))
;; Your proposed method
(defun find-max-002 ( list )
(labels ((odd-half ( list )
(cond ((null list) list)
((null (cdr list)) (list (car list)))
(T (cons (car list) (odd-half (cddr list))))))
(even-half ( list )
(if (null list) list (odd-half (cdr list)))))
(cond ((null list) list)
((null (cdr list)) (car list))
(T (let ((l1 (even-half list))
(l2 (odd-half list)))
(max (find-max-002 l1) (find-max-002 l2)))))))
;; Simplistic speed test
(let ((list (loop for x from 0 to 10000 collect (random 10000))))
(progn
(print "Running find-max-001")
(time (find-max-001 list))
(print "Running find-max-002")
(time (find-max-002 list))))
现在你可能会问自己,为什么我只使用10000作为列表大小,因为对于渐近计算来说,这是相当小的。事实上,sbcl认识到第一个函数是尾部递归的,因此将其抽象为一个循环,而第二个函数则没有,因此,在不破坏堆栈的情况下,这是我能得到的最大值。虽然从下面的结果可以看出,这足以说明这一点
"Running find-max-001"
Evaluation took:
0.000 seconds of real time
0.000000 seconds of total run time (0.000000 user, 0.000000 system)
100.00% CPU
128,862 processor cycles
0 bytes consed
"Running find-max-002"
Evaluation took:
0.012 seconds of real time
0.012001 seconds of total run time (0.012001 user, 0.000000 system)
[ Run times consist of 0.008 seconds GC time, and 0.005 seconds non-GC time. ]
100.00% CPU
27,260,311 processor cycles
2,138,112 bytes consed
即使在这个水平上,我们也在谈论大规模的经济放缓。在直接检查每个项目一次的方法降低到算法的10k评估之前,需要增加到大约一百万个项目
(let ((x (loop for x from 0 to 1000000 collect (random 1000000))))
(time (find-max-001 x)))
Evaluation took:
0.007 seconds of real time
0.008000 seconds of total run time (0.008000 user, 0.000000 system)
114.29% CPU
16,817,949 processor cycles
0 bytes consed
最后的想法和结论
所以接下来要问的问题是,为什么第二种算法真的要花那么长的时间。在不深入细节的情况下,有几件事真的很突出
第一个是犯人。现在是的,cons是O1,但这仍然是系统要完成的另一个操作。它要求系统分配和释放内存,并启动垃圾收集器。第二件真正让人吃惊的事情是,你基本上是在运行一个合并排序,除了抓取列表的下半部分和上半部分之外,你还要抓取奇偶节点,这也需要更长的时间,因为你每次都要迭代来构建列表。这里的算法充其量是一个On log n算法,请注意,这是一种合并排序,非常适合排序,但它会带来很多额外的开销。据我所知,您刚刚将On算法变成了On log n算法?是什么让你认为它会更快?除非你知道列表是如何排序的,否则max算法必须查看每个元素,这不能比On更好。据我所知,你刚刚把On算法变成了On log n算法?什么妈妈 你觉得会更快吗?除非您知道列表是如何排序的,否则max算法必须查看每个元素,这不会比On更好。