Tri-Partitions
November 15, 2019
We first partition the input into three lists based on their congruence mod 3. Two items cannot appear in succession if one of them is congruent to 1 mod 3 and the other is congruent to 2 mod 3, or if both of them are congruent to 0 mod 3. Then, we construct the output with alternating 1s and 0s, followed by alternating 2s and 0s, with a 0 in between:
(define (f xs) (let loop ((xs xs) (zeroes (list)) (ones (list)) (twos (list))) (if (pair? xs) (cond ((= (modulo (car xs) 3) 0) (loop (cdr xs) (cons (car xs) zeroes) ones twos)) ((= (modulo (car xs) 3) 1) (loop (cdr xs) zeroes (cons (car xs) ones) twos)) (else (loop (cdr xs) zeroes ones (cons (car xs) twos)))) (if (and (null? zeroes) (pair? ones) (pair? twos)) #f ; no zero separator (if (null? zeroes) (append ones twos) (let loop ((saved (car zeroes)) (zeroes (cdr zeroes)) (ones ones) (twos twos) (zs (list))) (cond ((and (null? zeroes) (null? ones) (null? twos)) (if saved (cons saved zs) zs)) ((and (pair? zeroes) (pair? ones)) (loop saved (cdr zeroes) (cdr ones) twos (cons (car ones) (cons (car zeroes) zs)))) ((pair? ones) (loop saved zeroes (cdr ones) twos (cons (car ones) zs))) (saved (loop #f zeroes ones twos (cons saved zs))) ((and (pair? twos) (pair? zeroes)) (loop saved (cdr zeroes) ones (cdr twos) (cons (car zeroes) (cons (car twos) zs)))) ((pair? twos) (loop saved zeroes ones (cdr twos) (cons (car twos) zs))) (else #f)))))))) ; leftover zeroes
Here are some examples:
> (f '(0 0 0)) #f > (f '(0 0 1)) (0 1 0) > (f '(0 0 2)) (0 2 0) > (f '(0 1 1)) (0 1 1) > (f '(0 2 2)) (2 2 0) > (f '(1 1 1)) (1 1 1) > (f '(2 2 2)) (2 2 2) > (f '(1 1 2)) #f > (f '(0 1 2)) (2 0 1) > (define xs (map (lambda (x) (randint 100)) (range 20))) > xs (2 67 65 71 86 83 12 17 9 30 42 61 33 68 57 30 74 42 41 51) > (f xs) (2 65 71 12 86 9 83 30 17 42 68 33 74 57 41 51 67 30 61 42) > (map (lambda (x) (modulo x 3)) (f xs)) (2 2 2 0 2 0 2 0 2 0 2 0 2 0 2 0 1 0 1 0)
You can run the program at https://ideone.com/v6b63R.
As you say, it comes down to taking the integers mod 3, then ensuring that 0 is never next to 0 and 1 is never next to 2. It’s a nice exercise to find all the ways this can be done, given the number of integers with each modulus. Here’s some Haskell, it would benefit from memoization or dynamic programming:
Incidentally, for your example with 9 0’s and 2’s and just 2 1’s, there are 4322 different possible orderings, from 01010202020202020222 to 22202020202020201010 (your ordering and the mirror image of the first).
Here’s a solution in Python.
Output:
Here’s a similar solution to the one I posted earlier, but without the dependence on the collections module.
Here’s a dynamic programming solution in Python that just computes the number of possible sequences. It’s not to difficult to adapt to compute the sequences themselves. solveseq(s) gives the number of rearrangements (that preserve the ordering for each modulus class) for s. Use numpy for convenient multidimensional arrays – we can also specify the array type, type ‘object’ gives us bignums, or we can use a floats to avoid overflow in bigger problems:
We can also use the dynamic programming array to generate uniformly random sample sequences (also fixing a bug with the range for n on line 6 – should be I+J+K+1, and tightening up the bounds on i). Also make the output fruitier:
Typical output:
๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐๐
Once again, screwed by WordPress code formatting: that should be just ‘print(“๐๐๐”[n],end=””,flush=True)’
A couple of solutions in Racket.
Another Python solution: this one attempts to rearrange the input list (of 0,1,2) to satisfy the ordering requirements, but making a fairly minimal set of changes. Strategy is to build up result list t, taking first item from s that can be appended to t, also ensuring that a solution is still possible with the remaining items – specifically, that there are enough 1’s and 2’s to keep the 0’s separated (and that if there is only one 0 remaining, it doesn’t get appended when both 1’s and 2’s remain).
Output (hoping the Unicode chars come out):
A variation, that takes doesn’t require the mod 3 to already be done:
Output:
And here is an in-place version of the above – nice opportunity to use python range assignments & for-else:
This time, we print a trace of the computation: