# partition lcm

Write a function that accepts a list of numbers and returns the maximum least common multiple among the sums of all possible partitions of the list.

``` \$ cat mlp.rkt
#lang racket

(define (max-lcm-partition l)

; Return the list (p1 p2 lcm) where p1 and p2 partition l and lcm is
; the lcm of the sums of p1 and p2 such that lcm is among the
; largest of all possible partitionings of l.

(let*

((max-p1 '())
(max-p2 l)
(max-lcm 0)
(f (lambda (p1 p2)
(let
((lcm (lcm (apply + p1) (apply + p2))))

(when (> lcm max-lcm)
(set! max-lcm lcm)
(set! max-p1 p1)
(set! max-p2 p2))))))

(let loop ((l l) (p1 '()) (p2 '()))
(if (null? l)
(f p1 p2)
(let ((e (car l))
(l (cdr l)))
(loop l (cons e p1) p2)
(loop l p1 (cons e p2)))))

(list max-p1 max-p2 max-lcm)))

(define (max-lcm-partition-2 l)

; Return the list (p1 p2 lcm) where p1 and p2 partition l and lcm is
; the lcm of the sums of p1 and p2 such that lcm is among the
; largest of all possible partitionings of l.

(define N (length l))

; The exploded N-bit number that generates list partitions.  The extra
; bit is the overflow bit signaling the end of partition generation.

(define partition (make-vector (+ N 1) 0))

(define max-lcm 0)
(define max-p1 '())
(define max-p2 l)

; Add one to the partition number.

(let loop ((i N))
(unless (< i 0)
(cond
((= (vector-ref partition i) 0)
(vector-set! partition i 1))
(#t
(vector-set! partition i 0)
(loop (- i 1)))))))

(define (sum-partition-lcm)

; Determine if the current partition has a new maximum lcm.

(let loop ((i N) (l l) (p1 '()) (p2 '()))
(if (< i 1)

(let ((lcm (lcm (apply + p1) (apply + p2))))
(when (< max-lcm lcm)
(set! max-lcm lcm)
(set! max-p1 p1)
(set! max-p2 p2)))

(let ((e (car l))
(l (cdr l)))
(if (= (vector-ref partition i) 0)
(loop (- i 1) l (cons e p1) p2)
(loop (- i 1) l p1 (cons e p2)))))))

(do () ((= (vector-ref partition 0) 1))
(sum-partition-lcm)

(list max-p1 max-p2 max-lcm))

(require rackunit)

(check-eq? (caddr (max-lcm-partition '(2 3 4 6))) 56)
(check-eq? (caddr (max-lcm-partition-2 '(2 3 4 6))) 56)

(define (chk l)
(let ((a1 (max-lcm-partition l))
(a2 (max-lcm-partition-2 l)))

(chk '(2 3 4 6))
(chk '(2 4 5))
(chk '(2 4 6 7))

(define (make-random-list n)
(let loop ((i 0) (l '()))
(if (>= i n)
l
(loop (+ i 1) (cons (random 1000) l)))))

(define (check-random-list i n)
(do ((j i (- j 1))) ((< j 1))
(chk (make-random-list n))))

(check-random-list 10 15)

(define (time-it f N)
(let loop ((t 0) (n N))
(if (= n 0)
(exact->inexact (/ t N))
(let-values
(((a b c d) (time-apply f '())))
(loop (+ t b) (- n 1))))))

(define (time len n)
(let* ((l (make-random-list len))
(p (lambda (t f)
(printf "~a, ~a elements ~a msec\n"
t len (time-it (lambda () (f l)) n)))))
(p "recursive" max-lcm-partition)
(p "iterative" max-lcm-partition-2)))

(define N 10)

(for-each (lambda (l) (time l N)) '(10 15 20))

\$ mzscheme mlp.rkt
recursive, 10 elements 0.8 msec
iterative, 10 elements 0.8 msec
recursive, 15 elements 26.0 msec
iterative, 15 elements 36.4 msec
recursive, 20 elements 966.4 msec
iterative, 20 elements 1406.8 msec

\$
```