对于一个类项目,我正在实现Bron-Kerbosch algorithm来查找图中的最大派系。在其他人的帮助下,我已经解决了最后几个问题。
此链接(http://pastebin.com/2GUPZFnR)包含我当前实现的SSCCE,概述了该问题。我认为这个问题在于我使用disj
来查找两个列表的交集。我认为这是基于我使用" sanity"来调用BK-Call
时给出的错误。输入
fptests.core> (BK-Call (sanity1))
ClassCastException clojure.lang.PersistentList$EmptyList cannot be cast to clojure.lang.IPersistentSet clojure.core/disj (core.clj:1449)
此错误在我的Bron-Kerbosch函数本身中追溯到几行
(defn Bron-Kerbosch [r p x graph cliques]
(cond (and (empty? p) (empty? x)) (conj cliques r)
:else
(let [neigh (neighV graph (dec (count p)))]
(loop [loop-clq '(cliques)
loop-cnt '(dec (count p))
loop-p '(p)
loop-x '(x)]
(cond (= -1 loop-cnt) loop-clq
:else
(recur (conj loop-clq (Bron-Kerbosch (conj r loop-cnt) (conj p neigh) (disj x neigh)))
(dec loop-cnt)
(disj p loop-cnt)
(conj x loop-cnt)))))))
特别是在recur
形式的函数递归调用中。虽然我认为这个问题适用于conj
和disj
的所有用途。似乎conj
"工作"但不是我假设的方式。
fptests.core> (disj '(1) '(1 2 3))
ClassCastException clojure.lang.PersistentList cannot be cast to clojure.lang.IPersistentSet clojure.core/disj (core.clj:1449)
fptests.core> (conj '(1) '(2 3))
((2 3) 1)
我认为(conj '(1) '(2 3))
会返回(1 2 3)
而不是(1 (2 3))
。所以看起来我在函数中使用列表是个问题。有没有办法可以克服这个问题?
我必须想象有conj
和disj
这样的函数可以使用列表。我猜我的另一个选择,如果不是这样,那就是在算法中使用其他一些数据结构。什么是合适的?
答案 0 :(得分:3)
使用cons
,rest
和concat
。
user=> (cons 1 '(2 3))
(1 2 3)
user=> (cons '(1) '(2 3))
((1) 2 3)
user=> (cons 1 ())
(1)
user=> (rest '(1 2 3))
(2 3)
user=> (concat '(1) '(2 3))
(1 2 3)
答案 1 :(得分:2)
我的确建议使用更适合手头问题的数据结构,即一组。
您已经在图表表示中使用了Clojure哈希集((repeat n #{})
中的empty-graph
); Bron-Kerbosch阶跃函数的r
,p
和x
参数都是概念设置,因此将它们表示为Clojure也是有意义的。
通过选择表示,事情会变得更简单 - 可以使用clojure.set/intersection
来计算集合交集,disj
可以用于删除单个键等。
单独说明,在if
中使用cond
而不是Bron-Kerbosch
更为自然(该cond
中的loop
实际上只有两个分支) 。更重要的是,您希望从'(dec (count p))
- '
中的init表达式中删除引号(请注意{{1}}),举个例子,它是一个双元素列表,而不是数字。 (在Clojure中,函数名称包括大写字母也有点不寻常,但当然这纯粹是一种风格问题。)