R5RS 中的数字分区

Number Partitioning in R5RS

我在一次实习面试中被要求做一个创建函数的 R5RS 程序,比方说两个子集。如果列表 L 包含两个元素总和相等且元素数量相等的子集,则此函数必须 return #t,否则它 returns #f。它接受列表 L(只有正数)和一些参数(我认为有用。参数数量没有条件)在开始时都等于 0。

我还记得当时的要求是: - 不要定义其他函数并在 "two-subsets" 函数中调用它们。 - 它只能使用以下结构:null?, cond, car, cdr, else, + ,=, not, and, #t, #f, two-subsets (itself for recursive call), 参数名称,例如列表、求和、...等、数字常量和括号。

有一些关于我们应该得到的结果的例子,比方说:

(two-subsets '(7 7) 0 0 0) returns #t. The two subsets are {7} and {7}. 
(two-subsets '(7 7 1) 0 0) returns #t. The two subsets are {7} and {7}. 
(two-subsets '(5 3 2 4) 0 0) returns #t. The two subsets are {2, 5} and {3, 4}. 
(two-subsets '(1 2 3 6 9) 0 0) returns #f. 

我开始写签名,在我看来它应该是这样的:

(define two-subsets (lambda (L m n ... other parameters)
                    (cond

这个问题真的很复杂,复杂度明显超过O(n),我在https://en.wikipedia.org/wiki/Partition_problem上看了。

我试着先定义算法,然后再编码。我考虑将其作为参数:列表 L 的总和,因此在我的条件下,我将仅迭代总和 <= sum(L)/2 的组合。通过这样做,我可以减少一点问题的复杂性,但我仍然不知道该怎么做。

这看起来是个有趣的问题,我真的很想了解更多。

这是一个不依赖于数字全部为正的版本。我有理由相信,通过了解它们,您可以做得比这更好。

注意这是假设:

  • 分区不需要面面俱到;
  • 但集合不能为空。

我很想看到依赖于列表元素的版本 +ve!

(define (two-subsets? l sl sld ssd)
  ;; l is the list we want to partition
  ;; sl is how many elements we have eaten from it so far
  ;; sld is the length difference in the partitions
  ;; ssd is the sum difference in the partitions
  (cond [(and (not (= sl 0))
              (= sld 0)
              (= ssd 0))
         ;; we have eaten some elements, the differences are zero
         ;; we are done.
         #t]
        [(null? l)
         ;; out of l, failed
         #f]
        ;; this is where I am sure we could be clever about the set containing
        ;; only positive numbers, but I am too lazy to think

        [(two-subsets? (cdr l)
                       (+ sl 1)
                       (+ sld 1)
                       (+ ssd (car l)))
         ;; the left-hand set worked
         #t]
        [(two-subsets? (cdr l)
                       (+ sl 1)
                       (- sld 1)
                       (- ssd (car l)))
         ;; the right-hand set worked
         #t]
        [else
         ;; finally drop the first element of l and try the others
         (two-subsets? (cdr l) sl sld ssd)]))