codingdiagrams

Created 17 Jul 2023

Filling a cube with 1:2:3 Cuboids

A positive integer nn is 'lucky' if it is possible to fill a cube with nn cuboids, each of whom has side ratio 1:2:3. Which numbers are lucky?

I encountered this problem while applying to MBL-Balkans 2023. It's essentially a 3D version of the problem discussed in this numberphile video.

It's more interesting to ask this version of the question:

Let CC be the minimal positive integer such that all integers C \geq C are lucky. Find an upper bound on CC.

For example, if we managed to show that all integers greater than 100 are lucky, then an upper bound on CC would be 101. Of course, it might be very hard to find the actual value of CC.

Below is the best upper bound on CC that I could get - do try the problem yourself and let me know your result.

Getting a foothold

Before we actually find a lucky number, we can try to find some rules of inference, for example "if nn is lucky then so is n+1000n+1000". If we can find lots of these, and at least one lucky number, then hopefully we can mark many integers as lucky.

We might first notice that if we have a filling of a cube with nn cuboids of side ratio 1:2:3, then we can split one of them into 8 new cuboids by halving along each edge. The new number of cuboids is n+81=n+7n+8-1 = n+7 (minus one because of the cuboid we replaced). So we have that:

If nn is lucky, then so is n+7n+7.

This sort of feels like trying to build a cube out of wooden blocks, but the only blocks we have access to are cubes and 1:2:3 cuboids.

With this mental imagery, we can find another construction:

construction

We used 2 cubes and 3 cuboids, thus if nn and mm are lucky, we can scale two cubes tiled with nn and mm cuboids to fit inside this construction. Hence we know that:

If nn and mm are lucky, then so is n+m+3n+m+3.

We can continue to try constructions like this.

Construction Tools

Claim 1.

If nn is lucky, then so is n+7n+7.

Proof.

See above.

Claim 2.

If nn and mm are lucky, then so is n+m+3n+m+3.

Proof.

See above.

Claim 3.

If nn and mm are lucky, then so is n+m+8n+m+8.

Proof.

construction

Claim 4.

If nn is lucky, then so is n+15n+15.

Proof.

construction

We can make a 6x6x3 cuboid with eleven 1:2:3 cuboids, as shown. Then, we can make another 6x6x3 cuboid using the same construction as in the proof of claim 3, where we wrap 4 cuboids around a cube. Thus we can combine these two 6x6x3 cuboids to form a 6x6x6 cube, using fifteen 1:2:3 cuboids and one smaller cube.

Claim 5.

If nn is lucky, then so is n+13n+13.

Proof.

construction

Claim 6.

If nn is lucky, then so is n+12n+12.

Proof.

construction

Finding a base case

To actually find a lucky number, we remove cubes from our lego building blocks and only use 1:2:3 cuboids.

Claim 7.

8 is lucky.

Proof.

construction

Now, let's see what numbers we can conquer with what we have so far.

By Claim 1, we know that 8, 15, 22, 29, 36 etc. are all lucky.

1234567

8

910
11121314

15

1617181920
21

22

232425262728

29

30
3132333435

36

37383940
etc.

Now we can use Claim 2 to conquer 8+8+3=19, 8+15+3=26, 15+15+3=33, 15+22+3=40, etc.

1234567

8

910
11121314

15

161718

19

20
21

22

232425

26

2728

29

30
3132

33

3435

36

373839

40

etc.

And so on, utilising all of the Claims. In the end, we conquer the following numbers up to 40:

1234567

8

910
11121314

15

161718

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

etc.

OK - great! Now we have seven consecutive numbers (26 to 32) all being lucky. Since n    n+7n \implies n+7 (Claim 1), this means every integer greater than or equal to 26 is lucky. So CC is at most 26.

Reducing the bound with code

Have you noticed that in the table above, 25 sticks out like a sore thumb? If we could just show that 25 is lucky, we could add the numbers from 19 to 24 to our chain of consecutive numbers... and C would be at most 1919 - and that would be a good place to stop, because conquering 25 feels like such a bargain (we would reduce C by a lot, not just by 1). But 25 = 18 + 7, so can we show that 18 is lucky? Then we'd have C18C \leq 18. 18 feels too big to manually try and search for, so can we write some code to brute force it?

Yes we can - if we assume that we can build up tilings by repeatedly joining two cuboids at a time into a larger cuboid, then we can store the side ratios that can be constructed like this.

The side ratio is stored as a 3-tuple (x,y,z)(x,y,z) with x always equal to 1. To see if we can combine two ratios, we check if the zz-values are the same, and if so we add their yy-values (assuming we always join them by placing one cuboid on top of the other - thus for each 3-tuple, we must store all of its 6 permutations). For example, (1,2,3)+(1,2,3)=(1,4,3)(1,2,3) + (1,2,3) = (1,4,3).

We can denote by SkS_k the set of all side ratios that can be constructed using exactly kk 1:2:3 cuboids. We generate SkS_k by trying to comine all aspect ratios which have kk total cuboids, which we can do dynamically (i.e. generate S1S_1, then S2S_2, then S3S_3, etc.). Then, we look at which SkS_k contain (1,1,1)(1,1,1).

#!/usr/bin/python3

from fractions import Fraction as F
from collections import defaultdict

S = {}

# normalized fractions: x := 1
S[1] = {
        (F(1,1),F(2,1),F(3,1)): (),
        (F(1,1),F(3,1),F(2,1)): (),
        (F(1,1),F(1,2),F(3,2)): (),
        (F(1,1),F(3,2),F(1,2)): (),
        (F(1,1),F(1,3),F(2,3)): (),
        (F(1,1),F(2,3),F(1,3)): (),
       }

for k in range(2,22):
    print(k)
    # compute S_k
    S[k] = defaultdict(lambda: ((0,0), (0,0))) # (n,key), (m,key)
    for n in range(1, k//2+1):
        m = k-n # m+n = k
        for a in S[n].keys():
            for b in S[m].keys():
                if a[2] == b[2]:
                    x,y,z = (F(1,1), a[1]+b[1], a[2])
                    newratios = list(set([
                        (F(1,1),y/x,z/x),
                        (F(1,1),z/x,y/x),
                        (F(1,1),x/y,z/y),
                        (F(1,1),z/y,x/y),
                        (F(1,1),x/z,y/z),
                        (F(1,1),y/z,x/z),
                        ]))
                    if S[k][newratios[0]] == ((0,0),(0,0)):
                        if newratios[0] == (1,1,1):
                            print(f"FOUND CUBE FOR S_{k}")
                        for newratio in newratios:
                            S[k][newratio] = ((n,a),(m,b))

def print_construction(k, r, depth):
    if k == 1:
        # terminal nodes displayed with a colon
        print("  "*depth, f":({r[0]}{r[1]}{r[2]})")
        return
    print("  "*depth, f"({r[0]}{r[1]}{r[2]})")
    ((n,a),(m,b)) = S[k][r]
    print_construction(n, a, depth+1)
    print_construction(m, b, depth+1)

r = (1, 1, 1)

for k in S.keys():
    if k==1: continue 
    if S[k][r] != ((0,0),(0,0)):
        print(f"found {r} in S_{k}:")
        print_construction(k, r, 0)

If the program finds (1,1,1)(1,1,1) in SkS_k (i.e. a cube, although you can search for any ratio you want by changing r), then it will print out the way it found to construct it, in a tree-like manner using the recursive print_construction function.

The program found the following construction, proving that 18 is lucky:

construction

Thus, 18+7=25 is also lucky, and so C18C \leq 18.