以下函数旨在返回集合列表中的所有超集 - 即删除列表中任何其他集合的子集。所以(超集'((1 2)(1 2 3)(3 2)(3 5)(5 3))) - > ((1 2 3)(3 5))。是否有更优雅/有效的方式来思考如何做到这一点?它似乎大致符合common-lisp“reduce”模型,除了两个非重叠集合简单地减少到它们自己。感谢您的任何简化或见解:
(defun supersets (sets)
(let ((remaining-sets sets))
(loop for set1 in sets
do (loop for set2 in (set-difference remaining-sets (list set1))
when (subsetp set1 set2)
do (setq remaining-sets
(set-difference remaining-sets (list set1)))
(return))
finally (return remaining-sets))))
答案 0 :(得分:3)
通过使用三个基本函数可以找到更有效的解决方案:
(defun supersets (sets)
(delete-duplicates (sort (copy-list sets) #'<= :key #'length) :test #'subsetp))
首先,列表按其元素的长度排序(copy-list
需要sort
,因为delete-duplicates
破坏性地修改了它的参数),所以我们现在有一个按长度递增的集合列表,所以每个集合只能是以下元素的子集。
然后我们应用:test
原语函数,重新定义副本不是作为一个等于另一个的元素,而是作为另一个的子集。这是通过关键参数delete-duplicates
完成的,该参数用于测试元素是否与另一个元素相等(即子集)。
请注意,我们可以使用remove-duplicates
代替非破坏性版本copy-list
,因为我们已使用:key
获取了列表的新副本。
<强>加成强>
上述功能通过解释来定义:
删除列表
中任何其他集的子集的任何集合
as:从列表中删除设置所有元素 X,使得另一个元素Y与X⊆Y。
最后,这是一个在更一般的比赛中使用的函数的版本。它是为序列定义的,不仅对于列表,还有两个可选的关键字参数:test
和(defun supersets (sets &key (key #'identity) (test #'eql))
"Given a sequence of lists representing sets, remove all sets that are contained
in others. The input sequence can be modified. The keyword parameter :key is a function
that, applied to each element of the input list, returns the set that must be considered
for the checking. The keyword parameter :test is a function used when testing
the equality for the elements of the set."
(delete-duplicates
(sort sets #'<= :key (lambda(x) (length (funcall key x))))
:key key :test #'(lambda (set1 set2) (subsetp set1 set2 :test test))))
(supersets
(copy-list '( ((1 2) (a)) ((1 2 3) (b)) ((3 2) (c)) ((4 5) (d)) ((5 4) (e)) ))
:key #'car) ; => (((4 5) (D)) ((1 2 3) (B)))
(supersets
#( ((1 2) (a)) ((1 2 3) (b)) ((3 2) (c)) ((4 5) (d)) ((5 4) (e)) )
:key #'car) ; => #(((5 4) (E)) ((1 2 3) (B)))
(supersets (copy-list '(("abc") ("bc" "abc"))) :test #'string=)
; => (("bc" "abc"))
,而可以破坏性地修改传递给它的序列(因此,使用注意,如果必须重复使用,则在传递之前复制序列,或者是常量列表。)
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
答案 1 :(得分:1)
如果问题的定义是(如帖子中所述),
删除列表
中任何其他集的子集的任何集合
这将是一个文字实现(我使用了来自alexandria的CURRY
。如果你不能拥有依赖项,你可以用常规LAMBDA
替换它:
(defun supersets (sets)
(remove-if (lambda (set)
(some (curry #'subsetp set)
(remove set sets)))
sets))
CL-USER> (supersets '((1 2) (1 2 3) (3 2) (3 5) (5 3)))
((1 2 3))
请注意,这也会从列表中删除(3 5)
,因为它是SUBSETP
的{{1}}。如果要保留它,可以定义(5 3)
的版本,该版本考虑相等的列表而不是子集。当然,那么你在结果中也会有SUBSETP
。我没有看到(5 3)
的任何合理解释,但不包括(3 5)
。
编辑:此处还有一个版本,其中包含(5 3)
和(3 5)
之一。
(5 3)
另一个编辑:更高效的版本。这样,它就不会使用(defun supersets (sets)
(let ((sets (remove-duplicates sets :test #'set-equal)))
(remove-if (lambda (set)
(some (curry #'subsetp set)
(remove set sets)))
sets)))
不断创建新列表。
REMOVE
答案 2 :(得分:0)
认为对这3个提供的功能中的每一个进行计时可能会很有趣。 (不是最好的,但只是在SBCL下获得5套10M次的给定列表的超集。)
davypough (me): 5.7 sec 4.8B cons
Renzo: 3.1 800M
jkiiski 5.0 2.2B
(注意:更改前2&#34;将jkiiski中的&#34; s删除为&#34;删除&#34; s。)
如果您不介意后续问题,那么如何估计这些序列函数的计算复杂性(如果没有深入到汇编代码中,或者运行增加N的实验并试图辨别曲线的形状)?例如,我的猜测是原始的删除重复是n平方?
答案 3 :(得分:0)
@Renzo。惊人。但是,对于我的应用程序,我需要将您的函数概括为包括:key和:test参数。例如,(超集'(((1 2)(a))((1 2 3)(b))((3 2)(c))((4 5)(d))((5 4)(e ))):key#'car:test#'equal) - &gt; (((1 2 3)(b))((4 5)(d))),因为数字实际上是列表,而顶级设置项是列表对。我的尝试不起作用。你能明白为什么吗?
(defun supersets (sets &key (key #'identity) (test #'eql))
(delete-duplicates
(sort (copy-list sets)
#'<= :key #'(lambda (set)
(length (funcall key set))))
:test #'(lambda (set1 set2)
(subsetp set1 set2 :key key :test test))))
(ps:另外,我注意到如果我只是在“let”中重新绑定输入“sets”参数(避免使用“copy-list”),原始示例中10M执行的时间减少到仅1秒这是一般原则,还是我偏离基础?谢谢。