假设Scheme中有任何给定的列表。此列表为‘(2 3 4)
我想查找此列表的所有可能分区。这意味着一个分区,其中列表被分成两个子集,这样列表的每个元素都必须在一个或另一个子集中,但不能同时在两个子集中,并且不能将任何元素排除在分割之外。
因此,给定列表‘(2 3 4)
,我想找到所有这些可能的分区。这些分区如下:{2,3}和{4},{2,4}和{3},最终可能的分区为{3,4}和{2}。
我希望能够以递归方式查找给出Scheme中列表的所有分区,但我对如何执行此操作没有任何想法。如果有人能为我提供代码或伪代码将帮助我!
我相信我必须使用lambda
作为递归函数。
答案 0 :(得分:1)
我在my blog讨论了几种不同类型的分区,但不是特定的分区。例如,假设整数分区是与给定整数相加的所有正整数集的集合。例如,4的分区是集合((1 1 1 1)(1 1 2)(1 3)(2 2)(4))。
构建分区的过程是递归的。有一个0的分区,空集()。单个分区为1,即集合(1)。有两个分区2,集合(1 1)和(2)。有三个分区3,集合(1 1 1),(1 2)和(3)。有4个分区,4个集合(1 1 1 1),(1 1 2),(1 3),(2 2)和(4)。存在5个分区,即集合(1 1 1 1 1),(1 1 1 2),(1 2 2),(1 1 3),(1 4),(2 3)和(5)。等等。在每种情况下,通过将小于或等于所需整数 n 的每个整数 x 添加到由分区形成的所有集合来确定下一个更大的分区集。 n - x ,消除任何重复。以下是我如何实现这一点:
Petite Chez Scheme Version 8.4
Copyright (c) 1985-2011 Cadence Research Systems
> (define (set-cons x xs)
(if (member x xs) xs
(cons x xs)))
> (define (parts n)
(if (zero? n) (list (list))
(let x-loop ((x 1) (xs (list)))
(if (= x n) (cons (list n) xs)
(let y-loop ((yss (parts (- n x))) (xs xs))
(if (null? yss) (x-loop (+ x 1) xs)
(y-loop (cdr yss)
(set-cons (sort < (cons x (car yss)))
xs))))))))
> (parts 6)
((6) (3 3) (2 2 2) (2 4) (1 1 4) (1 1 2 2) (1 1 1 1 2)
(1 1 1 1 1 1) (1 1 1 3) (1 2 3) (1 5))
我不会为你解决你的功课,但你的解决方案与上面给出的解决方案类似。您需要以递归方式陈述算法,然后编写代码来实现该算法。您的递归将是这样的:对于集合中的每个项目,将项目添加到集合中其余项目的每个分区,从而消除重复。
那会让你开始。如果您有具体问题,请回到此处获取更多帮助。
编辑:这是我的解决方案。我会让你弄清楚它是如何运作的。
(define range (case-lambda ; start, start+step, ..., start+step<stop
((stop) (range 0 stop (if (negative? stop) -1 1)))
((start stop) (range start stop (if (< start stop) 1 -1)))
((start stop step) (let ((le? (if (negative? step) >= <=)))
(let loop ((x start) (xs (list)))
(if (le? stop x) (reverse xs) (loop (+ x step) (cons x xs))))))
(else (error 'range "unrecognized arguments"))))
(define (sum xs) (apply + xs)) ; sum of elements of xs
(define digits (case-lambda ; list of base-b digits of n
((n) (digits n 10))
((n b) (do ((n n (quotient n b))
(ds (list) (cons (modulo n b) ds)))
((zero? n) ds)))))
(define (part k xs) ; k'th lexicographical left-partition of xs
(let loop ((ds (reverse (digits k 2))) (xs xs) (ys (list)))
(if (null? ds) (reverse ys)
(if (zero? (car ds))
(loop (cdr ds) (cdr xs) ys)
(loop (cdr ds) (cdr xs) (cons (car xs) ys))))))
(define (max-lcm xs) ; max lcm of part-sums of 2-partitions of xs
(let ((len (length xs)) (tot (sum xs)))
(apply max (map (lambda (s) (lcm s (- tot s)))
(map sum (map (lambda (k) (part k xs))
(range (expt 2 (- len 1)))))))))
(display (max-lcm '(2 3 4))) (newline) ; 20
(display (max-lcm '(2 3 4 6))) (newline) ; 56
答案 1 :(得分:1)
您可以使用内置的combinations
过程找到列表的所有2个分区。这个想法是,对于(len-k)-combination
的每个元素,k-combination
中都会有一个元素补充它,产生一对列表,其联合是原始列表,交集是空列表。
例如:
(define (2-partitions lst)
(define (combine left right)
(map (lambda (x y) (list x y)) left right))
(let loop ((m (sub1 (length lst)))
(n 1))
(cond
((< m n) '())
((= m n)
(let* ((l (combinations lst m))
(half (/ (length l) 2)))
(combine (take l half)
(reverse (drop l half)))))
(else
(append
(combine (combinations lst m)
(reverse (combinations lst n)))
(loop (sub1 m) (add1 n)))))))
然后您可以将分区构建为:
(2-partitions '(2 3 4))
=> '(((2 3) (4))
((2 4) (3))
((3 4) (2)))
(2-partitions '(4 6 7 9))
=> '(((4 6 7) (9))
((4 6 9) (7))
((4 7 9) (6))
((6 7 9) (4))
((4 6) (7 9))
((4 7) (6 9))
((6 7) (4 9)))
此外,您可以找到分区的最大lcm:
(define (max-lcm lst)
(define (local-lcm arg)
(lcm (apply + (car arg))
(apply + (cadr arg))))
(apply max (map local-lcm (2-partitions lst))))
例如:
(max-lcm '(2 3 4))
=> 20
(max-lcm '(4 6 7 9))
=> 165
答案 2 :(得分:0)
对列表进行分区是一种简单的递归非确定性编程。
给定一个元素,我们把它放在一个袋子里,或另一个袋子里。
第一个元素将进入第一个包,不失一般性。
最后一个元素必须只进入一个空包,如果那个时候存在的话。由于我们首先将第一个元素放入第一个包中,因此它只能是第二个:
(define (two-parts xs)
(if (or (null? xs) (null? (cdr xs)))
(list xs '())
(let go ((acc (list (list (car xs)) '())) ; the two bags
(xs (cdr xs)) ; the rest of list
(i (- (length xs) 1)) ; and its length
(z '()))
(if (= i 1) ; the last element in the list is reached:
(if (null? (cadr acc)) ; the 2nd bag is empty:
(cons (list (car acc) (list (car xs))) ; add only to the empty 2nd
z) ; otherwise,
(cons (list (cons (car xs) (car acc)) (cadr acc)) ; two solutions,
(cons (list (car acc) (cons (car xs) (cadr acc))) ; adding to
z))) ; either of the two bags;
(go (list (cons (car xs) (car acc)) (cadr acc)) ; all solutions after
(cdr xs) ; adding to the 1st bag
(- i 1) ; and then,
(go (list (car acc) (cons (car xs) (cadr acc))) ; all solutions
(cdr xs) ; after adding
(- i 1) ; to the 2nd instead
z))))))
那就是那个!
在撰写本文时,我通过关注this earlier related answer of mine得到了帮助。
测试:
(two-parts (list 1 2 3))
; => '(((2 1) (3)) ((3 1) (2)) ((1) (3 2)))
(two-parts (list 1 2 3 4))
; => '(((3 2 1) (4))
; ((4 2 1) (3))
; ((2 1) (4 3))
; ((4 3 1) (2))
; ((3 1) (4 2))
; ((4 1) (3 2))
; ((1) (4 3 2)))
可以在返回之前反转部件,或者当然;我希望保持代码简洁,干净,没有多余的细节。
编辑:该代码使用了Richard Bird的一项技术,将(append (g A) (g B))
替换为(g' A (g' B z))
,其中(append (g A) y) = (g' A y)
和{{1}的初始值是一个空列表。
另一种可能性是将z
的嵌套调用置于go
之后(确实是OP确定的)并在外部调用lambda
完成其工作时激活,整个函数尾递归,主要在CPS style。