Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Clojure 查找小于或等于给定整数n且与给定整数s之和的不同正整数i、j和k的所有有序三元组_Clojure_Sicp - Fatal编程技术网

Clojure 查找小于或等于给定整数n且与给定整数s之和的不同正整数i、j和k的所有有序三元组

Clojure 查找小于或等于给定整数n且与给定整数s之和的不同正整数i、j和k的所有有序三元组,clojure,sicp,Clojure,Sicp,这是SICP中的练习2.41 我自己写过这个天真的版本: (defn sum-three [n s] (for [i (range n) j (range n) k (range n) :when (and (= s (+ i j k)) (< 1 k j i n))] [i j k])) (定义和三[ns] (适用于[i(范围n) j(范围n) k(范围n) :何时(及(=s(+i j k

这是SICP中的练习2.41 我自己写过这个天真的版本:

(defn sum-three [n s]
  (for [i (range n)
        j (range n)
        k (range n)
        :when (and (= s (+ i j k))
                   (< 1 k j i n))]
    [i j k]))
(定义和三[ns]
(适用于[i(范围n)
j(范围n)
k(范围n)
:何时(及(=s(+i j k))
(<1 k j i n))]
[i j k]))
问题是:这在clojure中被认为是惯用的吗?我如何优化这段代码?因为计算
(三个500之和)需要永远的时间

另外,我如何让这个函数接受一个额外的参数来指定整数的个数来计算和?因此,它应该处理更一般的情况,如二之和、四之和或五之和等,而不是三之和

我想这不能通过使用
for
循环来实现?不确定如何动态添加i j k绑定

(更新:完全优化的版本是底部的
sum-c-opt

我认为这是惯用的,如果不是保持惯用的最快方法的话。好吧,当已知输入是数字时,使用
==
代替
=
可能更为惯用(注意,这些在数字上并不完全相同;但在这里并不重要。)

作为第一个优化过程,您可以从更高的范围开始,并用特定于数字的
=
替换
=

(defn sum-three [n s]
  (for [k (range n)
        j (range (inc k) n)
        i (range (inc j) n)
        :when (== s (+ i j k))]
    [i j k]))
(更改了绑定的顺序,因为您希望最后一个最小的数字。)

至于将整数数作为参数,有一种方法:

(defn sum-c [c n s]
  (letfn [(go [c n s b]
            (if (zero? c)
            [[]]
            (for [i  (range b n)
                  is (go (dec c) n (- s i) (inc i))
                  :when (== s (apply + i is))]
              (conj is i))))]
    (go c n s 0)))

;; from the REPL:
user=> (sum-c 3 6 10)
([5 4 1] [5 3 2])
user=> (sum-c 3 7 10)
([6 4 0] [6 3 1] [5 4 1] [5 3 2])
更新:反而破坏了使用它的练习,但提供了一个专门为解决此问题而定制的
组合
功能:

(require '[clojure.math.combinatorics :as c])

(c/combinations (range 10) 3)
;=> all combinations of 3 distinct numbers less than 10;
;   will be returned as lists, but in fact will also be distinct
;   as sets, so no (0 1 2) / (2 1 0) "duplicates modulo ordering";
;   it also so happens that the individual lists will maintain the
;   relative ordering of elements from the input, although the docs
;   don't guarantee this
适当地过滤输出

进一步的更新:通过以上工作的方式思考
sum-c
,可以提供进一步的优化思路。
sum-c
中的内部
go
函数的要点是生成一系列的元组,将它们相加到某个目标值(其初始目标减去
中当前迭代时的
i
值,以便理解);然而,我们仍然验证从对
go
的递归调用返回的元组的总和,就好像我们不确定它们是否真的完成了它们的工作一样

相反,我们可以通过构造确保生成的元组是正确的:

这个版本以列表的形式返回元组,以便在保持代码结构的同时保持预期的结果顺序,而这种方法是自然的。您可以使用
map-vec
过程将它们转换为向量

对于较小的参数值,这实际上比
sum-c
慢,但对于较大的值,这要快得多:

user> (time (last (sum-c-opt 3 500 500)))
"Elapsed time: 88.110716 msecs"
(168 167 165)
user> (time (last (sum-c 3 500 500)))
"Elapsed time: 13792.312323 msecs"
[168 167 165]
为了进一步保证它做同样的事情(除了归纳证明两种情况下的正确性之外):

(更新:完全优化的版本在底部为
sum-c-opt

我认为这是惯用的,如果不是保持惯用的最快方法的话。好吧,当已知输入是数字时,使用
==
代替
=
可能更为惯用(注意,这些在数字上并不完全相同;但在这里并不重要。)

作为第一个优化过程,您可以从更高的范围开始,并用特定于数字的
=
替换
=

(defn sum-three [n s]
  (for [k (range n)
        j (range (inc k) n)
        i (range (inc j) n)
        :when (== s (+ i j k))]
    [i j k]))
(更改了绑定的顺序,因为您希望最后一个最小的数字。)

至于将整数数作为参数,有一种方法:

(defn sum-c [c n s]
  (letfn [(go [c n s b]
            (if (zero? c)
            [[]]
            (for [i  (range b n)
                  is (go (dec c) n (- s i) (inc i))
                  :when (== s (apply + i is))]
              (conj is i))))]
    (go c n s 0)))

;; from the REPL:
user=> (sum-c 3 6 10)
([5 4 1] [5 3 2])
user=> (sum-c 3 7 10)
([6 4 0] [6 3 1] [5 4 1] [5 3 2])
更新:反而破坏了使用它的练习,但提供了一个专门为解决此问题而定制的
组合
功能:

(require '[clojure.math.combinatorics :as c])

(c/combinations (range 10) 3)
;=> all combinations of 3 distinct numbers less than 10;
;   will be returned as lists, but in fact will also be distinct
;   as sets, so no (0 1 2) / (2 1 0) "duplicates modulo ordering";
;   it also so happens that the individual lists will maintain the
;   relative ordering of elements from the input, although the docs
;   don't guarantee this
适当地过滤输出

进一步的更新:通过以上工作的方式思考
sum-c
,可以提供进一步的优化思路。
sum-c
中的内部
go
函数的要点是生成一系列的元组,将它们相加到某个目标值(其初始目标减去
中当前迭代时的
i
值,以便理解);然而,我们仍然验证从对
go
的递归调用返回的元组的总和,就好像我们不确定它们是否真的完成了它们的工作一样

相反,我们可以通过构造确保生成的元组是正确的:

这个版本以列表的形式返回元组,以便在保持代码结构的同时保持预期的结果顺序,而这种方法是自然的。您可以使用
map-vec
过程将它们转换为向量

对于较小的参数值,这实际上比
sum-c
慢,但对于较大的值,这要快得多:

user> (time (last (sum-c-opt 3 500 500)))
"Elapsed time: 88.110716 msecs"
(168 167 165)
user> (time (last (sum-c 3 500 500)))
"Elapsed time: 13792.312323 msecs"
[168 167 165]
为了进一步保证它做同样的事情(除了归纳证明两种情况下的正确性之外):


for是一个宏,因此很难扩展您的惯用回答来涵盖一般情况。幸运的是,clojure.math.combinationics提供了笛卡尔积函数,该函数将生成数字集的所有组合。这减少了过滤组合的问题:

(ns hello.core
  (:require [clojure.math.combinatorics :as combo]))

(defn sum-three [n s i]
  (filter #(= s (reduce + %))
          (apply combo/cartesian-product (repeat i (range 1 (inc n)))))) 

hello.core> (sum-three 7 10 3)
((1 2 7) (1 3 6) (1 4 5) (1 5 4) (1 6 3) (1 7 2) (2 1 7) 
 (2 2 6) (2 3 5) (2 4 4) (2 5 3) (2 6 2) (2 7 1) (3 1 6) 
 (3 2 5) (3 3 4) (3 4 3) (3 5 2) (3 6 1) (4 1 5) (4 2 4) 
 (4 3 3) (4 4 2) (4 5 1) (5 1 4) (5 2 3) (5 3 2) (5 4 1) 
 (6 1 3) (6 2 2) (6 3 1) (7 1 2) (7 2 1))

假设答案中的顺序很重要,因为这是一个宏,所以很难扩展您的惯用答案来涵盖一般情况。幸运的是,clojure.math.combinationics提供了笛卡尔积函数,该函数将生成数字集的所有组合。这减少了过滤组合的问题:

(ns hello.core
  (:require [clojure.math.combinatorics :as combo]))

(defn sum-three [n s i]
  (filter #(= s (reduce + %))
          (apply combo/cartesian-product (repeat i (range 1 (inc n)))))) 

hello.core> (sum-three 7 10 3)
((1 2 7) (1 3 6) (1 4 5) (1 5 4) (1 6 3) (1 7 2) (2 1 7) 
 (2 2 6) (2 3 5) (2 4 4) (2 5 3) (2 6 2) (2 7 1) (3 1 6) 
 (3 2 5) (3 3 4) (3 4 3) (3 5 2) (3 6 1) (4 1 5) (4 2 4) 
 (4 3 3) (4 4 2) (4 5 1) (5 1 4) (5 2 3) (5 3 2) (5 4 1) 
 (6 1 3) (6 2 2) (6 3 1) (7 1 2) (7 2 1))

假设答案中的顺序很重要,要使现有代码参数化,您可以使用
reduce
。此代码显示了一种模式,您可以使用该模式来参数化
宏用法的
案例数

不使用
宏的
代码(仅使用函数)将是:

(defn sum-three [n s]
  (mapcat (fn [i]
            (mapcat (fn [j]
                      (filter (fn [[i j k]]
                                (and (= s (+ i j k))
                                     (< 1 k j i n)))
                              (map (fn [k] [i j k]) (range n))))
                    (range n)))
          (range n)))
(定义和三[ns]
(mapcat(fn[i]