Enigmatic Code

Programming Enigma Puzzles

Enigma 767: Safety in numbers

From New Scientist #1922, 23rd April 1994 [link]

Harry’s safe has a combination lock, with the usual dial with divisions marked round its edge from 0 to 99. Security rules forbid him to write down the combination, which he is expected to memorise. He has a poor memory for figures, so he can seldom remember it, but he can remember that it has four 2-digit numbers and one 1-digit number in sequence, and that if the sequence of numbers is strung together, the digits of the resulting 9-digit number are all different. He can also remember that if he subtracts his room number from that number, the result is a perfect square; of which the square root is a perfect cube; of which the cube root is his room number. He would not of course tell you all this, because he knows that it would enable you to work out the combination.

Reminding himself of his room number (which is on his door!) he can work out the combination in a few minutes, even without his pocket calculator (which reduces the time to mere seconds). He thus avoids the ignominy of having to plead with Security to open the safe for him.

You may take rather longer than Harry to work out the combination; but if I tell you that the sum of the digits of the cube is equal to its cube root, it could help cut the time.

What is the combination (the five separate numbers in sequence)?

[enigma767]

10 responses to “Enigma 767: Safety in numbers

  1. Jim Randell 6 May 2024 at 2:09 pm

    This Python program runs in 57ms. (Internal runtime is 137µs).

    Run: [ @replit ]

    from enigma import (irange, inf, nsplit, dsum, printf)
    
    # consider room number x
    for x in irange(1, inf):
      x3 = x**3
      x6 = x3 * x3
      n = x6 + x
      # combination consists of 9 different digits
      ds = nsplit(n)
      if len(ds) < 9: continue
      if len(ds) > 9: break
      if len(set(ds)) != 9: continue
      # digit sum of x3 is x
      if dsum(x3) != x: continue
      # construct the combination
      ns = list(nsplit(n * 10, base=100))
      ns[-1] //= 10
      # check the 2-digit numbers
      if not all(n > 9 for n in ns[:-1]): continue
      # output solution
      printf("combination = {ns} [x={x} x^3={x3} x^6={x6}]")
    

    Solution: The combination is: 38, 74, 20, 51, 6.

    The room number is 27, and so the combination is determined from the calculation:

    >>> 27**6 + 27
    387420516
    
  2. ruudvanderham 6 May 2024 at 4:09 pm

    As @Jim Randell had also worked out:
    code = room_number ** 6 + room_number.
    And as there’s nowhere stated that the individual 2- and 1-digit numbers may not start with a 0, there’s no need to verify that (as @Jim Randell does in his code).

    Therefore, the code can be really simple:

    """
    it can be easily seen that
        cbrt(sqrt(code - room_number)) == room_number
    and thus
        code = room_number ** 6 + room
    """
    from istr import istr
    from itertools import count
    
    for room_number in istr(count(1)):
        with istr.int_format("09"):  # zero fill upto 9 digits
            code = room_number**6 + room_number
        if len(code) > 9:
            break
        if len(code) == 9 and len(set(code)) == 9:
            print(f"code = {'-'.join(code[i:i+2] for i in range(0,9,2))}  room number={room_number}")
    
    

    And indeed this gives two possible answers:

    code = 02-41-37-58-6 room number=17
    code = 38-74-20-51-6 room number=27

    Observation: The search stops at the moment room_number becomes 32.

    Note that it would be possible to determine the first possible start number (being 15), but I don’t think it’s worth the effort.

    • ruudvanderham 6 May 2024 at 4:14 pm

      Actually the test len(code) == 9 is not required!
      So the last test line could also read:

          if len(set(code)) == 9:
      
      • ruudvanderham 6 May 2024 at 5:00 pm

        I had forgotten the last hint
        “the sum of the digits of the cube is equal to its cube root”
        , but if I add that test, my two solutions are still ok.

        Here’s my updated code:

        from istr import istr
        from itertools import count
        
        for room_number in istr(count(1)):
            with istr.int_format("09"):  # zero fill upto 9 digits
                code = room_number**6 + room_number
            if len(code) > 9:
                break
            if len(set(code)) == 9 and (sum(room_number ** 3) == room_number):
                print(f"code = {'-'.join(code[i:i+2] for i in range(0,9,2))}  room number={room_number}")
        
    • Jim Randell 7 May 2024 at 6:46 am

      @Ruud: In these puzzles leading zeros are not usually allowed unless explicitly stated. So in this puzzle (as in standard usage) a 1-digit number is 0 – 9, a 2-digit number 10 – 99, etc.

      If you take this into account you should find there is a single unique solution to the puzzle.

  3. GeoffR 6 May 2024 at 8:36 pm
    from enigma import nsplit, all_different
    
    # Numerical limits of the room number with 9 digits code
    # 999999999**(1/6) = 31.62  UB of room no.= 31
    # 100000000**(1/6) = 21.54  LB of room no.= 22
    for rn in range(22, 32):
        N = rn**6 + rn
        a, b, c, d, e, f, g, h, i = nsplit(N)
        if not all_different(a, b, c, d, e, f, g, h, i):continue
        N3 = rn**3  # square root of rn**6 and a cube of rn
        m, n, p, q, r = nsplit(N3)
        # sum of the digits of the cube is equal to its cube root
        if m + n + p + q + r == rn:
            print(f"Room Number = {rn}")
            print(f"Code: {10*a+b} {10*c+d} {10*e+f} {10*g+h} {i}")
        
    # Room Number = 27
    # Code: 38 74 20 51 6
    
    
  4. Jim Randell 7 May 2024 at 7:04 am

    Assuming a 2-digit room number (as shown above by @GeoffR), we can use a simple [[ SubstitutedExpression ]] recipe to solve the puzzle.

    The following run file has an internal runtime of 78µs.

    Run: [ @replit ]

    #! python3 -m enigma -r
    
    # the code is: AB CD EF GH I
    # the room number is: XY
    
    SubstitutedExpression
    
    --distinct="ABCDEFGHI"
    
    "XY**6 + XY = ABCDEFGHI"
    "dsum(XY**3) = XY"
    
    --answer="(AB, CD, EF, GH, I)"
    
  5. GeoffR 7 May 2024 at 9:31 am

    Faster in Chuffed Solver than Geocode Solver, but much slower than Python.

    % A Solution in MiniZinc
    include "globals.mzn";
    
    var 1..9:A; var 0..9:B; var 1..9:C; var 0..9:D;
    var 1..9:E; var 0..9:F; var 1..9:G; var 0..9:H;
    var 1..9:I;
    
    constraint all_different([A,B,C,D,E,F,G,H,I]);
    
    var 100000000..999999999:ABCDEFGHI == I + 10*H + 100*G + 1000*F + E * pow(10,4)
    + D * pow(10,5) + C * pow(10,6) + B * pow(10,7) + A * pow(10,8);
    
    var 1..9:M; var 0..9:N; var 0..9:P; var 0..9:Q;var 0..9:R;
    
    % Construct XY ^ 3 with digits for use in the sum
    var 10000..99999:XY_CUBED == 10000*M + 1000*N + 100*P + 10*Q + R;
    constraint XY_CUBED = XY * XY * XY;
    
    var 1..9:X; var 0..9:Y; 
    var 10..99:XY == 10*X + Y;
    
    constraint XY * XY * XY * XY * XY * XY + XY == ABCDEFGHI;
    
    constraint M + N + P + Q + R == XY;
    
    % Parts of output code
    var 10..99:AB == 10*A + B;
    var 10..99:CD == 10*C + D;
    var 10..99:EF == 10*E + F;
    var 10..99:GH == 10*G + H;
    
    solve satisfy;
    
    output [ "Room Number = " ++ show(XY) ++ "\n" ++
    "Code = " ++ show(AB) ++ " " ++ show(CD) ++ " " ++ show(EF)
    ++ " " ++ show(GH) ++ " " ++ show(I) ];
    
    % Room Number = 27
    % Code = 38 74 20 51 6
    % ----------
    % ==========
    
    
    
    
  6. GeoffR 7 May 2024 at 3:27 pm
    import math
    from enigma import is_square, nsplit
    
    # Room number is a cube in range 22..32 - previous posting, so:
    rn = 27
     
    # Find range of cubes to give 9-digit numbers
    # 100000000**(1/3) = 464.15   LB = 465
    # 999999999**(1/3) = 999.99   UB = 999
    for x in range(465, 999):
      # cube chosen must also be a square number
      if is_square(x * x * x):
        mnpqr = math.isqrt(x * x * x)
        if rn * rn * rn == mnpqr:
          m, n, p, q, r = nsplit(mnpqr)
          if m + n + p + q + r == rn:
            N = x * x * x + rn
            if len(str(N)) == 9 and len(set(str(N))) == len(str(N)):
              a, b, c, d, e, f, g, h, i = nsplit(N)
              print(f"Code: {10*a+b} {10*c+d} {10*e+f} {10*g+h} {i}")
    
    
  7. Frits 7 May 2024 at 8:56 pm

    Only fast with Geocode Solver. Variable X has to be limited to 31 otherwise there is an “out of range” error. I didn’t code the optional constraint (“to help cut the time”).

    % A Solution in MiniZinc
    include "globals.mzn";
    
    set of int: odds = {3, 5, 7};
    
    % return <n>-th digit of number <a> with length<len>
    function var int: nthDigit(var int: a, var int: len, var int: n) =
      ((a mod pows[len + 2 - n]) div pows[len + 1 - n]);   
    
    var 22..31:X; 
    
    var 100000000..999999999: N;
    constraint N = X * X * X * X * X * X + X;
    
    % make a table of powers of 10
    set of int: pows = {pow(10, j) | j in 0..9}; 
    
    array[1..9] of var 0..9: dgts;
    constraint all_different(dgts);
    
    % fill the array with digits of N
    constraint forall (i in 1..9) (dgts[i] == nthDigit(N, 9, i));
    % four 2-digit numbers 
    constraint forall (i in odds) (dgts[i] != 0);
     
    solve satisfy;
     
    output [ "Room Number = " ++ show(X) ++ "\n" ++
    "Code = " ++ show(dgts[1]) ++ show(dgts[2]) ++ " " ++ 
                 show(dgts[3]) ++ show(dgts[4]) ++ " " ++ 
                 show(dgts[5]) ++ show(dgts[6]) ++ " " ++ 
                 show(dgts[7]) ++ show(dgts[8]) ++ " " ++ 
                 show(dgts[9])];
     
    % Room Number = 27
    % Code = 38 74 20 51 6
    % ----------
    % ========== 
    

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.