【问题标题】:(Lisp) Can i make this more efficient?(Lisp)我可以提高效率吗?
【发布时间】:2015-04-01 23:25:14
【问题描述】:

计算更改样式问题,我用 lisp 编写了这个递归,我想知道是否有人有任何技巧可以提高效率?如果数字太大,它就会开始中断,大约需要 3 分钟来计算相当于 10 英镑的不同组合! 即使指出我正确的方向也很好,谢谢!

(defun dollars (amount &optional (coins '(5 10 20 50 100 200 500 1000 2000 5000 10000)))
  (cond ((= amount 0) 1)
        ((or (< amount 0) (= (length coins) 0) (> amount 30000)) 0)          
        ((zerop (mod amount 5))
         (+ (dollars (- amount (first coins)) coins)
            (dollars amount (rest coins))))))

【问题讨论】:

  • 一个非常简单的加速:用(endp coins)替换(= (length coins) 0)。必须遍历整个列表来计算它的长度,而判断列表是否为空真的很快。
  • 太好了!没有意识到这会有很大的不同:)
  • 另一个简单的加速:反转硬币列表!然后,当金额小于下一个更高的硬币时,您将为每个剩余的硬币节省两次调用美元。您也可以通过将(&lt; amount 0) 替换为(&lt; amount (first coins)) 来实现几乎相同的效果。

标签: performance recursion lisp common-lisp


【解决方案1】:

让我们看一个类似的问题,计算斐波那契数。

(defun fib (n)
  (if (<= 0 n 1)
      n
      (+ (fib (- n 1))
         (fib (- n 2)))))

随着 n 变大,计算较小斐波那契数的次数呈指数增长。在计算 F(10) 时,总共计算了八次 F(5)。在计算 F(15) 时,F(5) 总共计算了 89 次不同的时间!我们可以通过在计算后保存一个值来解决这个问题。然后,当我们需要确定一个我们已经计算过的值时,只需查找它即可。下面的代码就是这样做的。

(defparameter *calculated* (make-hash-table))

(defun fib (n)
  (or (gethash n *calculated*)
      (setf (gethash n *calculated*)
            (if (<= 0 n 1)
                n
                (+ (fib (- n 1))
                   (fib (- n 2)))))))

当给定一个数字进行计算时,如果代码已经计算过了,则查找它的值,否则代码将计算该值并存储。现在当我们计算 F(15) 时,F(5) 只计算一次,因为每隔一次它的值只是在哈希表中查找。由于每个斐波那契数(从 F(0) 到 F(15))只计算一次,因此这会显着加快速度。

这是一种称为“dynamic programming”的技术,其中较大的值是从较小的值计算出来的,而较小的值会一遍又一遍地计算。简单的解决方案是在计算时只存储一个值并检查是否已经计算了一个值。如何将这种技术应用到自己的代码中应该很简单。

【讨论】:

  • 非常感谢!我已经在互联网上搜索了很久,但是这一切都很难理解,而这完全有道理!
  • 嗨,我已经尝试过实现它,但无论我怎么做,它都会返回错误的结果,使数字比它应该的大很多?
  • @RebeccaWilde 你确定这些值不正确吗?它们确实呈指数增长,因此您应该期望它们非常大。
  • @RebeccaWilde 您是否在密钥中同时使用金额和硬币进行记忆?否则,例如(dollars 10 '(5 10)) > (dollars 10 '(5)) 将导致结果错误。使用 (make-hash-table :test #'equal)(gethash (cons amount coins) *results*) 将得到与不使用 memoization 相同的结果。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-06-18
  • 2016-07-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多