【问题标题】:Generating a list of all possible combinations of true or false for n given variables in LISP为 LISP 中的 n 个给定变量生成真或假的所有可能组合的列表
【发布时间】:2016-10-26 10:53:02
【问题描述】:

我想定义一个函数,它接受输入“n”(变量的数量)并返回所有可能的真值。在这里,我表示变量 i (1

例如:

(generate-values 2)

应该返回:

((2 1)(2 -1)(-2 1)(-2 -1))

(generate-values 3)

应该返回:

((3 2 1)(3 2 -1)(3 -2 1)(3 -2 -1)(-3 2 1)(-3 2 -1)(-3 -2 1)(-3 -2 -1))

这是我的错误尝试:

(defun generate-values (n)
  (cond
   ((equal n 0) nil)
   (t (list (cons n (generate-values (- n 1)))
            (cons (- 0 n) (generate-values (- n 1)))))))

我知道为什么这是不正确的,但我无法找到生成(3 2 1) 然后转到(3 2 -1) 的方法。我的程序输出:

 ((3 (2 (1) (-1)) (-2 (1) (-1))) (-3 (2 (1) (-1)) (-2 (1) (-1))))

对这个问题的任何帮助都将不胜感激!谢谢!

【问题讨论】:

    标签: recursion lisp common-lisp


    【解决方案1】:

    以最简单的方式解决这个问题可能是最简单的,然后再想办法让它变得更简单或更高效。

    如果您以递归方式执行此操作,请务必考虑基本案例是什么。这里一个合理的基本情况可能是 n = 0。该函数总是应该返回一个列表列表。在 n = 0 的情况下,没有“变量”,因此结果必须是空列表的列表:(())

    如果 n 是其他任何东西,请考虑函数为 n-1 返回什么。它是n-1“变量”上所有组合的列表。您需要做的就是在每个前面加上 n,然后在每个前面加上 -n,然后确保最终得到所有这些的列表.

    直接编码,我们最终会得到这样的结果:

    (defun table (n)
      (if (zerop n)
          '(())
          (let* ((table (table (1- n)))
                 (plus-pos-n (mapcar (lambda (subtable)
                                       (list* n subtable))
                                     table))
                 (plus-neg-n (mapcar (lambda (subtable)
                                       (list* (- n) subtable))
                                     table)))
            (nconc plus-pos-n plus-neg-n))))
    
    CL-USER> (table 3)
    ((3 2 1) (3 2 -1) (3 -2 1) (3 -2 -1) (-3 2 1) (-3 2 -1) (-3 -2 1) (-3 -2 -1))
    

    现在,让我们看看您当前的实现有何不同之处,当然,它必须 使用完全相同的算法。

    (defun generate-values (n)
      (cond
        ((equal n 0)
         nil)
        (t
         (list (cons n
                       (generate-values (- n 1)))
                 (cons (- 0 n)
                       (generate-values (- n 1)))))))
    

    从风格上讲,由于只有两个分支,我更喜欢 ifcond ,但这不是问题。在攻击基本情况之前,让我们看看递归情况,当 n ≠ 0。首先,您调用 generate-values 两次;调用一次并保存结果会更有效。如果您使用较大的 n 值调用此函数,这可能会在以后变得很重要,但这不会使函数不正确。但请记住 generate-values 返回什么;它返回不同组合的列表。这意味着您对 (cons n (generate-values ...)) 的调用将返回一个列表,其第一个元素是 n,其余元素是 的组合>n-1。例如,您正在执行以下操作:

    CL-USER> (table 1)
    ((1) (-1))
    CL-USER> (cons 2 (table 1))
    (2 (1) (-1))
    

    但这不是你想要的。您确实想将 n 添加到每个列表中:

    CL-USER> (mapcar (lambda (x)
                       (cons 2 x))
                     (table 1))
    ((2 1) (2 -1))
    

    这就是递归情况下的问题。基本情况也有问题。在递归情况下,您希望将 n-n 添加到 n-1 情况下的每个子列表中。那么当你有 n = 1 时会发生什么?你想得到 (cons 1 '())(cons -1 '())。但是由于 cons 的第二个参数将是 (generate-values 0) 结果中的每个列表,因此您真的需要(generate-values 0) 返回的列表中有一些东西。需要有什么?空列表需要在那里。所以基本情况需要返回(()),而不是()。因此,在进行这些更改后,您的代码将是:

    (defun generate-values (n)
      (cond
        ((equal n 0)
         '(()))
        (t
         (list (mapcar (lambda (x)
                         (cons n x))
                       (generate-values (- n 1)))
               (mapcar (lambda (x)
                         (cons (- 0 n) x))
                       (generate-values (- n 1)))))))
    
    CL-USER> (generate-values 3)
    (((3 (2 (1)) (2 (-1))) (3 (-2 (1)) (-2 (-1))))
     ((-3 (2 (1)) (2 (-1))) (-3 (-2 (1)) (-2 (-1)))))
    

    这更接近了,但仍然不太正确。在递归情况下还有另一个。您最终会生成开头具有 n 的值(它们的列表),以及开头具有 -n 的值(它们的列表),但是你使用 list 来组合它们。这将返回一个包含两个值的列表。相反,您需要一个列表,其中包含每个列表中的值。您想将它们与 append 结合起来(或者,由于所有结构都是新生成的,您可以使用 nconc):

    (defun generate-values (n)
      (cond
        ((equal n 0)
         '(()))
        (t
         (append (mapcar (lambda (x)
                           (cons n x))
                         (generate-values (- n 1)))
                 (mapcar (lambda (x)
                           (cons (- 0 n) x))
                         (generate-values (- n 1)))))))
    
    CL-USER> (generate-values 3)
    ((3 2 1) (3 2 -1) (3 -2 1) (3 -2 -1) (-3 2 1) (-3 2 -1) (-3 -2 1) (-3 -2 -1))
    

    这个最终实现与我一开始并不完全一样,但在算法方面基本相同。差异主要是风格上的,但也存在一些效率问题。使用 nconc 而不是 append 会节省一些内存,并且缓存递归调用的结果而不是重新计算它确实会很好。不影响正确性的风格问题可能是使用 if 而不是 cond,使用 list* 而不是 cons (表示我们正在使用列表,而不是 cons 单元格树),很高兴注意到您不必这样做 (- 0 n), -带有单个参数的 strong> 返回参数的否定。也就是说,(-n) = -n

    【讨论】:

      猜你喜欢
      • 2017-02-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-24
      • 2015-05-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多