Knuth's program PERFECT-PARTITION-SQUARE
Donald Knuth has a page on his website where he sometimes posts programs he has written. (He says he writes several programs a week, mostly in CWEB and mostly for his own interest, but occasionally posts some of them online.)
A couple of days ago, on 2024-06-01, Knuth posted a program, with the description “An amusing recreation”, that solves the following problem:
Michael Keller suggested a problem that I couldn’t stop thinking about, although it is extremely special and unlikely to be mathematically useful or elegant: “Place seven 7s, . . . , seven 1s into a 7 × 7 square so that the 14 rows and columns exhibit all 14 of the integer partitions of 7 into more than one part.”
The word “exhibit” above needs some explanation. 6 5 7 4 5 5 6
, then, because it contains three
Clearly, the contents of any row or column (having
Note that although, for any
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 2 | 3 | 5 | 7 | 11 | 15 | 22 | 30 | 42 | 56 | 77 |
and so on:
(Aside: However, for
(31) | (22) | (211) | (1111) | |
---|---|---|---|---|
(31) | 1 | 4 | 4 | 4 |
(22) | 3 | 2 | 2 | 3 |
(211) | 1 | 2 | 3 | 1 |
(1111) | 1 | 4 | 3 | 2 |
The problem with
Anyway, for the
I doubt if there’s a solution. But if there is, I guess I want to know. So I’m writing this as fast as I can, using brute force for simplicity wherever possible (and basically throwing efficiency out the door).
Try this as an exercise yourself, and see how far you get, before reading Knuth’s program. You may discover that when Knuth says “brute force for simplicity” and “throwing efficiency out the door”, he still doesn’t quite mean what you or I might mean by it. :-)
For instance, the number of ways of arranging the numbers in the
#define gosper(b) { register x=b,y; \
x=b&-b,y=b+x; \
b=y+(((y^b)/x)>>2); }
(Spoiler: I was impressed to find that ChatGPT could work this out/reproduce it from its training data, even with the PDF copy-paste that has a control character instead of the right shift. Edit after playing with it a bit more: it’s not very good at follow-up questions and variations, so I guess this is another instance where the model’s wider initial knowledge can fool one into thinking it is more capable than it is.)
His program, compiled at -O3
, runs in about 6–8 minutes on my laptop (2023 M2 Pro / 2021 M1 Pro), and finds 30885 solutions, which are already distinct under symmetries like permuting rows, permuting columns, transposing (changing rows to columns and vice-versa), and permuting the digits themselves (swapping all
For example, here is the 24200th solution:
(421) | (322) | (3211) | (31111) | (22111) | (211111) | (1111111) | |
---|---|---|---|---|---|---|---|
(61) | 7 | 6 | 7 | 7 | 7 | 7 | 7 |
(52) | 1 | 6 | 1 | 6 | 6 | 6 | 6 |
(511) | 7 | 6 | 5 | 5 | 5 | 5 | 5 |
(43) | 2 | 4 | 4 | 4 | 2 | 2 | 4 |
(4111) | 2 | 3 | 5 | 4 | 3 | 3 | 3 |
(331) | 2 | 3 | 1 | 1 | 3 | 3 | 1 |
(2221) | 2 | 4 | 1 | 4 | 5 | 1 | 2 |
and you can see that the rows and columns cover all
To compile his program I had to change a couple of register
declarations to register int
, which reminded me of this tweet from Per Vognsen:
People who quote Knuth on premature optimization must not have seen his code. He's that dude who puts 'register' on variables in 2020 and implements a bespoke trie for simple one-off string processing programs.
— Per Vognsen (@pervognsen) April 21, 2020
and:
He's so much a "premature" optimization proponent that he's still putting 'register' on variables in C after compilers stopped paying attention to them 30 years ago. It's basically him just throwing up gang signs.
— Per Vognsen (@pervognsen) April 21, 2020
Note also that section 5 in the program is basically a copy-paste of section 4, with rows changed to columns. Similarly two code blocks inside section 6, and inside section 7 (and I wonder if some code could have been reused between 6 and 7?). There are “magic numbers” like 24 and 4 (besides of course 7) everywhere. And of course he uses goto
a couple of times (to break out of an inner loop). None of this matters — the program correctly does what is intended, is the way he is comfortable programming and works for him, and after reading it carefully (and picking up at least a couple of tricks for the future), I’m only left wondering what the “efficient” version would look like.
Sources:
- .w file on Knuth’s website
- typeset (PDF) version (The
.w
file run throughcweave
and then throughpdftex
) (See this repo for other programs from his website.) - .c file (The
.w
file run throughctangle
after replacing a couple ofregister
withregister int
, andvoid main
withint main
.)