过滤总和列表

时间:2013-07-03 11:29:56

标签: scheme racket

我需要编写一个函数,该函数使用整数列表lst并生成一个列表,该列表仅包含lst的唯一元素(没有特定顺序),它们是lst中任何两个其他元素的总和。

例如:

(sumfilt '(1 4 7 5 17 11)) => '(11 5)
(sumfilt '(5 4 7 5 9 1 10)) => '(5 9 10)

有人可以帮帮我吗?我也希望这能使它尽可能高效

2 个答案:

答案 0 :(得分:1)

我会给你一些提示来解决这个问题。第一种天真的方法是预先计算输入列表的每个大小为2的组合的和的列表,然后测试以查看哪些输入列表的元素属于和的列表。最后一步,删除重复项。

假设存在一个comb过程来计算lst列表的所有可能组合,并且给定大小m(查找它,或者自己实现它!),这里是一个非常问题的简短答案,实现上面解释的天真算法 - 对于1000个元素的列表来说应该足够好了:

(require srfi/26) ; I like to use `cut`, but `lambda` would serve just as well

(define (comb lst m)
  <???>) ; ToDo: generate all m-size combinations of lst

(define (sumfilt lst)
  (let ((sums (map (cut apply + <>) (comb lst 2))))
    (remove-duplicates (filter (cut member <> sums) lst))))

或等效地,使用cute仅计算一次预先计算的和数列表:

(define (sumfilt lst)
  (remove-duplicates
   (filter (cute member <> (map (cut apply + <>) (comb lst 2))) lst)))

更有效的方法将涉及subset sum问题的一些变化,通过动态编程解决。不过,这样的解决方案可以更精细地编写。无论哪种方式,不要忘记测试你的答案:

(sumfilt '(1 4 7 5 17 11)) 
=> '(5 11)

(sumfilt '(5 4 7 5 9 1 10)) 
=> '(5 9 10)

答案 1 :(得分:0)

由于你的问题被标记为 Racket ,我想建议一个使用Racket习语的解决方案:

根据奥斯卡的建议,

allsums 创建所有总和的列表。它基本上是一个嵌套循环,它处理所有必要的组合。为了不重复,我使用了一个 set ,它在添加时会以静默方式删除它们。稍后该集将转换为列表。

(define (allsums lst)
  (let* ([len (length lst)] [len-1 (sub1 len)])
    (set->list
     (for*/set ([i (in-range 0 len-1)]
                [j (in-range (add1 i) len)])
       (+ (list-ref lst i) (list-ref lst j))))))

sumfilt 现在根据 allsums 生成的列表过滤您的列表。同样,一个集合用于删除重复项:

(define (sumfilt lst)
  (let ([as (allsums lst)])
    (set->list
     (for/set ([i lst] #:when (member i as))
       i))))

> (sumfilt '(1 4 7 5 17 11))
'(5 11)
> (sumfilt '(5 4 7 5 9 1 10))
'(5 9 10)

但是如果你需要一个纯粹的Scheme解决方案,那么这段代码只会使用经典的形式和习语:

(define (allsums lst)
  (let loop1 ((lst lst) (res '()))
    (if (empty? lst)
        (reverse res)
        (let loop2 ((a (car lst)) (bs (cdr lst)) (res res))
          (if (empty? bs)
              (loop1 (cdr lst) res)
              (let ((s (+ a (car bs))))
                (loop2 
                 a 
                 (cdr bs) 
                 (if (member s res) res (cons s res)))))))))

(define (sumfilt lst)
  (let ((as (allsums lst)))
    (let loop ((lst lst) (res '()))
      (if (empty? lst)
          (reverse res)
          (let ((ca (car lst)))
            (loop 
             (cdr lst) 
             (if (and (member ca as) (not (member ca res)))
                 (cons ca res)
                 res)))))))

> (sumfilt '(1 4 7 5 17 11))
'(5 11)
> (sumfilt '(5 4 7 5 9 1 10))
'(5 9 10)