Jane’s Homework
December 12, 2017
Let’s augment the table from the first page:
0 - 0000 - () (2 3 4 6) - 0 15 - 0 1 - 0001 - (2) (3 4 6) - 2 13 - 26 2 - 0010 - (3) (2 4 6) - 3 12 - 12 3 - 0011 - (2 3) (4 6) - 5 10 - 10 4 - 0100 - (4) (2 3 6) - 4 11 - 44 5 - 0101 - (2 4) (3 6) - 6 9 - 18 6 - 0110 - (3 4) (2 6) - 7 8 - 56 7 - 0111 - (2 3 4) (6) - 9 6 - 18 8 - 1000 - (6) (2 3 4) - 6 9 - 18 9 - 1001 - (2 6) (3 4) - 8 7 - 56 10 - 1010 - (3 6) (2 4) - 9 6 - 18 11 - 1011 - (2 3 6) (4) - 11 4 - 44 12 - 1100 - (4 6) (2 3) - 10 5 - 10 13 - 1101 - (2 4 6) (3) - 12 3 - 12 14 - 1110 - (3 4 6) (2) - 13 2 - 26 15 - 1111 - (2 3 4 6) () - 15 0 - 0
For a list of n items, there will be 2n possible 2-partitions. A simple binary code running from 0 to 2n − 1 specifies which list items to include in each partition:
(define (part k 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))))))
The kth part
takes the binary representation of k and applies it to xs, keeping those items with a 1-bit at that list item. Thus, to compute the 11th partition of (2 3 4 6), express 11 as 1011 in binary and keep the 2, 3 and 6 (the bits count right-to-left) of the input. The other partition, of course, is whatever is left. Then it’s easy to find the maximum least common multiple:
(define (max-lcm 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))))))))))
Reading from the inside out: range
generates a half-list of partition numbers (only half is needed due to symmetry), then lambda (k)
computes the partition, sum
sums the item in the partition, lambda (s)
computes the least common multiple of the partition-sum and its conjugate, and max
finds the maximum:
> (max-lcm '(2 3 4 6)) 56
You can run the program at https://ideone.com/c4yYG6.
Two methods to partition, one recursive and the other using the powerset function from the itertools documentation.
Here’s another way: iteratively generate combinations but bail out when the combination sum gets too large (this works for any value of limit and it’s straightforward to generalize to multisets):
Here’s a Haskell version that doesn’t use explicit recursion. The part2s
function uses replicateM (the monadic version of replicate, for lists) to
generate all length 4 (in our example) combinations of Left and Right, the two
constructors for Either. For each combination we zip it with the original list,
using the “reverse application operator”, to produce a list of Eithers which is
then partitioned into two lists.
A couple of solutions in Racket.
Here’s a solution in C.
The code iterates over the power set of the input list. The approach is based on Loopless Gray binary generation (Algorithm L in TAOCP section 7.2.1.1), which calculates a single element index to be added or removed on each iteration. Partition sums are re-calculated with an addition or subtraction of the updated element. LCM is then calculated on these updated sums.
Output:
I forgot to free the memory I allocated in the calc_janes_homework function. mask and focus should both be free’d before the function returns.
Here’s a solution in racket.