【问题标题】:(Lisp) Counting Change code(Lisp) 计数更改代码
【发布时间】:2015-03-30 05:04:21
【问题描述】:

我最近开始学习 lisp,我认为一个有趣的问题是计数更改算法,它已经尝试了很多次,但是我发现很难弄清楚如何解决这个问题,我想我会尝试使用循环,但它只是不工作。我将把我的两个无用的尝试放在下面,但如果有人对我应该如何从 lisp 的角度考虑这个问题有任何建议,我将不胜感激。 我也更喜欢使用递归解决方案。

我一直在这里查看有关计算变化的问题,但它们都倾向于面向对象,而我需要更实用的东西。 这不是家庭作业,只是私人学习!

(defun dollar (amount)   
(let ((l 0) (j 0) (array (make-array '(5 10 20 50 100 200 500 1000 2000 5000 100000))) (results (make-array 50 :initial-element nil))
        (do (l 10 (+ l 1))
       (do ((= j (aref array l)) amount (+ j 1))
         (+ (aref array j) (- (aref results j) (aref array l))))))
                   ))

(defun coin-change (amount coins)
(cond ((< amount 0) 0)
      ((= amount 5) 1)
      ((null coins) 0)
      (t (+ (make-change-with-coins (- amount (car coins)) coins)
            (make-change-with-coins amount (cdr coins)))))

)

样本输入为 (coin-change 20 '(5 10 20 50 100 200 500 1000 2000 5000 100000)) 将返回 4

【问题讨论】:

标签: loops recursion count lisp common-lisp


【解决方案1】:

标准格式有助于获得正确的代码结构。阅读某种关于新语言的文档会更有帮助。

这是你写的:

(defun dollar (amount)
  (let ((l 0)
        (j 0)
        (array (make-array '(5 10 20 50 100 200 500 1000 2000 5000 100000)))
        (results (make-array 50 :initial-element nil))
        (do (l 10 (+ l 1))
            (do ((= j (aref array l)) amount (+ j 1))
                (+ (aref array j) (- (aref results j) (aref array l))))))))

Dollar 没有正确理解语义。 Make-array 将维度作为第一个参数,您很可能希望将 do 表单作为 let 的主体。我会在这里使用矢量文字。

(defun dollar (amount)
  (let ((l 0)
        (j 0)
        (array #(5 10 20 50 100 200 500 1000 2000 5000 100000))
        (results (make-array 50 :initial-element nil)))
    (do (l 10 (+ l 1))
        (do ((= j (aref array l)) amount (+ j 1))
            (+ (aref array j) (- (aref results j) (aref array l)))))))

Do 首先获取一个绑定列表,然后是一个包含结束条件和返回表单的表单,最后是构成正文的表单。

(defun dollar (amount)
  (let ((l 0)
        (j 0)
        (array #(5 10 20 50 100 200 500 1000 2000 5000 100000))
        (results (make-array 50 :initial-element nil)))
    (do ((l 10 (+ l 1)))
        (#| end condition here |#
         #| some more forms
         that return something |#)
      (do ((= j (aref array l)) ; uh, so this binds the variable "=" to j
                                ; and updates it to (aref array l) on iteration
           amount    ; an empty binding, initially nil
           (+ j 1))  ; binds the variable "+" to j and updates it to 1
          (+           ; tries to evaluate the variable "+" as a form...
           (aref array j)   ; no effect
           (- (aref results j) (aref array l)))))))  ; would return this

我试图纠正外部do 的形状并注释内部do。如您所见,这根本没有意义。

(defun coin-change (amount coins)
  (cond ((< amount 0) 0)
        ((= amount 5) 1)
        ((null coins) 0)
        (t (+ (make-change-with-coins (- amount (car coins)) coins)
              (make-change-with-coins amount (cdr coins))))))

这看起来至少在语义上是正确的,但我不知道它应该如何工作(而且我不知道 make-change-with-coins 做了什么)。

我认为最好先阅读一本好的入门书籍(我喜欢 Practical Common Lisp)并仔细阅读 Common Lisp Hyperspec (CLHS)。

【讨论】:

  • 对不起,我已经更改了第二个名称!多么愚蠢,因为我有很多版本,但我忘了更改顶部的那个!
  • 非常感谢有关如何正确格式化所有内容的提示,我对 common lisp 比较陌生,所以欢迎任何提示!
【解决方案2】:

我不清楚第一个函数应该做什么。

第二个差不多了,这是固定版本:

(defun coin-change (amount coins)
  (cond
    ((< amount 0) 0)
    ((= amount 0) 1)
    ((= (length coins) 0) 0)
    (t (+ (coin-change (- amount (first coins)) coins)
          (coin-change amount (rest coins))))))

想法是:

  • 无法匹配负数(0 种方式)
  • 零只能以一种方式匹配(不给任何硬币)
  • 如果金额 >0 并且我们没有剩余硬币类型,那么没有办法
  • 否则,我们可以 A) 给出第一种硬币类型的一枚并计算我们可以匹配剩余部分的方式,B) 计算方式而不使用第一种硬币类型。答案是A+B

请注意,这将花费大量的计算时间,因为它没有利用一个重要的事实,即从某种硬币类型开始匹配一定数量的方法的数量并不取决于我们如何达到这个数量(即过去的历史)。 通过添加缓存,您可以获得更快的动态编程解决方案,因为每个计算只执行一次。

【讨论】:

  • 非常感谢,没有意识到我需要在第二个工作中进行更改,我肯定会继续努力!你有什么缓存技巧吗?还有第一个我是基于我在网上看到的用java编写的其他示例,但他们使用了循环,我试图转换它!说实话有点乱哈哈
猜你喜欢
  • 1970-01-01
  • 2021-04-06
  • 1970-01-01
  • 1970-01-01
  • 2010-10-07
  • 1970-01-01
  • 2015-11-13
  • 2013-12-03
相关资源
最近更新 更多