Clojure 将数字集添加到16
我有几组数字:Clojure 将数字集添加到16,clojure,permutation,Clojure,Permutation,我有几组数字: (#{7 1} #{3 5} #{6 3 2 5} #{0 7 1 8} #{0 4 8} #{7 1 3 5} #{6 2} #{0 3 5 8} #{4 3 5} #{4 6 2} #{0 6 2 8} #{4} #{0 8} #{7 1 6 2} #{7 1 4}) #{7 1} => [1 1 7 7] #{4 3 5} => [3 4 4 5] #{4} => [4 4 4 4] #{0 8} => [0
(#{7 1} #{3 5} #{6 3 2 5}
#{0 7 1 8} #{0 4 8} #{7 1 3 5}
#{6 2} #{0 3 5 8} #{4 3 5}
#{4 6 2} #{0 6 2 8} #{4} #{0 8}
#{7 1 6 2} #{7 1 4})
#{7 1} => [1 1 7 7]
#{4 3 5} => [3 4 4 5]
#{4} => [4 4 4 4]
#{0 8} => [0 0 8 8]
我希望将每一组向量都变成一个四位数的向量,这样所有向量的总和加起来就是16,它们只能来自于这组数字:
(#{7 1} #{3 5} #{6 3 2 5}
#{0 7 1 8} #{0 4 8} #{7 1 3 5}
#{6 2} #{0 3 5 8} #{4 3 5}
#{4 6 2} #{0 6 2 8} #{4} #{0 8}
#{7 1 6 2} #{7 1 4})
#{7 1} => [1 1 7 7]
#{4 3 5} => [3 4 4 5]
#{4} => [4 4 4 4]
#{0 8} => [0 0 8 8]
最后,向量必须包含集合中的所有数字。对于任意向量长度,解决这个问题将非常好:)
clojure代码是如何编写的。假设每个集合只需要一个解决方案,并且希望解决方案按照您的示例按升序排列,这就是我提出的。1-4个数字集合的组合并不多,因此我最初分解问题的方法是查看可能的解决方案的模式
(def x #{3 5})
(def g 16)
(def y {1 [[0 0 0 0]]
2 [[0 0 0 1][0 0 1 1][0 1 1 1]]
3 [[0 0 1 2][0 1 1 2][0 1 2 2]]
4 [[0 1 2 3]]})
此地图的此键指示正在评估的集合x
的大小。这些值是将集合排序为向量后索引到集合的可能排列。现在,我们可以根据集合的大小选择排列,并计算每个排列的值,一旦达到目标就停止:
(filter #(= g (apply + %))
(for [p (y (count x))]
(mapv #((into [] (sort x)) %) p)))
排列上方映射的每个键的值形成一个模式:第一个索引始终为0,最后一个索引始终为设置的大小-1,所有值要么与左侧的值相同,要么高于左侧的值。因此,上述地图可概括为:
(defn y2 [m s]
(map (fn [c] (reduce #(conj %1 (+ %2 (peek %1))) [0] c))
(clojure.math.combinatorics/permutations
(mapv #(if (>= % (dec s)) 0 1) (range (dec m))))))
(def y (partial y2 4))
过滤器现在可用于
s
以内的任意数量的设置项。当输入集被排序时,可以优化搜索以找到正确的(或否)通过对log2n搜索时间的可能解决方案的排列进行二进制搜索来解决问题。假设每个集合只需要一个解决方案,并且希望解决方案按照示例升序排列。这就是我提出的。1-4个数字集合的组合并不多,因此我最初分解问题的方法是查看可能的解决方案的模式
(def x #{3 5})
(def g 16)
(def y {1 [[0 0 0 0]]
2 [[0 0 0 1][0 0 1 1][0 1 1 1]]
3 [[0 0 1 2][0 1 1 2][0 1 2 2]]
4 [[0 1 2 3]]})
此地图的此键指示正在评估的集合x
的大小。这些值是将集合排序为向量后索引到集合的可能排列。现在,我们可以根据集合的大小选择排列,并计算每个排列的值,一旦达到目标就停止:
(filter #(= g (apply + %))
(for [p (y (count x))]
(mapv #((into [] (sort x)) %) p)))
排列上方映射的每个键的值形成一个模式:第一个索引始终为0,最后一个索引始终为设置的大小-1,所有值要么与左侧的值相同,要么高于左侧的值。因此,上述地图可概括为:
(defn y2 [m s]
(map (fn [c] (reduce #(conj %1 (+ %2 (peek %1))) [0] c))
(clojure.math.combinatorics/permutations
(mapv #(if (>= % (dec s)) 0 1) (range (dec m))))))
(def y (partial y2 4))
过滤器现在可用于s
以内的任意数量的设置项。当输入集被排序时,可以通过对log2n搜索时间的可能解的排列进行二进制搜索来优化搜索,以找到正确(或否)的解。使用小集合和最初规定的输出长度4
这很容易通过简单的搜索处理
(defn bag-sum [s n]
(for [a s, b s, c s, d s
:let [v [a b c d]]
:when (= n (apply + v))
:when (= (set v) s)]
v))
假设16是固定的,所有数字都是非负的 即使没有设置约束,搜索空间也很小
(require '[clojure.math.combinatorics :refer [partition]])
(count (partitions (repeat 16 1))) ;=> 231
因此,一个简单的解决方案也是非常实用的。我们将生产各种长度的解决方案,可以根据需要进一步过滤。如果输入集中有一个零,它可以填充任何解决方案
(defn bag-sum16 [s]
(for [p (partitions (repeat 16 1))
:let [v (mapv (partial apply +) p)]
:when (= (set v) s)]
v))
第一个示例有两个解决方案-长度4和长度10
(bag-sum16 #{7 1}) ;=> ([7 7 1 1] [7 1 1 1 1 1 1 1 1 1])
(bag-sum16 #{3 5}) ;=> ([5 5 3 3])
(bag-sum16 #{3 4 5}) ;=> ([5 4 4 3])
使用core.logic finite domains使用任意但指定的域集
s
、输出长度m
、和n
这仍然是相当幼稚的,但是当超过目标和时会修剪搜索树。我是core.logic的新手,所以这更多的是一个练习的机会,而不是试图最好地表达问题。这在小空间上的性能比上面的原始解决方案差,但在某些中等大小的情况下可以进行计算
(defn bag-sum-logic [s m n]
(let [m* (- m (count s))
n* (- n (apply + s))
nums (vec (repeatedly m* lvar))
sums (concat [0] (repeatedly (dec m*) lvar) [n*])
dom (apply fd/domain (sort s))
rng (fd/interval n*)
sol (run 1 [q]
(== q nums)
(everyg #(fd/in % dom) nums)
(everyg #(fd/in % rng) sums)
(everyg #(apply fd/+ %)
(map cons nums (partition 2 1 sums))))]
(when (seq sol) (sort (concat s (first sol))))))
一般情况下更好的算法? 该问题是一个via矩阵单模行缩减问题,即在一列中执行欧几里德算法,同时将整个基行用于骑乘 例如,在#{35}和sum 16的情况下,您需要解方程
3x + 5y = 16
受制于附加约束,即x>0
、y>0
和x+y=4
(您的示例)
矩阵与约化步骤
[[3 1 0] -> [[3 1 0] -> [[1 2 -1] -> [[1 2 -1]
[5 0 1]] [2 -1 1]] [2 -1 1]] [0 -5 3]]
所以3和5的GCD是1,除以16。因此,在约束之前存在(无限多个)解
x = 16 * 2 - 5k
y = 16 * -1 + 3k
因为我们需要x+y=4
,4=16-2k
,因此k=6
,所以
x = 2
y = 2
我们需要2份3号和2份5号
这以同样的方式推广到2个以上的变量。但是,对于2个变量,解的长度完全限制了单个自由变量,如上图所示,超过3个变量可能未指定
求解线性丢番图方程可以在多项式时间内完成。然而,一旦你添加了边界(0,m)
,找到一个解决方案就变成了NP完全问题,尽管快速阅读一下,你会发现有相当容易处理的方法。使用小集合和最初规定的输出长度4
这很容易通过简单的搜索处理
(defn bag-sum [s n]
(for [a s, b s, c s, d s
:let [v [a b c d]]
:when (= n (apply + v))
:when (= (set v) s)]
v))
假设16是固定的,所有数字都是非负的 即使没有设置约束,搜索空间也很小
(require '[clojure.math.combinatorics :refer [partition]])
(count (partitions (repeat 16 1))) ;=> 231
因此,一个简单的解决方案也是非常实用的。我们将生产各种长度的解决方案,可以根据需要进一步过滤。如果输入集中有一个零,它可以填充任何解决方案
(defn bag-sum16 [s]
(for [p (partitions (repeat 16 1))
:let [v (mapv (partial apply +) p)]
:when (= (set v) s)]
v))
第一个示例有两个解决方案-长度4和长度10
(bag-sum16 #{7 1}) ;=> ([7 7 1 1] [7 1 1 1 1 1 1 1 1 1])
(bag-sum16 #{3 5}) ;=> ([5 5 3 3])
(bag-sum16 #{3 4 5}) ;=> ([5 4 4 3])
使用core.logic finite domains使用任意但指定的域集
s
、输出长度m
、和n
这仍然是相当幼稚的,但是当超过目标和时会修剪搜索树。我是core.logic的新手,所以这更多的是一个练习的机会,而不是试图最好地表达问题。这