对数时间方案算法

时间:2014-09-03 01:18:39

标签: algorithm scheme time-complexity

我目前正在通过Hailperin,Kaiser和Knight的计划书“Concrete Abstractions”。我已经编写了如下所示的相同算法的递归和迭代版本。该算法用于对数量或事物n个时间执行操作。所以,通常,一起复制(操作n事物)。例如,我可以找到3个像这样的立方体,一起复制(* 3 3)或3个正方形 - 复制 - (* 2 3)。

递归版:

(define together-copies-of-linrec
  (lambda (combine quantity thing)
    (define together-iter
      (lambda (combine start thing)
        (if (= start quantity)
            thing
            (combine (together-iter combine (+ start 1) thing)
                     thing))))
      (together-iter combine 1 thing)))

迭代版本:

(define together-copies-of-linit
  (lambda (combine quantity thing)
    (define together-iter
      (lambda (combine start newthing)
        (if (= start quantity)
            newthing
            (together-iter combine (+ start 1) (combine newthing thing)))))
    (together-iter combine 1 thing)))

现在,我需要编写这个算法的对数时间版本,但我真的不知道从哪里开始。在这种情况下,我没有看到如何减少每个实例的操作以使其成为对数时间。

1 个答案:

答案 0 :(得分:1)

假设combine保证是引用透明 0 ,您可以通过将整个计算视为二叉树 1 <来编写对数时间版本/ SUP>。例如,假设将(together-copies-of * 4 3)称为二叉树(将together-copies-of缩写为t):

               (t * 4 3)
                   |
               ----*-----
              /          \
             /            \
       (t * 2 3)         (t * 2 3)
           |                 |
      -----*-             ---*----
     /       \           /        \
(t * 1 3) (t * 1 3) (t * 1 3) (t * 1 3)

这里的要点是,您不需要计算(t * 1 3)四次,而且您不需要计算(t * 2 3)两次;你只需要计算一次。如果我们确保只计算每行的计算次数,那么我们只需要每行执行O(1)次运算。由于二叉树中的行数在元素数量上是对数的,这意味着我们可以使用O(log n)算法。

相比之下,您当前的算法如下所示:

(t * 4 3)
    |
  3 *
     \
  (t * 3 3)
       |
     3 *
        \
     (t * 2 3)
         |
       3 *
          \
       (t * 1 3)
          |
        3 * 3

这是什么使你的程序(两者都是)线性的:它的结构是一条大线,所以它必然需要线性时间。

以下简单程序实现了二叉树的想法。

(define together-copies-of-log
  (lambda (combine quantity thing)
    (if (= quantity 1)
        thing
        (let ((child (together-copies-of-log combine (/ quantity 2) thing)))
          (combine child child)))))

由于我刚刚写了这篇文章来展示这个概念,所以它有一些不足之处:

  1. 如果quantity不是2的幂,则会失败。
  2. 它不是尾递归的。
  3. 修复这些是留给读者的练习。 :)


    一些澄清的评论:

    0:为什么combine需要引用透明?如果它不是,那么将两个调用combine更改为一个实际上可能会改变该值,因此它不会成为有效的转换。

    1:为什么是二叉树,而不是三元树或任何其他树?这是因为combine只有两个参数。如果您正在为不同的arity函数编写一个版本,那么树将具有相同的arity。