没有重复元素的列表元素的所有组合

时间:2017-06-07 01:59:05

标签: mapping common-lisp combinations

Alexandria有一个函数map-product,它接受​​任意数量的列表参数,并按顺序生成元素的所有组合,每个元素组合一个。例如:

(alexandria:map-product 'list '(1 2) '(3 4) '(5 6))
=> ((1 3 5) (1 3 6) (1 4 5) (1 4 6) (2 3 5) (2 3 6) (2 4 5) (2 4 6))

当参数中存在重复元素时,结果组合也将包含一些重复元素:

(alexandria:map-product 'list '(1 2) '(3 4) '(5 1))
=> ((1 3 5) (1 3 1) (1 4 5) (1 4 1) (2 3 5) (2 3 1) (2 4 5) (2 4 1))

其中(1 3 1)和(1 4 1)包含重复项。

我想从结果中删除包含重复项的所有此类列表。我目前的解决方案是简单地做:

(delete-if-not #'alexandria:setp result)

但这需要过多的后处理,特别是因为产生的组合数量通常为数百个。一个更好的解决方案是编写像map-product这样的函数,它首先不会生成重复项。

zck在Lisp: How to get all possible combinations of the elements from lists contained on a list?的另一篇文章提供的函数大致相当于map-product,似乎可以修改它以在内部删除重复项:

(defun combinations (&rest lists)
  (if (car lists)
      (mapcan (lambda (inner-val)
                (mapcar (lambda (outer-val)
                          (cons outer-val
                                inner-val))
                        (car lists)))
              (apply #'combinations (cdr lists)))
    (list nil)))

然而,对我来说,如何插入重复测试并不明显。此外,一个简单的计时运行似乎表明这个函数比alexandria:map-product慢大约16倍。获得此功能的更快版本是否可行,但没有重复组合?

1 个答案:

答案 0 :(得分:1)

你可能需要检查一下这是否正确,但它应该给你一个想法:

CL-USER 40 > (defun combinations-1 (lists)
               (if (car lists)
                   (mapcan (lambda (inner-val)
                             (mapcan (lambda (outer-val)
                                       (unless (member outer-val inner-val)
                                         (list (cons outer-val inner-val))))
                                     (car lists)))
                           (combinations-1 (cdr lists)))
                 (list nil)))
COMBINATIONS-1

CL-USER 41 > (combinations-1 '((3 2) (1 2) (5 1)))
((3 1 5) (2 1 5) (3 2 5) (3 2 1))

另一个MAPCAN而不是MAPCAR过滤NIL。为此,我们需要返回列表,因此添加了LIST调用。我们只在列表中添加内容,如果它不是成员,否则使用空列表。

请注意,我还删除了& rest list / apply pattern。

问:所有重复的子列表是否都减少到NIL,以便通过MAPCAN删除它们?

相关问题