Updates from Jim Randell Toggle Comment Threads | Keyboard Shortcuts

  • Unknown's avatar

    Jim Randell 9:26 am on 2 April 2023 Permalink | Reply
    Tags: by: V. Bryant   

    Brainteaser 1099: Jackpot! 

    From The Sunday Times, 28th August 1983 [link]

    Our local maths club has an appropriate slot machine. It consists of the digits from 1 to 9 in a row and each of them has a star below it which can light up. The machine always has some of the stars lit up and the idea is to read the number formed by the lit-up digits. So, for example, in the situation illustrated the number is 14689.

    There are four possible jackpots which this machine pays out. To have a go you observe the lit-up number, place 10p in the slot (which causes a different selection of digits to light), and observe the new number. (The machine is then ready for the next go). The jackpots are the “Fair Share” which is won if the number showing after putting in your 10p is precisely half the number which was showing before. The “Rare Square” which is won if the number showing after putting in your 10p is the square of the number showing before. The “Cute Root” which requires that the number before you place in the 10p is the square of the number showing afterwards. And the “Rum Sum” which is won if the number showing after you place in your 10p is the sum of the digit which were lit up before.

    One member had a very successful session yesterday. He placed four 10ps in the machine in order to have  four consecutive goes. And the sequence of five different selections of lit-up digits which he observed resulted in him winning all four jackpots.

    What number was showing immediately before he put in his first 10p?

    This puzzle is included in the book Microteasers (1986).

    [teaser1099]

     
    • Jim Randell's avatar

      Jim Randell 9:26 am on 2 April 2023 Permalink | Reply

      In order to arrive at a unique solution I had to (reasonably) assume that “some of the stars” means “at least 2 of the stars”.

      This Python program runs in 63ms. (Internal runtime is 7.8ms).

      Run: [ @replit ]

      from enigma import (irange, subsets, nconcat, dsum, diff, printf)
      
      # possible displayed numbers
      # = numbers with at least 2 different digits in increasing order
      ns = list(subsets(irange(1, 9), min_size=2, fn=nconcat))
      
      # record prize pairs
      prize = { 1: set(), 2: set(), 3: set(), 4: set() }
      
      # collect prize numbers
      for n in ns:
        tw = 2 * n
        if tw in ns:
          prize[1].add((tw, n))
        sq = n * n
        if sq in ns:
          prize[2].add((n, sq))
          prize[3].add((sq, n))
        ds = dsum(n)
        if ds in ns:
          prize[4].add((n, ds))
      
      # find a sequence of numbers that give prizes in <ks>
      def solve(ks, ss):
        if not ks:
          yield ss
        else:
          x = ss[-1]
          for k in ks:
            for (y, z) in prize[k]:
              if x == y and z not in ss:
                yield from solve(diff(ks, {k}), ss + [z])
      
      # choose a starting pair
      for (k, vs) in prize.items():
        for (a, b) in vs:
          # solve for the remaining prizes
          for ss in solve(diff(prize.keys(), {k}), [a, b]):
            printf("{ss}")
      

      Solution: The machine was initially showing: 134689.

      So we have:

      134689 -(sqrt)-> 367 -(dsum)-> 16 -(sq)-> 256 -(half)-> 128

      Like

    • Frits's avatar

      Frits 6:35 pm on 2 April 2023 Permalink | Reply

      Starting from Jim’s program but not using enigma.py and using a dictionary of slot machine numbers .

         
      from itertools import combinations
      
      dsum = lambda n: sum(int(x) for x in str(n))
      
      # number of different selections of lit-up digits
      N = 5
       
      # possible slot machine numbers
      ns = list(int("".join(str(y) for y in x)) 
                for n in range(2, 10) for x in combinations(range(1, 10), n))
       
      # dictionary of slot machine numbers and following jackpots
      d = {n : list([0] * (N - 1)) for n in ns}
       
      # collect dictionary elements
      for n in ns:
        tw = 2 * n
        if tw in ns:
          d[tw][0] = n
        sq = n * n
        if sq in ns:
          d[n][1] = sq
          d[sq][2] = n
        ds = dsum(n)
        if ds in ns:
          d[n][3] = ds
       
      # only keep slot machine numbers after which a jackpot may occur  
      d = {k: v for k, v in d.items() if v != [0] * (N - 1)}    
      
      # find a sequence of N slot machine numbers with N - 1 different jackpots
      def solve(k, ss, types=[]):
        if len(ss) == N:
          yield ss
        else:
          for t, v in enumerate(d[k]):
            # type of jackpot already processed?
            if t in types: continue 
            if ((len(ss) == N - 1 and v in ns) or v in d):
              # number may not have been used before
              if v not in ss: 
                yield from solve(v, ss + [v], types + [t])
       
      # check slot machine numbers after which a jackpot may occur 
      for k in d.keys():
        # check if N - 1 different jackpots can follow
        for ss in solve(k, [k]):
          print(f"{ss}")
      

      Like

  • Unknown's avatar

    Jim Randell 3:57 pm on 31 March 2023 Permalink | Reply
    Tags:   

    Teaser 3158: Digital trio 

    From The Sunday Times, 2nd April 2023 [link] [link]

    “I have a couple of subtraction problems for you”, George told Martha.

    Look:

    N1N2 = N3
    N3N4 = N5

    Can you solve them if I tell you that N1, N3 and N5 are all three-digit whole numbers whose sum is less than 2000, the same three non-zero digits appearing in all three numbers but no digit being repeated within any of those numbers? N2 and N4 are both two-digit whole numbers [each] using two of the three digits mentioned above, and the first digit of N1 is not equal to the first digit of N2.

    What is N1?

    [teaser3158]

     
    • Jim Randell's avatar

      Jim Randell 4:14 pm on 31 March 2023 Permalink | Reply

      I used the following interpretation for the numbers. Each of the 3-digit numbers is an arrangement of the same 3 different digits. And each of the 2-digit numbers is an arrangement of two of those 3 digits (but the 2-digit numbers are not necessarily rearrangements of each other).

      We can solve this puzzle using the [[ SubstitutedExpression ]] solver from the enigma.py library.

      This run file executes in 82ms. (The internal runtime of the generated program is 15ms).

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      #    ABC  (N1)
      #  -  DE  (N2)
      #  -----
      #  = FGH  (N3)
      #  -  IJ  (N4)
      #  -----
      #  = KLM  (N5)
      
      --digits="1-9"
      --distinct="ABC,DE,FGH,IJ,KLM,AD"
      
      # N1 - N2 = N3
      "ABC - DE = FGH"
      
      # N3 - N4 = N5
      "FGH - IJ = KLM"
      
      # N1 + N3 + N5 < 2000"
      "ABC + FGH + KLM < 2000"
      
      # N1, N3, N5 have the same digits
      "{A, B, C} == {F, G, H}"
      "{A, B, C} == {K, L, M}"
      
      # N2 and N4 are subsets of the 3 digits
      "{D, E}.issubset({A, B, C})"
      "{I, J}.issubset({A, B, C})"
      
      --answer="ABC"
      

      Solution: N1 = 594.

      Like

    • GeoffR's avatar

      GeoffR 9:36 am on 1 April 2023 Permalink | Reply

      % A Solution in MiniZinc
      include "globals.mzn";
      
      % Digits for three 3-digit numbers and two 2-digit numbers
      var 1..9:A; var 1..9:B; var 1..9:C;
      var 1..9:F; var 1..9:G; var 1..9:H;
      var 1..9:K; var 1..9:L; var 1..9:M;
      var 1..9:D; var 1..9:E;
      var 1..9:I; var 1..9:J;
      
      % Three 3-digit numbers and two 2-digit numbers
      var 100..999:ABC == 100*A + 10*B + C;  % N1
      var 100..999:FGH == 100*F + 10*G + H;  % N3
      var 100..999:KLM == 100*K + 10*L + M;  % N5
      var 10..99: DE == 10*D + E;  % N2
      var 10..99: IJ == 10*I + J;  % N4
      
      % Teaser number/digit constraints
      constraint ABC + FGH + KLM < 2000;
      constraint {A,B,C} == {F,G,H} /\ {A,B,C} == {K,L,M};
      constraint {D,E} subset {A,B,C};
      constraint {I,J} subset {A,B,C};
      constraint A != D;
      
      % Main equations
      constraint ABC - DE == FGH;  % N1 - N2 = N3
      constraint FGH - IJ == KLM;  % N3 − N4 = N5
      
      solve satisfy;
      
      output["N1 - N2 = N3 is " ++ show(ABC) ++ " - " ++ show(DE) ++ " = " ++ show(FGH)
      ++ "\n" ++ "N3 − N4 = N5 is " ++ show(FGH) ++ " - " ++ show(IJ) ++ " = " ++ show(KLM) ];
      
      
      
      

      Like

    • GeoffR's avatar

      GeoffR 5:26 pm on 1 April 2023 Permalink | Reply

      
      from itertools import permutations, combinations
      
      from enigma import Timer
      timer = Timer('ST3158', timer='E')
      timer.start()
      
      # choose three digits from 1..9
      for DIG3 in combinations('123456789', 3):
        # Lists for two and three digit numbers
        DL2, DL3 = [], []
        A, B, C = DIG3
        # Form possible 3-digit numbers for N1, N3 and N5
        ABC, ACB, BAC = int(A + B + C), int(A + C + B), int(B + A + C)
        BCA, CAB, CBA = int(B + C + A), int(C + A + B), int(C + B + A)
        DL3 = [ABC, ACB, BAC, BCA, CAB, CBA]
        for N1, N3, N5 in permutations(DL3, 3):
          if N1 + N3 + N5 >= 2000:
            continue
          
          # Form possible 2-digit numbers for N2 and N4
          AB, AC, BA = int(A + B), int(A + C), int(B + A)
          BC, CB, CA = int(B + C), int(C + B), int(C + A) 
          DL2 = [AB, AC, BA, BC, CB, CA]
          for N2, N4 in permutations(DL2, 2):
            # check 1st digits of N1 and N2 are different
            if N1 // 100 == N2 // 10:
              continue
            # Check two sums given are valid
            if N1 - N2 == N3 and N3 - N4 == N5:
              print(f"1st sum is {N1} - {N2} = {N3}")
              print(f"2nd sum is {N3} - {N4} = {N5}")
      
      # timer outside looping
      timer.stop()      
      timer.report()
      
      
      

      @ Jim: I guess it is OK to use the timer in enigma.py in the IDLE interface
      under Windows?
      I finished the timer outside all the looping and timed it
      on an I7 laptop (110 ms) and an I9 desktop (42ms)

      Like

  • Unknown's avatar

    Jim Randell 10:07 am on 30 March 2023 Permalink | Reply
    Tags: by: Jon Ashman   

    Teaser 2256: Stack of primes 

    From The Sunday Times, 11th December 2005 [link]

    The digits 0 to 9 are printed unambiguously on 10 discs and they are placed on a flat surface to form a triangular arrangement with one disc in the top row, two in the next row, three in the next row and four along the bottom row.

    No row begins with a zero, each row viewed as a single number is a prime; the sum of the digits in each row is a prime (with no two rows giving the same sum); all three corner digits are prime; and the sum of the digits around the perimeter of the triangle is prime. (Readers are reminded that 1 is not a prime).

    Draw a picture of the triangular stack.

    [teaser2256]

     
    • Jim Randell's avatar

      Jim Randell 10:11 am on 30 March 2023 Permalink | Reply

      We can use the [[ SubstitutedExpression ]] solver from the enigma.py library to solve this puzzle.

      The following run file executes in 68ms. (Internal runtime of the generated program is 3.1ms).

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      # each row is prime
      "is_prime(A)"
      "is_prime(BC)"
      "is_prime(DEF)"
      "is_prime(GHIJ)"
      
      # the sum of the digits in each row is prime
      "is_prime(B + C)"
      "is_prime(D + E + F)"
      "is_prime(G + H + I + J)"
      
      # and the sum of each row is different
      "all_different(A, B + C, D + E + F, G + H + I + J)"
      
      # all three corner digits are prime
      "is_prime(G)"
      "is_prime(J)"
      
      # the sum of the digits around the perimeter is prime
      "is_prime(45 - E)"
      
      --template="(A) (BC) (DEF) (GHIJ)"
      --solution=""
      

      Solution: The stack looks like this:

      Like

    • GeoffR's avatar

      GeoffR 12:05 pm on 30 March 2023 Permalink | Reply

      
      from enigma import is_prime, nsplit, all_different
      
      #     A
      #    B C
      #   D E F
      #  G H I J
      
      for A in (2, 3, 5, 7):
        # 2nd row of pyramid
        for BC in range(10, 100):
          if not is_prime(BC):continue
          B, C = nsplit(BC)
          if not all_different(A, B, C):continue
          if not is_prime(B + C):continue
          
          # 3rd row of pyramid
          for DEF in range(100, 999):
            if not is_prime(DEF):continue
            D, E, F = nsplit(DEF)
            if not all_different(D, E, F):continue
            if not all_different(A, B, C, D, E, F):continue
            if not is_prime(D + E + F):continue
            
            # 4th row of pyramid
            for GHIJ in range(1000, 9999):
              if not is_prime(GHIJ):continue
              G, H, I, J = nsplit(GHIJ)
              if not all_different(G, H, I, J):continue
              if not is_prime(G + H + I + J):continue
              
              # All digits 0..9 are different
              if not all_different(A,B,C,D,E,F,G,H,I,J):continue
              # all three corner digits are prime (A already prime)
              if is_prime(G) and is_prime(J): 
                # the sum of each row is different
                if not all_different(A, B + C, D + E + F, G + H + I + J):
                  continue
                # the sum of the digits around the perimeter is prime
                if is_prime(A + C + F + J + I + H + G + D + B):
                  print(f"A={A}, BC={BC}, DEF={DEF}, GHIJ={GHIJ}")
      
      # A=2, BC=61, DEF=487, GHIJ=5903
      
      

      @Jim: Why are 6 and 9 underlined in your diagram of the pyramid?

      Like

      • Jim Randell's avatar

        Jim Randell 12:14 pm on 30 March 2023 Permalink | Reply

        @GeoffR: This is to disambiguate the 6 and 9 discs, otherwise one could be used the wrong way up. The other digits don’t have this issue.

        Like

  • Unknown's avatar

    Jim Randell 9:14 am on 28 March 2023 Permalink | Reply
    Tags: ,   

    Teaser 2707: Good Times 

    From The Sunday Times, 10th August 2014 [link] [link]

    The Good Times bus company has a route from Timesboro to Teaseboro which passes through six other towns on its direct route. The distances from each town to the next are different whole numbers of miles, the largest of the seven distances being six miles more than the shortest. I worked out the average of those seven distances and my friend worked out the average distance between any two of the eight towns. Our answers used the same two digits but in reverse orders.

    How far is it from Timesboro to Teaseboro?

    [teaser2707]

     
    • Jim Randell's avatar

      Jim Randell 9:15 am on 28 March 2023 Permalink | Reply

      I am assuming all distances are measured along the road.

      The 7 distances between the towns are different whole numbers, where the largest of the values is 6 more than the smallest. So the distances, when ordered numerically, must form a sequence of 7 consecutive integers.

      We will denote them (x − 3, …, x + 3).

      The total of these distances (and the required answer) is: 7x.

      And the mean distance is: x.

      We can choose a 2-digit value for x (the mean of the distances), and then look for an arrangement of the distances that gives the mean pairwise distance to be the reverse of x.

      This Python program runs in 205ms.

      Run: [ @replit ]

      from enigma import (C, irange, multiset, subsets, tuples, printf)
      
      # count the number of pairwise distances between the 8 towns
      n = C(8, 2)
      
      # count the number of times each segment occurs in the total distance
      m = multiset()
      for k in irange(1, 7):
        for s in tuples(irange(7), k):
          m.update(s)
      
      # calculate total distance between pairs in <ss>
      pair_dist = lambda ss: sum(s * m[i] for (i, s) in enumerate(ss))
      
      # consider the digits a, b
      for (a, b) in subsets(irange(1, 9), size=2, select='P'):
        # x is the mean of the segment lengths
        # y is the reverse of x
        (x, y) = (10 * a + b, 10 * b + a)
      
        # choose an order for the segments
        for ss in subsets(irange(x - 3, x + 3), size=7, select='P'):
          if pair_dist(ss) == n * y:
            # total distance is 7x
            printf("d={d} [x={x} y={y} ss={ss}]", d=7 * x)
            break  # we only need one example
      

      Solution: The total distance is 98 miles.

      For example the distances could be: (13, 15, 12, 11, 14, 16, 17) miles, to give a total distance of 98 miles. (But there are other arrangements of these distances which also work).

      The mean distance is 98/7 = 14 miles.

      If we label the distances (a, b, c, d, e, f, g) then there are 28 ways of choosing pairs of distances, and the total of these pairwise distances is given by:

      T = 7(a + g) + 12(b + f) + 15(c + e) + 16d

      (In the program this is determined by lines 6-10).

      So in this case we get: T = 1148, and so the pairwise mean is: 1148/28 = 41 miles.


      And we can use this analysis to produce a faster program.

      If the arrangement of distances is:

      (x + a, x + b, x + c, x + d, x + e, x + f)

      where (a, …, f) are the offsets (−3, …, 3) in some order.

      Then the pairwise mean is given by:

      3x + (7(a + g) + 12(b + f) + 15(c + e) + 16d)/28

      And so we can find arrangements of (a, …, f) that are multiples of 28, and these will give integer pairwise distances.

      We can then look for instances where a 2-digit mean distance (= x) along with a viable set of offsets gives a pairwise mean that is the reverse of x.

      Run: [ @replit ]

      from enigma import (irange, subsets, dot, div, printf)
      
      # find ordering of offsets, that give an integer pairwise mean
      rs = dict()
      for vs in subsets(irange(-3, +3), size=7, select='P'):
        k = div(dot([7, 12, 15, 16, 15, 12, 7], vs), 28)
        if k is not None:
          rs[k] = vs
      
      # consider digit reversed pairs of 2-digit numbers
      for (a, b) in subsets(irange(1, 9), size=2, select='P'):
        # x is the mean of the segment lengths
        # y is the reverse of x
        (x, y) = (10 * a + b, 10 * b + a)
      
        # look for corresponding offsets
        for (k, vs) in rs.items():
          if 3 * x + k == y:
            ss = list(x + v for v in vs)
            printf("d={d} [x={x} y={y} ss={ss}]", d=7 * x)
      

      Like

  • Unknown's avatar

    Jim Randell 2:16 pm on 26 March 2023 Permalink | Reply
    Tags: by: J. G. Pratt   

    Brainteaser 1159: Number unobtainable 

    From The Sunday Times, 18th November 1984 [link]

    Our little village is furious because they have ‘improved’ our telephones. In other words they closed our local exchange, added several extra non-zero figures to the beginning of each person’s old number, and then put us into Hogglestock’s exchange. We used to have only a few figures in each of our phone numbers but now with umpteen of them (more than three times as many as before) it is quite beyond me to remember any of the new ones.

    However, I thought I had found a way to work out my own number. My old one was a prime, its digits were different and they came in ascending order. The amazing thing is that those facts are true of the added figures as well, and also of the complete new number! But all the commotion has left me in such a state that I’m not sure if I can work out my number correctly.

    What is my new number?

    [teaser1159]

     
    • Jim Randell's avatar

      Jim Randell 2:17 pm on 26 March 2023 Permalink | Reply

      The digits of the phone number are in increasing order, but do not start with 0, so there can be no zeros in the entire number.

      Also the new number has more than 3× the number of digits that the old one. So the added prefix is more than twice the length of the original number.

      If the original number only has 1 digit, then the added prefix must have more than 2 (i.e. 3 or more digits).

      If the digits are all different then the final number can have no more than 9 digits, and it must be more than 3× longer than the original number, so the original number can only have 1 or 2 digits. So the prefix must have 3 – 8 digits.

      This Python program runs in 58ms. (Internal runtime is 2.1ms).

      Run: [ @replit ]

      from enigma import (irange, subsets, nconcat, is_prime, wrap, cache, printf)
      
      # generate k-length increasing numbers, that satisfy fn
      # return (<n>, <first-digit>)
      @cache
      @wrap(list)
      def generate(k, fn=is_prime):
        for ds in subsets(irange(1, 9), size=k, select='C'):
          n = nconcat(ds)
          if fn(n):
            yield (n, ds[0])
      
      # consider p-length prefix
      for p in irange(3, 8):
        for (pn, _) in generate(p):
          pd = pn % 10
          # consider s-length suffix
          for s in irange(1, (p - 1) // 2):
            for (sn, sd) in generate(s):
              if not (pd < sd): continue
              n = pn * 10**s + sn
              if not is_prime(n): continue
              # output solution
              printf("{pn} + {sn} -> {n}")
      

      Solution: The new number is: 1234789.

      The prefix is 12347, and the original number was 89.

      It doesn’t seem that hard to remember. Just dial the numbers in order, skipping 56.

      Like

    • Frits's avatar

      Frits 10:47 am on 27 March 2023 Permalink | Reply

      As we have more than three times as many figures as before and the original number had multiple digits we can conclude that the original number must have two digits (no zeroes allowed) .

      from enigma import SubstitutedExpression, is_prime
      
      # invalid digit / symbol assignments
      d2i = dict()
      for d in range(0, 10):
        vs = set()
        if d == 0: vs.update('CDEFGHI')
        # G and I must be odd
        if d % 2 == 0: vs.update('GI')
        # ABCDEFGHI is increasing
        for i in range(1, 9):
          # set maximum
          if d > i: vs.update("ABCDEFGHI"[i-1])
          # set minimum
          if d < i - 1: vs.update("ABCDEFGHI"[i])
          
        d2i[d] = vs
      
      if 0:
        print("Allowed values")
        for s in "ABCDEFGHI":   
          print(s, ":", end=" ")
          for k, vs in d2i.items():
            if s not in vs:
              print(k, end="")
          print()    
        
      def check(n):
        # n has to be a prime number
        if not is_prime(n): return False
        
        # with increasing digits
        s = str(n)
        if "".join(sorted(s)) != s: return False
        return True
      
      # the alphametic puzzle
      p = SubstitutedExpression(
        [
          "check(HI)",
          "check(ABCDEFG)",
          "check(ABCDEFGHI)",
          "A == 0 or A < B",
        ],
        answer="ABCDEFGHI",
        d2i=d2i,
        # only A and B may be the same digit (if zero)
        distinct=("ACDEFGHI", "BCDEFGHI"),
        env=dict(check=check),
        #reorder=0,
        verbose=0,    # use 256 to see the generated code
      )
      
      # print answers
      for (_, ans) in p.solve():
        print(f"my new number is {ans}")   
      

      Like

      • Jim Randell's avatar

        Jim Randell 11:40 am on 27 March 2023 Permalink | Reply

        If we accept that the suffix is 2-digits, then the prefix must 5- or 6-digits (neither 1234567 nor 123456789 is prime), so we can just consider 8-digit numbers that have an optional leading zero:

        Run: [ @replit ]

        #! python3 -m enigma -rr
        
        SubstitutedExpression
        
        # prefix is ABCDEF (A may be 0), suffix is GH
        --invalid=""
        
        # digits are increasing
        "A < B" "B < C" "C < D" "D < E" "E < F" "F < G" "G < H"
        
        # suffix, prefix and entire number are prime
        "is_prime(GH)"
        "is_prime(ABCDEF)"
        "is_prime(ABCDEFGH)"
        
        # output solution
        --answer="ABCDEFGH"
        

        Like

  • Unknown's avatar

    Jim Randell 4:32 pm on 24 March 2023 Permalink | Reply
    Tags:   

    Teaser 3157: End-of-season analysis 

    From The Sunday Times, 26th March 2023 [link] [link]

    In our local football league each team plays each other once every season. The winner of a game is awarded one point, the loser no points and each team half a point if the game is drawn.

    In the end-of-season analysis, it was found that for each team, exactly half of the points that they were awarded had been awarded for their games against the 15 teams with the lowest number of points.

    How many teams were in the league?

    [teaser3157]

     
    • Jim Randell's avatar

      Jim Randell 5:40 pm on 24 March 2023 Permalink | Reply

      Here is a non-constructive analysis, that finds the only viable answer, but doesn’t show that there is an allocation of points that allows the puzzle to work:

      Equivalently we work in “half-points”, with 2 for a win and 1 for a draw.

      If there are (k + 15) teams (i.e. the bottom 15 teams and k top teams), then the table of points is a (k + 15) × (k + 15) matrix, such that:

      pts(i, i) = 0
      pts(i, j) + pts(j, i) = 2, where i ≠ j

      The sum of the entries in each row in the matrix gives the total points for the corresponding team, and last 15 rows represent the matches against the bottom 15 teams, whereas the first k rows represent the matches against the top k teams, so for each row these sections must have the same sum.

      In the 15 × 15 bottom right sub-matrix, we get an upper triangle with T(14) = 105 elements that sum to: B.

      And a lower triangle that sums to: 210 − B.

      Together the total number of points for the sub-matrix is 210. And this is the total number of points gained by the bottom 15 teams in their matches among themselves. And this accounts for half the points gained by the bottom 15 teams, so the total number of points gained by the bottom 15 teams against the top k teams is also 210, and this the total value of the entries in the k × 15 sub-matrix on the bottom left.

      Hence in total the bottom 15 teams scored 420 points. And the lowest number of points that the best of the bottom teams can achieve is 28 points. (If they all have the same number of points. If any of them score less than this the best of the bottom teams must have more points to compensate).

      Now, considering the top right 15 × k sub-matrix, the entries in this must sum to 30k − 210, and this must have the same value as the k × k top left sub-matrix. Which has entries that sum to 2 T(k − 1) = k² − k.

      Hence:

      k² − k = 30k − 210
      k² − 31k + 210 = 0
      (k − 10)(k − 21) = 0

      So: k = 10; or k = 21.

      In the case k = 10, the total number of points shared between the top 10 teams is 180, so the largest amount the team in 10th place can have is 18 points, but this is not enough.

      However if k = 21, the total number of points shared between the top 21 teams is 840, and the largest amount the team in 21st place can have is 40 points, and this is larger than the lowest number of points the best of the bottom teams can achieve.

      So this case gives the only viable answer to the puzzle. And I wrote a MiniZinc model to verify that a viable table can be constructed.

      Solution: There were 36 teams in the league.

      Here is one possible allocation of half-points:

       1: [0 0 0 0 2 2 2 2 2 0 0 2 0 2 2 2 2 2 2 2 2   0 2 2 2 2 2 2 2 2 2 2 2 2 2 2] → 56
       2: [2 0 0 2 0 2 0 0 2 2 1 0 2 0 0 2 2 2 2 2 2   2 1 0 2 2 2 2 2 0 2 2 2 2 2 2] → 50
       3: [2 2 0 0 0 0 2 0 1 2 1 2 0 0 1 2 2 2 2 2 2   2 2 0 0 2 2 2 2 2 2 1 2 2 2 2] → 50
       4: [2 0 2 0 0 0 0 0 0 0 0 2 2 2 2 0 1 0 2 2 2   0 2 2 2 0 0 2 0 2 1 2 0 2 2 2] → 38
       5: [0 2 2 2 0 0 2 0 2 2 0 0 0 2 0 0 0 0 2 2 1   2 0 0 0 0 2 2 2 1 2 2 2 2 2 0] → 38
       6: [0 0 2 2 2 0 0 0 0 0 1 0 1 0 0 2 2 2 1 2 2   2 2 2 1 0 0 2 2 0 0 2 2 2 0 2] → 38
       7: [0 2 0 2 0 2 0 0 0 0 0 2 2 1 0 0 0 2 2 2 2   2 2 2 0 2 1 0 2 2 2 1 1 0 2 0] → 38
       8: [0 2 2 2 2 2 2 0 0 0 0 0 0 0 2 1 0 2 0 2 0   2 2 2 0 2 2 0 2 2 1 2 0 0 0 2] → 38
       9: [0 0 1 2 0 2 2 2 0 0 0 0 0 0 2 0 2 0 2 2 2   2 0 0 2 2 0 2 1 0 2 2 2 0 2 2] → 38
      10: [2 0 0 2 0 2 2 2 2 0 0 0 0 0 2 0 0 0 2 1 2   2 2 2 2 2 2 0 2 1 2 0 2 0 0 0] → 38
      11: [2 1 1 2 2 1 2 2 2 2 0 0 0 0 0 0 0 2 0 0 0   0 0 2 2 1 0 2 2 2 0 0 2 2 2 2] → 38
      12: [0 2 0 0 2 2 0 2 2 2 2 0 0 0 1 2 0 0 0 0 2   2 0 2 2 0 2 2 0 0 0 1 2 2 2 2] → 38
      13: [2 0 2 0 2 1 0 2 2 2 2 2 0 0 0 0 0 0 0 0 2   2 2 0 0 2 2 2 2 0 2 1 0 2 0 2] → 38
      14: [0 2 2 0 0 2 1 2 2 2 2 2 2 0 0 0 0 0 0 0 0   0 0 2 2 2 2 0 1 2 0 0 2 2 2 2] → 38
      15: [0 2 1 0 2 2 2 0 0 0 2 1 2 2 0 0 2 1 0 0 0   2 2 1 2 0 0 2 0 0 2 2 0 2 2 2] → 38
      16: [0 0 0 2 2 0 2 1 2 2 2 0 2 2 2 0 0 0 0 0 0   2 1 2 2 0 0 1 0 2 2 2 0 1 2 2] → 38
      17: [0 0 0 1 2 0 2 2 0 2 2 2 2 2 0 2 0 0 0 0 0   2 2 0 1 2 2 0 0 2 2 2 0 0 2 2] → 38
      18: [0 0 0 2 2 0 0 0 2 2 0 2 2 2 1 2 2 0 0 0 0   0 1 2 2 2 0 0 1 1 0 2 2 2 2 2] → 38
      19: [0 0 0 0 0 1 0 2 0 0 2 2 2 2 2 2 2 2 0 0 0   0 0 2 2 0 2 0 2 2 1 0 2 2 2 2] → 38
      20: [0 0 0 0 0 0 0 0 0 1 2 2 2 2 2 2 2 2 2 0 0   0 1 0 0 2 2 2 2 2 0 0 2 2 2 2] → 38
      21: [0 0 0 0 1 0 0 2 0 0 2 0 0 2 2 2 2 2 2 2 0   0 2 2 1 2 2 2 0 2 2 2 2 0 0 0] → 38
      
      22: [2 0 0 2 0 0 0 0 0 0 2 0 0 2 0 0 0 2 2 2 2   0 0 2 2 0 1 0 0 0 1 2 2 2 2 2] → 32
      23: [0 1 0 0 2 0 0 0 2 0 2 2 0 2 0 1 0 1 2 1 0   2 0 0 0 2 2 0 0 2 0 2 2 2 0 2] → 32
      24: [0 2 2 0 2 0 0 0 2 0 0 0 2 0 1 0 2 0 0 2 0   0 2 0 1 0 0 0 2 0 0 2 2 2 2 2] → 30
      25: [0 0 2 0 2 1 2 2 0 0 0 0 2 0 0 0 1 0 0 2 1   0 2 1 0 0 0 0 0 0 2 2 2 2 2 2] → 30
      26: [0 0 0 2 2 2 0 0 0 0 1 2 0 0 2 2 0 0 2 0 0   2 0 2 2 0 0 0 0 1 0 0 2 2 2 2] → 30
      27: [0 0 0 2 0 2 1 0 2 0 2 0 0 0 2 2 0 2 0 0 0   1 0 2 2 2 0 0 0 2 0 0 2 0 2 2] → 30
      28: [0 0 0 0 0 0 2 2 0 2 0 0 0 2 0 1 2 2 2 0 0   2 2 2 2 2 2 0 0 0 2 0 1 0 0 0] → 30
      29: [0 0 0 2 0 0 0 0 1 0 0 2 0 1 2 2 2 1 0 0 2   2 2 0 2 2 2 2 0 0 0 0 0 1 2 0] → 30
      30: [0 2 0 0 1 2 0 0 2 1 0 2 2 0 2 0 0 1 0 0 0   2 0 2 2 1 0 2 2 0 0 0 0 2 0 2] → 30
      31: [0 0 0 1 0 2 0 1 0 0 2 2 0 2 0 0 0 2 1 2 0   1 2 2 0 2 2 0 2 2 0 0 0 0 0 2] → 30
      32: [0 0 1 0 0 0 1 0 0 2 2 1 1 2 0 0 0 0 2 2 0   0 0 0 0 2 2 2 2 2 2 0 0 0 2 0] → 28
      33: [0 0 0 2 0 0 1 2 0 0 0 0 2 0 2 2 2 0 0 0 0   0 0 0 0 0 0 1 2 2 2 2 0 0 2 2] → 26
      34: [0 0 0 0 0 0 2 2 2 2 0 0 0 0 0 1 2 0 0 0 2   0 0 0 0 0 2 2 1 0 2 2 2 0 0 2] → 26
      35: [0 0 0 0 0 2 0 2 0 2 0 0 2 0 0 0 0 0 0 0 2   0 2 0 0 0 0 2 0 2 2 0 0 2 0 0] → 20
      36: [0 0 0 0 2 0 2 0 0 2 0 0 0 0 0 0 0 0 0 0 2   0 0 0 0 0 0 2 2 0 0 2 0 0 2 0] → 16
      


      In general if there are n bottom teams we are looking for integer roots of:

      k² − (2n + 1)k + n(n − 1) = 0, where k ≥ n

      Which gives solutions:

      n = i (i + 1) / 2 = tri(i) [number of bottom teams]
      k = (i + 1) (i + 2) / 2 = tri(i + 1) [number of top teams]
      t = (i + 1)² [total number of teams in the league]
      for positive integers i

      And the answer to this puzzle is found when i = 5.

      Like

  • Unknown's avatar

    Jim Randell 10:34 am on 23 March 2023 Permalink | Reply
    Tags:   

    Teaser 2710: Sterling achievement 

    From The Sunday Times, 31st August 2014 [link] [link]

    Some people were asked to donate one pound each to charity. They all used only “silver” coins (5p, 10p, 20p, 50p) to make up their pound and no two of them used the same combination. I also donated a pound using silver coins, but it was inevitable that my combination of coins would be a repeat of one already used. When all the coins were sorted into the different denominations we found that we had a whole number of pounds in each denomination.

    Which coins did I use to make my pound?

    [teaser2710]

     
    • Jim Randell's avatar

      Jim Randell 10:34 am on 23 March 2023 Permalink | Reply

      There are a couple of routines in enigma.py to calculate ways of expressing amounts using specified denominations (e.g. coins or stamps). In this case using the more sophisticated [[ Denominations() ]] algorithm gives a (slightly) faster solution than the simple [[ express() ]] function.

      This Python program runs in 51ms. (Internal runtime is 261µs).

      Run: [ @replit ]

      from enigma import (Denominations, ceil, ediv, printf)
      
      # express 100 using denominations of 5, 10, 20, 50
      ds = (5, 10, 20, 50)
      qss = Denominations(ds).express(100)  # or: express(100, ds)
      
      # sum the quantities
      tqs = list(sum(qs) for qs in zip(*qss))
      
      # determine extra coins required to make the total of each
      # denomination a whole number of pounds
      for (d, q) in zip(ds, tqs):
        t = d * q
        x = ceil(t, 100) - t
        n = ediv(x, d)
        printf("{n} x {d}p = {x}p [{q} x {d}p coins = {t}p]")
      

      Solution: 6× 5p; 3× 10p; 2× 20p.

      There are 49 different ways to make 100p using 5p, 10p, 20p, 50p.

      And if we sum them all we get:

      294× 5p = 1470p
      147× 10p = 1470p
      63× 20p = 1260p
      14× 50p = 700p

      The setters donation makes the total of the 5p and 10p coins up £15, and the 20p coins up to £13, and the 50p coins were already at £7. Making a grand total of £50.

      Like

  • Unknown's avatar

    Jim Randell 9:29 am on 21 March 2023 Permalink | Reply
    Tags:   

    Teaser 2701: Flipping ages! 

    From The Sunday Times, 29th June 2014 [link] [link]

    At a recent family party the ages of the ten people present formed an arithmetic progression (i.e. the difference between each person’s age in years and the age of the next oldest person was always the same). My age was like my wife’s but with the digits in reverse order. Likewise, my sister’s age was the reverse of my mother’s, my son’s age was the reverse of my grandson’s, and my daughter’s age was the reverse of my granddaughter’s. Furthermore, the product of my brother’s age and the age of his son equalled the year of birth of one of the people at the party.

    What were the ages of my brother, my sister and myself (in that order)?

    [teaser2701]

     
    • Jim Randell's avatar

      Jim Randell 9:30 am on 21 March 2023 Permalink | Reply

      This Python program chooses the 4 pairs of ages that are the mutually reversed, and then extends this set of 8 numbers to 10 numbers in arithmetic progression (if possible). It then allocates the given relationships between the numbers (it looks for viable parent/child ages where the parent is between 16 and 50 years older than the child).

      Runtime is 282ms.

      Run: [ @replit ]

      from enigma import (irange, subsets, union, tuples, mgcd, call, divisor, div, decompose, diff, printf)
      
      # collect pairs that are the reverse of each other
      pairs = set((10 * a + b, 10 * b + a) for (a, b) in subsets(irange(1, 9), size=2))
      
      # generate arithmetic progressions based on the given numbers
      def generate(ns):
        ns = sorted(ns)
        (a, z) = (ns[0], ns[-1])
        # find potential common differences
        for d in divisor(call(mgcd, (y - x for (x, y) in tuples(ns, 2)))):
          # calculate number of terms
          k = div(z - a, d)
          if k is None or k > 9: continue
          k += 1
          # pad the sequence to bring it to 10 terms
          for (pa, pz) in decompose(10 - k, 2, increasing=0, sep=0, min_v=0):
            # construct the new sequence
            ns = list(irange(a - pa * d, z + pz * d, step=d))
            yield ns
      
      # check for viable parent, child age
      check = lambda parent, child: 15 < parent - child < 51
      
      # choose 4 pairs
      for ps in subsets(pairs, size=4):
        ss = union(ps)
        for ns in generate(ss):
          # the additional ages are brother and nephew
          (nep, bro) = diff(ns, ss)
          if not check(bro, nep): continue
          # and their product is the birth year of one of the party
          year = bro * nep
          x = 2014 - year
          if not { x, x - 1 }.intersection(ns): continue
      
          # choose the pairs
          for ((sis, mum), (gson, son), (gdtr, dtr), other) in subsets(ps, size=4, select="P"):
            # check parent/child relationships
            if not (check(mum, sis) and check(mum, bro)): continue
            if not (check(son, gson) or check(dtr, gson)): continue
            if not (check(son, gdtr) or check(dtr, gdtr)): continue
            # the remaining pair is (me, wife) (in some order)
            for (me, wife) in subsets(other, size=2, select="P"):
              # check parent/child relationships
              if not (check(mum, me) and check(me, son) and check(me, dtr)): continue
              if not (check(wife, son) and check(wife, dtr)): continue
              # output solution
              printf("{ns}")
              printf("-> me={me} wife={wife}")
              printf("-> sis={sis} mum={mum}")
              printf("-> son={son} gson={gson}")
              printf("-> dtr={dtr} gdtr={gdtr}")
              printf("-> bro={bro} nep={nep}; year={year}")
              printf()
      

      Solution: Brother = 60. Sister = 69. Self = 78.

      The arithmetic progression is (15, 24, 33, 42, 51, 60, 69, 78, 87, 96).

      And the pairs are:

      me = 78; wife = 87
      sister = 69; mother = 96
      child = 51; grandchild = 15
      child = 42; grandchild = 24
      brother = 60; nephew = 33

      And the product of the ages of the brother and nephew give 1980. And if the 33 year-old nephew has not yet celebrated his birthday in 2014, his year of birth is 1980.

      But we can’t tell how the child/grandchild pairs correspond to the son/grandson and daughter/granddaughter pairs.

      Like

  • Unknown's avatar

    Jim Randell 7:46 am on 19 March 2023 Permalink | Reply
    Tags:   

    Brainteaser 1057: Palindromic primes 

    From The Sunday Times, 31st October 1982 [link]

    Teacher was introducing his class to the binary system of notation (wherein the unit values attaching to successive digits from right to left of any integer are 1, 2, 4, 8, 16, etc., as against 1, 10, 100, 1000, etc., in the decimal system).

    He went on to explain that many arithmetical relationships are equally valid in both the binary and decimal systems. And gave as the simplest example:

    10 × 11 = 110

    which in the binary system represents 2 × 3 = 6, pointing out this difference however – that while both factors 10 and 11 are primes in the binary system, only 11 is prime in the decimal system.

    Developing this theme he observed that very often such relationships could be described as “pan-palindromic”, in the sense that both of the factors, as well as their product, are numerical palindromes (i.e. each of the three integers reads the same forwards as backwards). His first example was:

    1001 × 10101 = 10111101

    (which in the binary system represents 9 × 21 = 189), and he pointed out how this time neither factor was a prime using either the binary or decimal system (being patently divisible by 11 and 3 respectively).

    He contrasted this with another example:

    111 × 1001001 = 111111111

    which in the binary system represents: 7 × 73 = 511, where both factors are primes in the binary system, but neither of them are in the decimal system (both being divisible by 3).

    To test how well his pupils were taking in all this, he told them to proceed on their own and write down any binary palindromes they could find, of less than twelve digits, which simultaneously, in both binary and decimal systems, factorised into just two different palindromic primes.

    What should they have written down?

    [teaser1057]

     
    • Jim Randell's avatar

      Jim Randell 7:47 am on 19 March 2023 Permalink | Reply

      This Python program considers numbers that are binary palindromes (with < 12 digits), and that have exactly 2 different prime factors, which are themselves binary palindromes. It then checks that if the multiplication sum is interpreted in decimal, instead of binary, that it still works, and the decimal factors are also prime.

      Total runtime is 64ms. (Internal runtime is 713µs).

      Run: [ @replit ]

      from enigma import (irange, inf, nsplit, rev, nconcat, factor, is_npalindrome, is_prime, printf)
      
      # generate numbers that are binary palindromes < 12 digits
      def generate():
        for n in irange(1, inf):
          # split n into digits
          ds = nsplit(n, base=2, fn=list)
          k = len(ds)
          if k > 6: break
          # mirror the digits to make a palindrome
          ds.extend(rev(ds))
          if k < 6: yield nconcat(ds, base=2)
          ds.pop(k)
          yield nconcat(ds, base=2)
      
      # start with a number that is a binary palindrome
      for n in generate():
        # find the prime factorisation
        fs = factor(n)
        if not (len(fs) == 2 and len(set(fs)) == 2): continue
        (a, b) = fs
        # check the factors are themselves binary palindromes
        if not (is_npalindrome(a, base=2) and is_npalindrome(b, base=2)): continue
        # check the multiplication works if the binary factorisation is interpreted as decimal
        (nx, ax, bx) = (nconcat(nsplit(x, base=2), base=10) for x in (n, a, b))
        if not (ax * bx == nx): continue
        # check the factors are prime if interpreted as decimal
        if not (is_prime(ax) and is_prime(bx)): continue
        # output solution
        printf("{a:b} * {b:b} = {n:b} [base 2] -> {a} * {b} = {n} [base 10]")
      

      Solution: The sum is: 11 × 101 = 1111.

      In decimal both 11 and 101 are prime.

      In binary this corresponds to: 3 × 5 = 15, and both 3 and 5 are prime.

      Like

  • Unknown's avatar

    Jim Randell 4:30 pm on 17 March 2023 Permalink | Reply
    Tags:   

    Teaser 3156: Balancing act 

    From The Sunday Times, 19th March 2023 [link] [link]

    A circus elephant standing on one end of a see-saw pivoted at the centre was balanced by a troupe of fewer than 20 acrobats, each of equal weight, standing on the other end. The elephant moved forwards and several acrobats jumped off to maintain balance. The elephant moved backwards and some of them climbed back on to the end to rebalance.

    The elephant always moved a prime number of feet and there was always a prime number of acrobats on the see-saw. If I told you how far backwards the elephant moved you could work out the numbers of acrobats.

    (In equilibrium, the product of weight and distance from pivot point must be the same on both sides).

    How far did the elephant move backwards, and how many acrobats are there in the troupe?

    [teaser3156]

     
    • Jim Randell's avatar

      Jim Randell 5:24 pm on 17 March 2023 Permalink | Reply

      I think it is necessary to assume that the elephant did not return to its original position. (i.e. “some of them” is interpreted as “some (but not all) of them”).

      So we suppose that we start off with a see-saw of length 2d and a unit weight acrobats on one end (a is a prime number, less than 20). They are balanced by an elephant of weight a on the other end.

      The elephant then moves forwards (towards the pivot) p feet (p is a prime number), and some of the acrobats jump off, leaving b acrobats balancing the elephant (b is a prime number, less than a):

      a . (d − p) = b . d

      The elephant then moves backwards (away from the pivot) q feet (q is a prime number, less than p), and some of the acrobats remount to make c acrobats balancing the elephant (c is a prime number, between b and a):

      a . (d − p + q) = c . d

      From these two equations we can determine p in terms of q and a, b, c:

      p . (c − b) = q . (a − b)

      There are only a certain number (a, b, c) values, so we can look at possible values for q, and see if there is only a single set of (a, b, c) values that give a viable value of p.

      This Python program runs in 62ms. (Internal runtime is 285µs).

      Run: [ @replit ]

      from enigma import (primes, subsets, div, inf, fdiv, printf)
      
      # collect possible (a, b, c) numbers of acrobats
      abcs = list((a, b, c) for (b, c, a) in subsets(primes.between(2, 20), size=3))
      
      # consider the distance the elephant moves back = q
      for q in primes.irange(2, inf):
        # find viable p values
        ps = list()
        for (a, b, c) in abcs:
          p = div(q * (a - b), c - b)
          if p and p > q and p in primes:
            ps.append((a, b, c, p))
        if len(ps) == 1:
          (a, b, c, p) = ps[0]
          printf("q={q} -> a={a} b={b} c={c} p={p}; d={d:g}", d=fdiv(a * q, c - b))
          break
      

      Solution: The elephant moved backwards 11 ft. There are 19 acrobats in the troupe.

      The see-saw is 19 feet either side of the pivot (i.e. end to end length is 38 feet).

      And initially there are 19 acrobats on one end, balanced by an elephant that weighs the same as 19 acrobats on the other end.

      The elephant moves forward by 17 ft, so it is 2 ft from the pivot, and this is balanced by 2 acrobats 19 ft from the pivot (19 × 2 = 2 × 19).

      The elephant then moves backwards by 11 ft, so it is 13 ft from the pivot. And this is balanced by 13 acrobats 19 ft from the pivot (19 × 13 = 13 × 19).


      There are 16 candidate solutions.

      q=2 ⇒ 6 candidates
      q=3 ⇒ 6 candidates
      q=5 ⇒ 3 candidates
      q=11 ⇒ 1 candidate

      So the answer is found from q = 11.

      Like

      • Jim Randell's avatar

        Jim Randell 11:53 am on 18 March 2023 Permalink | Reply

        Even shorter (and exhaustive):

        q / p = (c − b) / (a − b)

        and both p and q are primes, so can be determined from their ratio.

        Run: [ @replit ]

        from enigma import (primes, subsets, fraction, filter_unique, item, fdiv, printf)
        
        # generate candidates (p, q, a, b, c)
        def candidates():
          for (b, c, a) in subsets(primes.between(2, 20), size=3):
            (q, p) = fraction(c - b, a - b)
            if primes.is_prime(q) and primes.is_prime(p):
              yield (p, q, a, b, c)
        
        # find solutions unique by q (= item 1)
        for (p, q, a, b, c) in filter_unique(candidates(), item(1)).unique:
            printf("q={q} -> a={a} b={b} c={c} p={p}; d={d:g}", d=fdiv(a * q, c - b))
        

        Like

    • Jim Randell's avatar

      Jim Randell 8:27 am on 18 March 2023 Permalink | Reply

      @GeoffR: I think 5 is also a prime.

      And there are additional candidate solutions where the length of the see-saw is not an even integer number of feet. But the length of the see-saw can be eliminated from the equations to keep the remaining variables integers. (Although it would be possible to also specify the see-saw length as an integer number of inches).

      Like

    • GeoffR's avatar

      GeoffR 10:00 am on 18 March 2023 Permalink | Reply

      
      from itertools import permutations
      from enigma import is_prime
      
      from collections import defaultdict
      nums = defaultdict(list)
      
      primes = (2, 3, 5, 7, 11, 13, 17, 19)
      
      for acrobats in permutations(primes, 3):
        # A1 = full acrobat troupe
        # A2 = acrobats after elephant moves forward
        # A3 = acrobats after elephant then moves backwards
        A1, A2, A3 = acrobats
        if A1 > A2 and A1 > A3 and A2 < A3:
          # for intial balancing of elephant v. troupe
          # elephants weight = weight of acrobat troupe
          E = A1
          # assume max. half seesaw length = 25 ft.
          for dist in permutations(range(1, 26), 3):
            half_see_saw, p1, p2 = dist
            if p2 < p1 and is_prime(p1) and is_prime(p2):
              # Taking moments about centre pivot
              if (half_see_saw - p1) * E == half_see_saw * A2:
                if (half_see_saw - p1 + p2) * E ==  half_see_saw * A3:
                  # index dictionary on distance elephant moved backwards
                  nums[p2] += [(p1, p2, A1, A2, A3)]
      
      # find a single solution
      for k,v in nums.items():
          if len(v) == 1:
            print(f"Distance elephant moves backward = {k} ft.")
            print(f"Number of acrobats in troupe = {v[0][2]}.")
      
      

      Like

  • Unknown's avatar

    Jim Randell 8:43 am on 16 March 2023 Permalink | Reply
    Tags:   

    Teaser 2711: Megan’s house 

    From The Sunday Times, 7th September 2014 [link] [link]

    Megan’s class has been learning about primes and squares. So their teacher challenged each of them to use some of the digits 1 to 9 to write down three two-figure squares and a two-figure prime without using any digit more than once. They all succeeded in doing this and in Megan’s case the left-over digit was her house number. If you knew her house number it would be possible to work out the four two-figure numbers that she wrote down.

    What were they?

    [teaser2711]

     
    • Jim Randell's avatar

      Jim Randell 8:43 am on 16 March 2023 Permalink | Reply

      We can use the [[ SubstitutedExpression() ]] solver from the enigma.py library to find viable allocations of the 9 digits to three 2-digit squares, a 2-digit prime, and the remaining digit. We can then use the [[ filter_unique() ]] function to select those allocations that are unique by the remaining digit.

      This Python program runs in 57ms. (Internal runtime is 4.4ms).

      Run: [ @replit ]

      from enigma import (SubstitutedExpression, irange, filter_unique, item, printf)
      
      # squares: AB, CD, EF; prime: GH
      p = SubstitutedExpression(
        [
          "is_square(AB)", "is_square(CD)", "is_square(EF)", "A < C < E",
          "is_prime(GH)",
        ],
        digits=irange(1, 9),
        answer="(X, AB, CD, EF, GH)"
      )
      
      # if we knew X, we could deduce the 2-digit numbers
      rs = filter_unique(p.answers(verbose=0), item(0)).unique
      
      # output solution
      for (X, sq1, sq2, sq3, pr) in rs:
        printf("X={X} -> squares = ({sq1}, {sq2}, {sq3}), prime = {pr}")
      

      Solution: The four 2-digit numbers were: 16, 25, 49 (squares), and 83 (prime).

      So Megan’s house number is 7.

      Like

    • GeoffR's avatar

      GeoffR 11:56 am on 16 March 2023 Permalink | Reply

      from enigma import is_square, is_prime
      from itertools import permutations
      from collections import defaultdict
      nums = defaultdict(list)
      
      for p1 in permutations('123456789'):
          a, b, c, d, e, f, g, h, i = p1
          ab, cd, ef = int(a + b), int(c + d), int(e + f)
          if ab < cd < ef :
              if is_square(ab) and is_square(cd) and is_square(ef):
                  gh = int(g + h)
                  if is_prime(gh):
                      nums[i] += [(ab, cd, ef, gh)]
                  
      for k, v in nums.items():
          if len(v) == 1:
              print(f"Megan's house number was {k}.")
              print(f"Her four 2-digit numbers were {v}.")
              
      # Megan's house number was 7.
      # Her four 2-digit numbers were [(16, 25, 49, 83)].
      
      
      

      Like

    • GeoffR's avatar

      GeoffR 11:41 am on 17 March 2023 Permalink | Reply

      The basis of this teaser can be seen by printing the python dictionary in this solution, with the house number as the key. All house numbers, except 7, have multiple solutions for the four 2-digit numbers.

      8 [(16, 25, 49, 37), (16, 25, 49, 73), (25, 36, 49, 17), (25, 36, 49, 71)]
      7 [(16, 25, 49, 83)]
      9 [(25, 36, 81, 47), (25, 64, 81, 37), (25, 64, 81, 73)]
      4 [(25, 36, 81, 79), (25, 36, 81, 97)]
      6 [(25, 49, 81, 37), (25, 49, 81, 73)]
      3 [(25, 49, 81, 67), (25, 64, 81, 79), (25, 64, 81, 97)]

      Like

  • Unknown's avatar

    Jim Randell 7:47 am on 14 March 2023 Permalink | Reply
    Tags:   

    Teaser 2713: Very similar triangles 

    From The Sunday Times, 21st September 2014 [link] [link]

    Two triangles are “similar” if they are the same shape (but not necessarily the same size). I cut out two similar (but not identical) triangles from a piece of A4 paper, all the sides of the triangles being whole numbers of centimetres in length. In fact the two triangles were extra similar in the sense that two of the sides of the first triangle were the same lengths as two of the sides of the second triangle.

    What were the lengths of the sides of the smaller triangle?

    [teaser2713]

     
    • Jim Randell's avatar

      Jim Randell 7:47 am on 14 March 2023 Permalink | Reply

      (See also: Enigma 1198).

      For a triangle with sides (a, b, c) (in order), we must have: a + b > c.

      The triangle is then scaled up to give another similar triangle (f.a, f.b, f.c) (f > 1).

      And two of the sides are the same, which must be: f.a = b, f.b = c.

      Hence:

      f = b/a = c/b
      a.c = b²

      So we can choose a value for b, and then look for a, c sides that multiply to give b².

      This Python program runs in 52ms. (Internal runtime is 288µs).

      Run: [ @replit ]

      from enigma import (irange, hypot, intf, divisors_pairs, div, sqrt, printf)
      
      # area of a triangle
      def area(a, b, c):
        return 0.5 * sqrt((a + b + c) * (a + b - c) * (c + a - b) * (b + c - a))
      
      # dimensions of an A4 sheet (cm)
      (X, Y) = (21.0, 29.7)
      A4 = X * Y
      
      # choose a value for b
      for b in irange(1, intf(hypot(X, Y))):
        # calculate possible a, c values from divisors of b^2
        for (a, c) in divisors_pairs(b, 2):
          if not (a < b < c and a + b > c): continue
          # calculate d
          d = div(b * c, a)
          if d is None: continue
          if area(a, b, c) + area(b, c, d) > A4: continue
          # output solution
          printf("({a}, {b}, {c}) -> ({b}, {c}, {d})")
      

      Solution: The smaller triangle has sides of length 8, 12, 18 cm.

      And the larger triangle has sides of length 12, 18, 27 cm.

      The dimensions of the second triangle are 1.5 times those of the first.

      Here is a diagram of the two triangles, set on a sheet of A4 paper:

      Like

  • Unknown's avatar

    Jim Randell 11:51 am on 11 March 2023 Permalink | Reply
    Tags:   

    Teaser 3155: Increasing temperature 

    From The Sunday Times, 12th March 2023 [link] [link]

    I have written down an above-freezing temperature, a whole number of degrees Celsius, in which the digits are all different and are in decreasing order. I have then calculated the Fahrenheit equivalent. It is also a whole number whose digits are all different, but here the digits are in increasing order.

    If I told you the first digit of the Celsius temperature, then you would not be able to calculate the temperature. However, bearing that in mind, if I now told you the final digit of the Celsius temperature, then it would be possible to calculate it.

    You should now be able to work out the Celsius and Fahrenheit temperatures.

    What are they?

    [teaser3155]

     
    • Jim Randell's avatar

      Jim Randell 12:02 pm on 11 March 2023 Permalink | Reply

      This Python program runs in 56ms. (Internal runtime is 3.4ms).

      Run: [ @replit ]

      from enigma import (
        irange, subsets, nconcat, div, nsplit, tuples,
        filter_unique, join, printf
      )
      
      # generate temperatures (C, F) with C digits in decreasing order,
      # F digits in decreasing order, return the digits of (C, F)
      def generate():
        # make the C temperature
        for cs in subsets(irange(9, 0, step=-1), min_size=1, select='C'):
          C = nconcat(cs)
          if C == 0: continue
          # convert to Fahrenheit
          F = div(9 * C + 160, 5)
          if F is None: continue
          # check digits are in increasing order
          fs = nsplit(F)
          if not all(a < b for (a, b) in tuples(fs, 2)): continue
          # return (C, F) temperatures
          printf("[C={C} F={F}]")
          yield (cs, fs)
      
      # collect possible temperatures
      ss = list(generate())
      
      # selection function for the i'th digit of (C, F)
      digit = lambda i, k: (lambda x: x[i][k])
      
      # we cannot work out the temperature knowing the first digit of C
      ss = filter_unique(ss, digit(0, 0)).non_unique
      
      # but now we can work it out knowing the last digit of C
      ss = filter_unique(ss, digit(0, -1)).unique
      
      # output solution
      for (C, F) in ss:
        printf("answer: C={C} F={F}", C=join(C), F=join(F))
      

      Solution: The temperatures are: 75 C and 167 F.

      In total there are 7 possible temperatures:

      20 C = 68 F
      65 C = 149 F
      70 C = 158 F
      75 C = 167 F
      730 C = 1346 F
      865 C = 1589 F
      7520 C = 13568 F

      (As F = (9/5)C + 32, only C temperatures that are divisible by 5 can have a corresponding F temperature that is an integer, so the last digit of a C temperature will always be 0 or 5).

      The only temperatures that cannot be determined from the first digit are those beginning with 7, i.e.: 70, 75, 730, 7520.

      And of these there is only one that ends in 5, i.e.: 75, and so this gives us the required answer.

      Like

  • Unknown's avatar

    Jim Randell 10:57 am on 9 March 2023 Permalink | Reply
    Tags:   

    Teaser 2712: New tricks 

    From The Sunday Times, 14th September 2014 [link] [link]

    I am currently experiencing the joy of seeing Liam, my first grandchild, developing lots of new tricks. With this in mind I have written down four odd numbers and then consistently replaced different digits by different letters to give:

    SIT
    CRAWL
    WALK
    TALK

    In fact the largest of these numbers is the sum of the other three.

    Find the numerical value of TRICK.

    [teaser2712]

     
    • Jim Randell's avatar

      Jim Randell 10:57 am on 9 March 2023 Permalink | Reply

      We can use the [[ SubstitutedExpression ]] solver from the enigma.py library to solve this puzzle. And as it is a simple addition sum we can use the [[ split_sum() ]] helper function for a faster run time.

      The following run file executes in 64ms. (Internal runtime of the generated program is 117µs).

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression.split_sum
      
      "SIT + WALK + TALK = CRAWL"
      
      # T, K, L are odd
      --invalid="0,CKLSTW"
      --invalid="2|4|6|8,KLT"
      
      --answer="TRICK"
      

      Solution: TRICK = 50619.

      There are two possibilities for the original sum (but both give the same value for TRICK):

      SIT + WALK + TALK = CRAWL
      265 + 4739 + 5739 = 10743
      765 + 4239 + 5239 = 10243

      The values of A and S are 2 and 7 (in some order).

      Like

    • GeoffR's avatar

      GeoffR 1:05 pm on 9 March 2023 Permalink | Reply

      
      from itertools import permutations
      
      # Find numbers WALK and TALK 
      for p1 in permutations('1234567890', 5):
          W, A, L, K, T = p1
          if '0' in (W, T, K):continue
          if int(K) % 2 == 0 or int(T) % 2 == 0: continue
          WALK, TALK = int(W + A + L + K), int(T + A + L + K)
          q1 = set('1234567890').difference(p1)
          
          # Find numbers SIT, CRAWL and TRICK
          for p2 in permutations(q1, 4):
              S, I, C, R = p2
              if '0' in (C, S):continue
              if int(L) % 2 == 0:continue
              SIT = int(S + I + T)
              CRAWL = int(C + R + A + W + L)
              if SIT + WALK + TALK == CRAWL:
                  TRICK = int(T + R + I + C + K)
                  print(f"TRICK = {TRICK}")
                  print(f"{SIT} + {WALK} + {TALK} = {CRAWL}")
      
      # TRICK = 50619
      # 765 + 4239 + 5239 = 10243
      # 265 + 4739 + 5739 = 10743
      
      
      

      Like

    • GeoffR's avatar

      GeoffR 10:08 am on 10 March 2023 Permalink | Reply

      
      % A Solution in MiniZinc
      include "globals.mzn";
      
      var 1..9:S; var 0..9:I; var 1..9:T; var 1..9:W; var 0..9:A; 
      var 1..9:L; var 1..9:K; var 1..9:C; var 0..9:R;
      
      constraint all_different ([S, I, T, W, A, L, K, C, R]);
      constraint T mod 2 == 1 /\ L mod 2 == 1 /\ K mod 2 == 1;
      
      var 100..999: SIT = 100*S + 10*I + T;
      var 1000..9999:WALK = 1000*W + 100*A + 10*L + K;
      var 1000..9999:TALK = 1000*T + 100*A + 10*L + K;
      var 10000..99999:CRAWL = 10000*C + 1000*R + 100*A + 10*W + L;
      var 10000..99999:TRICK = 10000*T + 1000*R + 100*I + 10*C + K;
      
      constraint SIT + WALK + TALK == CRAWL;
      
      solve satisfy;
      output ["TRICK = " ++ show(TRICK) ];
      
      % TRICK = 50619
      % ----------
      % ==========
      
      

      Like

  • Unknown's avatar

    Jim Randell 10:17 am on 7 March 2023 Permalink | Reply
    Tags:   

    Teaser 2714: Group of death 

    From The Sunday Times, 28th September 2014 [link] [link]

    In a Champions League group four teams play each other twice with the top two qualifying for the next stage. If two teams are level on points then their head-to-head results determine their position. In one group, after five games each, the teams in order were A, B, C, D, each with a different number of points. At that stage team A was bound to qualify and teams B and C could still top the group. But the final two games were draws. All games had different results, no game had more than five goals, and team D had the same number of “goals for” as “goals against”. Team A scored fewer goals than any other team whilst D scored more than any other.

    What were the results of the two games B v C?

    [teaser2714]

     
    • Jim Randell's avatar

      Jim Randell 10:18 am on 7 March 2023 Permalink | Reply

      I am assuming the point are allocated as: “3 points for a win, 1 point for a draw”. (Although the puzzle also seems to work if “2 points for a win” is used instead).

      Each team plays their three opponents twice, so there are 12 matches in total.

      There are not more than 5 goals scored in any match, so possible scores are:

      drawn = (0, 0) (1, 1) (2, 2)
      won/list = (1, 0) (2, 0) (3, 0) (4, 0) (5, 0) (2, 1) (3, 1) (4, 1) (3, 2)

      This accounts for all 12 matches, so each score must be used.

      And the final 2 matches are drawn, so there is exactly 1 draw in the first 10 matches.

      Programming a constructive solution for this puzzle is quite involved. I hope that a manual solution is more fun. I used the [[ Football() ]] helper class from the enigma.py library to save on some of the coding, but things are complicated a bit by the fact each pair of teams meet twice, and there is nothing to distinguish the two games, so to avoid duplicated solutions I store the results for the two games in order with the “best” game (from the first team’s point of view) given first.

      This Python program runs in 253ms.

      Run: [ @replit ]

      from enigma import (Football, subsets, partitions, update, compare, ordered, rev, join, map2str, printf)
      
      football = Football(points=dict(w=3, d=1))
      
      # possible scores, no game had more than 5 goals
      scores = dict()
      scores['d'] = [(0, 0), (1, 1), (2, 2)]
      scores['w'] = [(1, 0), (2, 0), (2, 1), (3, 0), (3, 1), (3, 2), (4, 0), (4, 1), (5, 0)]
      scores['l'] = list(map(rev, scores['w']))
      
      # label the matches (each label corresponds to 2 matches)
      teams = "ABCD"
      matches = list(x + y for (x, y) in subsets(teams, size=2))
      
      # complete possible match pairs (additional win/lose outcomes)
      pairs = {
        '-': ['ww', 'wl', 'll'],
        'x': ['wx', 'lx'],
        'd': ['wd', 'dl'],
      }
      
      # swap pairs into canonical order
      _pair = { 'dw': 'wd', 'lw': 'wl', 'ld': 'dl' }
      pair = lambda x: _pair.get(x, x)
      
      # allocate win/lose outcomes to matches
      def allocate_matches(ks, ms):
        # are we done?
        if not ks:
          yield ms
        else:
          k = ks[0]
          v = ms.get(k, '-')
          if len(v) > 1:
            # skip this key
            yield from allocate_matches(ks[1:], ms)
          else:
            # allocate both outcomes
            for x in pairs[v]:
              yield from allocate_matches(ks[1:], update(ms, [(k, x)]))
      
      # allocate scores to matches
      def allocate_scores(ks, ms, used=set(), ss=dict()):
        # are we done?
        if not ks:
          yield (ss, used)
        else:
          k = ks[0]
          if k in ss:
            # skip this key
            yield from allocate_scores(ks[1:], ms, used, ss)
          else:
            # allocate both scores
            (a, b) = ms[k]
            for x in scores[a]:
              x_ = ordered(*x)
              if x_ in used: continue
              for y in scores[b]:
                y_ = ordered(*y)
                # remove duplicate solutions
                if (a == b and not x_ > y_) or y_ in used: continue
                yield from allocate_scores(ks[1:], ms, used.union({x_, y_}), update(ss, [(k, (x, y))]))
      
      # extract match/teams values from dict d
      def extract(d, t, fn):
        (gs, ts) = (list(), list())
        for (k, v) in d.items():
          if t in k:
            gs.extend(v)
            ts.extend([(0 if k[0] == t else 1)] * len(v))
        return fn(gs, ts)
      
      # construct the table for team t, using matches in ms
      table = lambda ms, t: extract(ms, t, football.table)
      
      # goals for/against team t, using scores in ss
      goals = lambda ss, t: extract(ss, t, football.goals)
      
      # output the matches and scores
      def output_matches(ms, ss, **kw):
        def _process(d, fn):
          d_ = dict()
          for (k, (a, b)) in d.items():
            d_[k] = a
            d_[rev(k)] = fn(b)
          return d_
        football.output_matches(_process(ms, football.swap), _process(ss, rev), **kw)
      
      # does team <x> beat team <y> in the table <tbl> and match outcomes <ms>?
      def beats(x, y, tbl, ms):
        # a team does not beat itself
        if x == y: return 0
        # can it be decided on points?
        r = compare(tbl[x].points, tbl[y].points)
        if r == 1: return 1
        if r == -1: return 0
        # can it be decided on the outcome of head-to-head games?
        (k, a, b) = ((x + y, 0, 1) if x < y else (y + x, 1, 0))
        gs = ms[k]
        (X, Y) = (football.table(gs, [a, a]), football.table(gs, [b, b]))
        r = compare(X.points, Y.points)
        if r == 1: return 1
        if r == -1: return 0
        # there may be other ways to order the teams (e.g. goal difference
        # or goals scored), but these are not mentioned
        return 0
      
      # check all possible final 2 games for required scenario
      def check(ms, f1, f2):
        # check conditions for teams A, B, C
        fB = fC = 0
        for (g1, g2) in football.games('wdl', repeat=2):
          ms_ = update(ms, [(f1, pair(ms[f1][0] + g1)), (f2, pair(ms[f2][0] + g2))])
          # calculate the final table
          tbl = dict((t, table(ms_, t)) for t in teams)
          # can A fail to qualify? (i.e. fail to beat at least 2 of the other teams)
          if not (sum(beats('A', t, tbl, ms_) for t in teams) >= 2): return False
          # can B come top? (i.e. beat the other 3 teams)
          if fB == 0 and sum(beats('B', t, tbl, ms_) for t in teams) == 3: fB = 1
          # can C come top? (i.e. beat the other 3 teams)
          if fC == 0 and sum(beats('C', t, tbl, ms_) for t in teams) == 3: fC = 1
        return (fB and fC)
      
      # find viable scenarios
      def generate():
        # choose the final 2 matches (which are draws)
        for ((t1, t2), (t3, t4)) in partitions(teams, 2):
          (f1, f2) = (t1 + t2, t3 + t4)
          ms0 = { f1: 'x', f2: 'x' }
          # of the remaining matches exactly 1 of them is a draw
          for d in matches:
            ms1 = update(ms0, [(d, ('dx' if d in ms0 else 'd'))])
            # choose win/lose outcomes for the remaining matches
            for ms2 in allocate_matches(matches, ms1):
              # calculate the table before the final 2 matches are played
              (A, B, C, D) = (table(ms2, t) for t in teams)
              if not (A.points > B.points > C.points > D.points): continue
              # in the final 2 games B or C _could_ gain the top spot
              # so A cannot be more than 3 points ahead of C
              if A.points > C.points + 3: continue
              # check all possible final 2 games
              if not check(ms2, f1, f2): continue
      
              # now include the final games (both are, in fact, drawn)
              ms3 = update(ms2, list((k, pair(ms2[k][0] + 'd')) for k in (f1, f2)))
              (A, B, C, D) = (table(ms3, t) for t in teams)
      
              # allocate scores for D
              (Ds, As) = (list(k for k in matches if t in k) for t in "DA")
              for (ss1, us1) in allocate_scores(Ds, ms3):
                # D has equal for and against (and scored more goals than any other team)
                (fD, aD) = goals(ss1, 'D')
                if fD != aD: continue
                # allocate remaining scores for A
                for (ss2, us2) in allocate_scores(As, ms3, us1, ss1):
                  # A scored fewer goals than any other team
                  (fA, aA) = goals(ss2, 'A')
                  if not (fA < fD): continue
                  # allocate remaining scores (B and C)
                  for (ss3, us3) in allocate_scores(matches, ms3, us2, ss2):
                    ((fB, aB), (fC, aC)) = (goals(ss3, t) for t in "BC")
                    if not (fA < fB < fD and fA < fC < fD): continue
      
                    # yield scenario (matches, scores, final2, goals for/against)
                    yield (ms3, ss3, (f1, f2), ((fA, aA), (fB, aB), (fC, aC), (fD, aD)))
      
      
      # look for viable scenarios
      for (ms, ss, fs, gfa) in generate():
        # output scenarios as we find them
        output_matches(ms, ss, end=None)
        printf("final games = {fs}", fs=join(fs, sep=", "))
        printf("goals for/against = {gfa}", gfa=map2str(zip(teams, gfa), arr=": ", enc=""))
        BCs = ss['BC']
        # answer is the scores in the B vs C matches
        printf("-> B vs C = {BCs}", BCs=join(BCs, sep=" "))
        printf()
      

      Solution: The scores in the B vs. C games were: 4-1 and 1-1.

      And the B vs C 1-1 draw was one of the two final games.


      There are two possible scenarios. Here is one of them:

      A vs B: 3-0, 2-0
      A vs C: 0-0, 0-4
      A vs D: 1-0, (2-2)
      B vs C: 4-1, (1-1)
      B vs D: 3-1, 2-1
      C vs D: 3-2, 0-5

      (with the two final draws indicated in brackets).

      Before the final 2 games (A vs D, B vs C) are the points are:

      A: 10 points
      B: 9 points
      C: 7 points
      D: 3 points

      If B wins their final game then they will have 12 points, and top the table (providing A does not win their final game).

      If C wins their final game then they will have 10 points, and if A loses their final game they will also have 10 points, so the top position comes down to the result of the A vs C games. And these are a draw and a win for C, so C comes out on top.

      A is guaranteed to finish higher than D, and unless C wins (and A loses) their final game also higher than C. But if C does win, then B must lose, and A will finish higher than B. So A is guaranteed to finish in the top 2 teams (and qualify for the next round).

      When the final 2 games are played they are both drawn, and the results are:

      A: 11 points; goals = 8 for, 6 against
      B: 10 points; goals = 10 for, 9 against
      C: 8 points; goals = 9 for, 12 against
      D: 4 points; goals = 11 for, 11 against

      So A and B go through to the next round.

      The other scenario plays out similarly, but the games won 3-1 and 3-2 are swapped with each other. Although this second scenario could be eliminated if the puzzle text stated that D were the only team with the same number of goals for as goals against. (As B ends up with 10 goals for and 10 goals against).

      We can use these 2 scenarios to construct further solutions where the order of the two matches played by pairs of teams are swapped over.

      Like

  • Unknown's avatar

    Jim Randell 10:20 am on 5 March 2023 Permalink | Reply
    Tags:   

    Brainteaser 1071: Particular palindromes 

    From The Sunday Times, 13th February 1983 [link]

    When either of a [particular] pair of numbers which differ by twenty-two is added to its own square, the result is a numerical palindrome between ten million and a hundred million.

    One way of finding the pair could be, of course, by extensive trial-and-error, working through the six thousand or so possibilities that lie in the given range, and electronic assistance would surely be appreciated in these circumstances.

    It should come as some relief, therefore, to puzzlers not so equipped to know that an answer can be found by performing less than twenty simple calculations (in which a calculator may nevertheless be usefully employed), none involving the square of a four-digit number, and that a further similar number of such calculations can show that no other answer is possible.

    What is the pair?

    This puzzle is included in the book The Sunday Times Book of Brainteasers (1994).

    [teaser1071]

     
    • Jim Randell's avatar

      Jim Randell 10:20 am on 5 March 2023 Permalink | Reply

      A brute force approach considers 6838 values, which is what the following Python program does.

      It runs in 61ms. (Internal runtime is 4.7ms).

      Run: [ @replit ]

      from enigma import (irange, inf, is_npalindrome, printf)
      
      # function to apply
      f = lambda x: x * (x + 1)
      
      # consider possible numbers
      vs = dict()
      for n in irange(0, inf):
        v = f(n)
        if v < 10000000: continue
        if v > 100000000: break
        # is the result a palindrome?
        if is_npalindrome(v):
          # have we recorded (n - 22)?
          v2 = vs.get(n - 22)
          if v2 is not None:
            # output solution
            printf("f({n}) = {v}; f({n2}) = {v2}", n2=n - 22)
          # record this value
          vs[n] = v
      

      Solution: The numbers are 5291, 5313.

      We have:

      5313 − 5291 = 22

      f(5291) = 27999972
      f(5313) = 28233282

      In fact these are the only two 8-digit palindromes that are the product of 2 consecutive integers.

      Like

      • Jim Randell's avatar

        Jim Randell 10:45 am on 5 March 2023 Permalink | Reply

        This puzzle was included in the book Microteasers (1986) by Victor Bryant.

        Here is the BBC BASIC program given in the text. It looks for palindromic 8-digit numbers that are the product of two consecutive integers, and prints them out. So it doesn’t look for a pair of numbers that differ by 22.

        >LIST
            5 REM Victor Bryant program for Teaser 1071
            6 REM from "Microteasers" (1986)
           10 REM We want N + N^2 to be an 8-digit palindrome
           15 start% = TIME
           20 N = 3000
           30 REPEAT
           40   N = N + 1
           50   M = N + N*N
           60   L = LEN(STR$(M))
           70   IF L <> 8 THEN 150
           80   REM We now work out the Dth digit from the right and the Dth digit from the left and see if they are the same
           90   D = 0
           91   REPEAT
           92     D = D + 1
          100     left$ = MID$(STR$(M), D, 1)
          110     right$ = MID$(STR$(M), 9 - D, 1)
          120   UNTIL D = 4 OR left$ <> right$
          130   REM if left$ = right$ when we get to this line then M = palindrome
          140   IF left$ = right$ THEN PRINT N, M
          150 UNTIL L > 8
          155 PRINT "Time = ";(TIME - start%) / 100;"s"
        >RUN
              5291  27999972
              5313  28233282
        Time = 322.32s
        >
        

        Like

      • Jim Randell's avatar

        Jim Randell 3:40 pm on 5 March 2023 Permalink | Reply

        A little bit of analysis can significantly reduce the number of cases tested:

        In order for n(n + 1) to be a palindrome it cannot be divisible by 10, so neither n nor (n + 1) can be divisible by 5 (as one of them will be even).

        And if (n + 22)(n + 23) is a palindrome, neither (n + 22) nor (n + 23) can be divisible by 5.

        So the residue of n mod 5 cannot be 0, 2, 3, 4. Hence it must be 1.

        So we only need to consider n values of the form (5k + 1).

        Also if n(n + 1) is an 8-digit palindrome, say ABCDDCBA then we have:

        n(n + 1) = 10000001.A + 1000010.B + 100100.C + 11000.D
        n(n + 1) = 11.(909091.A + 90910.B + 9100.C + 1000.D)

        So one of n, (n + 1) must be divisible by 11. (And also one of (n + 22), (n + 23) but that follows directly).

        So here is a program that only considers 248 candidate numbers, and has an internal runtime of 491µs.

        Run: [ @replit ]

        from enigma import (irange, inf, is_npalindrome, nsplit, printf)
        
        # function to apply
        f = lambda x: x * (x + 1)
        
        # consider possible numbers
        for n in irange(1, inf, step=5):
          if n % 11 not in {0, 10}: continue
          # apply the function
          v = f(n)
          if v < 10_000_000: continue
          v2 = f(n + 22)
          if v2 > 100_000_000: break
          # is the result a palindrome?
          if is_npalindrome(v) and is_npalindrome(v2):
            printf("f({n}) = {v}; f({n2}) = {v2}", n2=n + 2)
        

        In particular using this approach I was able to write a modified version of Victor’s BBC BASIC program that reduced the runtime from 5m22s (Victor’s original program) to 13.9s (my modified program).

        >LIST
           10 REM Modification of Victor Bryant program for Teaser 1071
           20 REM from "Microteasers" (1986)
           30 REM We want N + N^2 to be an 8-digit palindrome
           40 start% = TIME
           50 FOR N = 3161 TO 9999 STEP 5
           60   IF N MOD 11 <> 0 AND N MOD 11 <> 10 THEN 140
           70   P = N + N*N
           80   IF NOT FNpal(STR$(P)) THEN 140
           90   M = N + 22
          100   Q = M + M*M
          110   IF NOT FNpal(STR$(Q)) THEN 140
          120   PRINT N, P
          130   PRINT M, Q
          140 NEXT N
          150 PRINT "Time = ";(TIME - start%) / 100;"s"
          160 END
          170 REM check for 8 character palindrome
          180 DEF FNpal(M$)
          190   IF LEN(M$) <> 8 THEN =FALSE
          200   FOR D = 1 TO 4
          210     left$ = MID$(M$, D, 1)
          220     right$ = MID$(M$, 9 - D, 1)
          230     IF left$ <> right$ THEN =FALSE
          240   NEXT D
          250   =TRUE
        >RUN
              5291  27999972
              5313  28233282
        Time = 13.9s
        

        But the setter must have some other analysis in mind to reduce the number of calculations to less than 20.

        Like

    • GeoffR's avatar

      GeoffR 1:29 pm on 5 March 2023 Permalink | Reply

      
      # testing for (num + square(num)) between 10 million and 100 million approx.
      # LB = 3161 + 3161 ** 2 = 9,995,082
      # UB = 9999 + 9999 ** 2 = 99,990,000
      for n in range(3161, 10000):
        sum_prod = n + n ** 2
        # check if this number is a numerical palindrome
        if str(sum_prod) == str(sum_prod)[::-1]:
          # Second number is 22 more than first number
          n2 = n + 22
          sum_prod2 = n2 + n2 ** 2
          # check if this number is a numerical palindrome
          if str(sum_prod2) == str(sum_prod2)[::-1]:
            print(f"Pair of numbers are {n} and {n2}.")
            print(f"Associated Palindromes are {sum_prod} and {sum_prod2}.")
              
      # Pair of numbers are 5291 and 5313.
      # Associated Palindromes are 27999972 and 28233282.
      
      

      Like

    • GeoffR's avatar

      GeoffR 8:57 am on 6 March 2023 Permalink | Reply

      % A Solution in MiniZinc
      include "globals.mzn";
      
      % Pair of required numbers 
      var 3161..9999:N1;
      var 3161..9999:N2;
      
      % Palindrome digits for both palindromes
      var 1..9:A; var 0..9:B; var 0..9:C; var 0..9:D;
      var 1..9:G; var 0..9:H; var 0..9:I; var 0..9:J; 
       
      % Two eight digit palindromes
      var 10000000..99999999:ABCDDCBA = A * pow(10,7) + B * pow(10,6) + C * pow(10,5)
      + D * pow(10,4) + D * pow(10,3) + C * pow(10,2) + B * pow(10,1) + A;
      
      var 10000000..99999999: GHIJJIHG = G * pow(10,7) + H * pow(10,6) + I * pow(10,5)
      + J * pow(10,4) + J * pow(10,3) +  I * pow(10,2) + H * pow(10,1) + G;
      
      % Palindrome formation
      constraint N1 + N1 * N1 == ABCDDCBA;   % 1st 8-digit palindrome
      
      % Pair of numbers differ by twenty-two
      constraint N2 == N1 + 22;
      constraint N2 + N2 * N2 == GHIJJIHG;   % 2nd 8-digit palindrome
      
      solve satisfy;
      
      output ["Two required numbers are " ++ show(N1) ++ " and " ++ show(N2) 
      ++ "\n" ++ "Palindromes are " ++ show(ABCDDCBA) ++ " and " ++ show(GHIJJIHG)];
      
      % Two required numbers are 5291 and 5313
      % Palindromes are 27999972 and 28233282
      % ----------
      % ==========
      
      
      

      Like

    • John Crabtree's avatar

      John Crabtree 4:59 pm on 8 March 2023 Permalink | Reply

      f(N) = N(N+1) = “ABCDDCBA” = 0 (mod 11) as the sum of alternating digits is the same
      f(N+22) – f(N) = 44N + 506
      The first and last digits do not change. The second digit from the left, B, must either be the same or increase by 1.
      If it stays the same, 44N + 506 = 0 (mod 100) with no solution.
      If it increases by 1, 44N + 506 = 10 (mod 100) which leads to N = 16 mod 25

      41*42 = 66*67 = 22 (mod 100)
      sqrt(22) = 4.690 and sqrt(23) = 4.795
      4741 = 0 (mod 11), but f(4741+22) must start with 22 and is invalid
      4766 = 3 (mod 11)

      16*17 = 91*92 = 72 (mod 100)
      sqrt(27) = 5.196 and sqrt(28) = 5.291
      5191 = 10 (mod 11), but is too small
      5216 = 2 (mod 11)
      5291 = 0 mod 11, and f(5191+22) must start with 28

      And so the numbers must be 5291 and 5313.
      At this point the calculator is very useful to find that
      f(5291) = 27,999,922 and f(5313) = 28,233,282

      There are not many calculations in this solution. I do not understand the setter’s statement about needing as many calculations again to show that the solution is unique. That is included above.

      Like

      • John Crabtree's avatar

        John Crabtree 5:50 am on 10 March 2023 Permalink | Reply

        Simplifying, N = -34 + 275k = 10 (mod 11); or N = 66 + 275k = 0 (mod 11)
        With AB = 22 or 27, there are only two possible values of N:
        4741 = 4675 (17 * 275) + 66, but f(4741 + 22) is invalid
        5291 = 5225 (19 * 275) + 66, and f(5291 + 22) = f(5313) is valid

        Like

  • Unknown's avatar

    Jim Randell 4:17 pm on 3 March 2023 Permalink | Reply
    Tags:   

    Teaser 3154: Gill’s primes 

    From The Sunday Times, 5th March 2023 [link] [link]

    Jack told Gill: “I have found three equally-spaced prime numbers 29, 41, and 53. The difference between the first and second is the same as the difference between the second and third, and there are no repeated digits in the six digits of my primes”. Gill told Jack she had also found three equally-spaced primes, each having three digits and with no repeated digits in the nine digits of her primes. She said: “If I told you that the three-digit sum of each of my primes is an odd number then you should be able to find them”.

    In ascending order what are Gill’s three primes?

    [teaser3154]

     
    • Jim Randell's avatar

      Jim Randell 4:30 pm on 3 March 2023 Permalink | Reply

      I am assuming that “the three-digit sum of each of my primes is an odd number”, means that for each prime the sum of the three digits gives an odd number (as this does indeed eliminate a second candidate solution).

      We can use the [[ SubstitutedExpression ]] solver from the enigma.py library to solve this puzzle.

      The following run file executes in 121ms. (Internal runtime of the generated program is 8.3ms).

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      # suppose the primes are: ABC, DEF, GHI
      
      SubstitutedExpression
      
      # the numbers are prime
      "is_prime(ABC)"
      "is_prime(DEF)"
      "is_prime(GHI)"
      
      # the primes are ordered
      "A < D < G"
      
      # the primes form an arithmetic sequence
      "DEF - ABC == GHI - DEF"
      
      # each prime has an odd digit sum
      "(A + B + C) % 2 == 1"
      "(D + E + F) % 2 == 1"
      "(G + H + I) % 2 == 1"
      
      --template="ABC DEF GHI"
      --solution=""
      

      Solution: Gill’s primes are: 157, 283, 409.

      The common difference is 126, and each prime has a digit sum of 13.

      Without the stipulation that the digit sum of each prime is odd there would be a second solution, with the primes: 109, 283, 457.

      In this case the common difference is 174, and the digit sums are: 10, 13, 16.

      Like

    • GeoffR's avatar

      GeoffR 4:51 pm on 3 March 2023 Permalink | Reply

      
      % A Solution in MiniZinc
      include "globals.mzn";
      
      var 1..9:A; var 0..9:B; var 1..9:C; 
      var 1..9:D; var 0..9:E; var 1..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]);
      
      predicate is_prime(var int: x) = x > 1 
      /\ forall(i in 2..1 + ceil(sqrt(int2float(ub(x))))) 
      ((i < x) -> (x mod i > 0));
      
      var 100..999:ABC = 100*A + 10*B + C;
      var 100..999:DEF = 100*D + 10*E + F;
      var 100..999:GHI = 100*G + 10*H + I;
      
      constraint sum([is_prime(ABC), is_prime(DEF), is_prime(GHI)]) == 3;
      constraint ABC < DEF /\ DEF < GHI;
      constraint DEF - ABC == GHI - DEF;
      
      constraint sum([(A+B+C) mod 2 == 1, (D+E+F) mod 2 == 1,
      (G+H+I) mod 2 == 1]) == 3;
      
      solve satisfy;
      
      output ["Gill's three primes are " ++ show(ABC) ++ ", " 
      ++ show(DEF) ++ ", " ++ show(GHI)];
      
      

      Like

    • Hugo's avatar

      Hugo 11:33 am on 12 March 2023 Permalink | Reply

      Sum 849, common difference 126, again with no repeated digit between them.
      The previous member of the arithmetic progression, 31, is also prime, but it has only two digits and they’re both repeats of ones in the solution.

      “the three-digit sum of each of my primes” is indeed an odd way of expressing it.

      Like

    • Frits's avatar

      Frits 12:25 pm on 27 March 2023 Permalink | Reply

      Use primes ooo, eeo and eeo (o = odd and e = even).

         
      from enigma import SubstitutedExpression
      
      # invalid digit / symbol assignments
      d2i = dict()
      for d in range(0, 10):
        vs = set()
        if d == 0: vs.update('ADG')
        # A, B, C, F and I must be odd
        if d % 2 == 0: vs.update('ABCFI')
        else: vs.update('DEGH')
        
        if d == 8: vs.update('D')
        if d == 2: vs.update('G')
          
        d2i[d] = vs
      
      # the alphametic puzzle
      p = SubstitutedExpression(
        [
          # prime ooo
          "is_prime(ABC)",
          # prime eeo
          "is_prime(DEF)",
          "D < G",
          # missing digit must be 0 or 6 (as sum of all 9 digits is a multiple of 3)
          "len({D, E, G, H}.difference([2, 4, 8])) == 1",
          # prime eeo
          "is_prime(GHI)",
          
          # the primes form an arithmetic sequence
          "(srt := sorted([ABC, DEF, GHI]))[2] == 2 * srt[1] - srt[0]",
        ],
        answer="srt",
        d2i=d2i,
        distinct=("ABCFI", "DEGH"),
        #reorder=0,
        verbose=0,    # use 256 to see the generated code
      )
      
      # print answers
      for (_, ans) in p.solve():
        print(f"{ans}")
      

      Like

  • Unknown's avatar

    Jim Randell 11:28 am on 2 March 2023 Permalink | Reply
    Tags: by: J M Leonard   

    Brain-Teaser 259: [Musical tastes] 

    From The Sunday Times, 17th April 1966 [link]

    A survey of the musical tastes of 100 boys showed that the most popular group was one known as the Jonahs, three girls by the names of Jane, Jean and June.

    58 were fans of Jane, 45 fans of Jean, and 42 fans of June. Jane and Jean had 27 fans in common; Jane and June, 28; Jean and June 19; and 18 were fans of all three.

    How many of the boys were not fans of the group?

    This puzzle was originally published with no title.

    [teaser259]

     
    • Jim Randell's avatar

      Jim Randell 11:30 am on 2 March 2023 Permalink | Reply

      We can label the 8 areas of the corresponding Venn diagram according to who likes who:

      (Jane) = a
      (Jean) = b
      (June) = c
      (Jane + Jean) = d
      (Jean + June) = e
      (Jane + June) = f
      (Jane + Jean + June) = g
      () = x

      and we have:

      a + b + c + d + e + f + g + x = 100
      a + d + g + f = 58
      b + e + d + g = 45
      c + e + f + g = 42
      d + g = 27
      g + f = 28
      e + g = 19
      g = 18

      These equations are easily solved manually to give:

      g = 18
      e = 1
      f = 10
      d = 9
      a = 21
      b = 17
      c = 13
      x = 11

      Solution: 11 of the boys were not fans of any member of the group.


      Here is a program using the [[ Matrix() ]] class from the enigma.py library.

      Run: [ @replit ]

      from enigma import (Matrix, as_int, printf)
      
      # construct the equations
      eq = Matrix.equation("abcdefgx")
      eqs = [
        eq("abcdefgx", 100),
        eq("adgf", 58), eq("bedg", 45), eq("cefg", 42),
        eq("dg", 27), eq("gf", 28), eq("eg", 19),
        eq("g", 18),
      ]
      
      # solve the equations
      (a, b, c, d, e, f, g, x) = Matrix.linear(eqs, valid=(lambda n: as_int(n, "0+")))
      
      # output the solution
      printf("x={x} [a={a} b={b} c={c} d={d} e={e} f={f} g={g}]")
      

      Like

  • Unknown's avatar

    Jim Randell 9:18 am on 28 February 2023 Permalink | Reply
    Tags:   

    Teaser 2715: Colour coded 

    From The Sunday Times, 5th October 2014 [link] [link]

    Five lads cycle together daily. Each has a different number of helmets, less than a dozen, and none of them has two of the same colour. Each wears in daily rotation all the cycle helmets he possesses. On 1st September, Alan wore mauve, Bill and Charles wore red, Dave orange and Eric green. On 11th September, two were red, one green, one mauve and one white. On 19th September, Dave’s was orange, Eric’s green and the others red. Eric wore orange on the 22nd and white on the 23rd. On 1st October all five wore the same as on 1st September.

    In alphabetical order of the riders, what were the helmet colours on the 11th September?

    [teaser2715]

     
    • Jim Randell's avatar

      Jim Randell 9:18 am on 28 February 2023 Permalink | Reply

      I am assuming that each cyclist cycles(!) through their helmets in strict rotation. So each time through they wear the same colours in the same order.

      None of them have duplicate colours, so if we see one of them wearing the same colour on different days, they must have completed a whole number of cycles between those days. And they are all wearing the same colours on 1st September and 1st October, so the length of each of their cycles must be a divisor of 30, less than 12 (i.e. 1, 2, 3, 5, 6, 10).

      For any given cyclist, if we are given a set of days and colours then we can look at days when the same colour is worn. The gap between the days must be a multiple (possibly 1) of the cycle length. And so by considering all possible gaps we can can determine possible cycle lengths. Furthermore, different colours must be worn on different days in the cycle. So

      This Python program calculates possible cycle lengths from the colours given on the specified dates.

      It runs in 55ms. (Internal runtime is 2.2ms).

      Run: [ @replit ]

      from enigma import (
        subsets, group, unpack, union, tuples, mgcd, divisors,
        seq_all_different, delete, update, rev, map2str, printf
      )
      
      # find cycle lengths for values give in ms
      def solve(ms, ns=dict()):
        # are we done?
        if not ms:
          yield ns
        else:
          # choose a key to process (the one with the most entries)
          x = max(ms.keys(), key=(lambda k: len(ms[k].keys())))
          # group days by colour
          g = group(ms[x].items(), by=unpack(lambda k, v: v), f=unpack(lambda k, v: k))
          # collect multiples of cycle length
          s = union((abs(b - a) for (a, b) in tuples(vs, 2)) for vs in g.values())
          # consider possible cycle lengths (must be < 12)
          for n in divisors(mgcd(*s)):
            if n > 11: break
            # is this one already used?
            if n in ns: continue
            # check different colours give different cycle days
            if not seq_all_different(vs[0] % n for vs in g.values()): continue
            # solve for the remaining items
            yield from solve(delete(ms, [x]), update(ns, [(n, x)]))
      
      # consider possible orderings for 11th
      for (a, b, c, d, e) in subsets('RRGMW', size=len, select='mP'):
        # known positions (day 1 = 1st September)
        ms = dict(
          A={ 1: 'M', 11: a, 19: 'R', 31: 'M' },
          B={ 1: 'R', 11: b, 19: 'R', 31: 'R' },
          C={ 1: 'R', 11: c, 19: 'R', 31: 'R' },
          D={ 1: 'O', 11: d, 19: 'O', 31: 'O' },
          E={ 1: 'G', 11: e, 19: 'G', 22: 'O', 23: 'W', 31: 'G' },
        )
        # find possible cycle lengths
        for ns in solve(ms):
          # output solution
          printf("day 11: (A B C D E) = ({a} {b} {c} {d} {e}) {ns}", ns=map2str(rev(ns), sep=" ", enc="[]"))
      

      Solution: On 11th September, the colours were: Alan = mauve; Bill = red; Charles = red; Dave = green; Eric = white.

      Alan has 5 or 10 helmets. Bill has 1 or 2. Charles has 2 or 1. Dave has 3. Eric has 6.

      We can’t work out all the colours, but here is what we do know:

      A: (M ? ? ? ?) or (M ? ? ? ? ? ? ? ? ?)
      B: (R) or (R ?)
      C: (R ?) or (R)
      D: (O ? ?)
      E: (G ? ? O W ?)

      However these cycle lengths are chosen, they all give the same sequence of values for the 11th September.

      Like

  • Unknown's avatar

    Jim Randell 9:44 am on 26 February 2023 Permalink | Reply
    Tags:   

    Brain-Teaser 845: Hymn boards again 

    From The Sunday Times, 25th September 1977 [link] [link]

    I bade the Admiral good morning as I came out of church on Sunday morning, adding: “Did you notice anything odd about the hymns today?”.

    “Odd?”, he snorted; “they always sound odd to me, and they always will so long as Mrs. Haslet bellows her tuneless version at the back of my neck. A good enough woman in so many respects, but she doesn’t know a B-flat from a bull’s foot”.

    “No”, I said hastily, “I was referring to the mathematical rather than the musical aspect. There were, you will recall, three hymns all of three digits. Each digit appeared once only, and there were no zeros. The sums of the digits in all three hymns were equal and, what is more, the sum of the three hymn-numbers was a perfect square containing THREE digits. Very odd”.

    “Yes, I suppose so”, replied the Admiral, “… I do wish Mrs. Haslet would sit somewhere else”.

    What were the hymn numbers in ascending order (appropriately enough)?

    This puzzle is also included in the book The Sunday Times Book of Brain-Teasers: Book 2 (1981). Where it appears in the following form as Puzzle 50b:

    The Sunday before last the board also provided me with some mathematical interest. There were three hymns and again each digit from 1 to 9 inclusive was used. The sums of the digits in all three hymns were equal and, what is more, the sum of all three hymn numbers was a perfect square containing three digits.

    What were the hymn numbers on that Sunday?

    [teaser845]

     
    • Jim Randell's avatar

      Jim Randell 9:47 am on 26 February 2023 Permalink | Reply

      We can use the [[ SubstitutedExpression ]] solver from the enigma.py library to solve this puzzle.

      The following run file executes in 81ms. (Internal runtime of the generated program is 17ms).

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      --digits="1-9"
      
      # digit sums are all the same
      "A + B + C == D + E + F"
      "D + E + F == G + H + I"
      
      # sum of the numbers is a 3-digit perfect square
      "9 < is_square(ABC + DEF + GHI) < 32"
      
      # remove duplicate solutions
      "A < D < G"
      
      --answer="(ABC, DEF, GHI)"
      

      Solution: The hymn numbers were: 195, 267, 438.

      Like

    • GeoffR's avatar

      GeoffR 12:01 pm on 26 February 2023 Permalink | Reply

      % A Solution in MiniZinc
      include "globals.mzn";
      
      var 1..9:A; var 1..9:B; var 1..9:C; % Hymn ABC digits
      var 1..9:D; var 1..9:E; var 1..9:F; % Hymn DEF digits
      var 1..9:G; var 1..9:H; var 1..9:I; % Hymn GHI digits
      
      constraint all_different([A, B, C, D, E, F, G, H, I])
      /\ A < D /\ D < G;
      
      set of int: sq3 = { pow(x, 2) | x in 10..31 };
      
      constraint A + B + C == D + E + F /\ A + B + C == G + H + I
      /\ (100*A + 10*B + C)  +  (100*D + 10*E + F) + (100*G + 10*H + I) in sq3;
      
      solve satisfy;
      
      output ["Hymn Numbers were \(A)\(B)\(C)" ++  ", \(D)\(E)\(F)"
      ++  " and \(G)\(H)\(I)." ];
      
      % Hymn Numbers were 195, 267 and 438.
      % ----------
      % ==========
      
      
      

      Like

    • GeoffR's avatar

      GeoffR 9:09 am on 2 March 2023 Permalink | Reply

      
      from itertools import permutations
      from enigma import is_square
      
      for p1 in permutations(range(1, 10)):
          A, B, C, D, E, F, G, H, I = p1
          if A < D < G:  # order three hymn numbers
              if not(A + B + C == D + E + F):continue
              if not(A + B + C == G + H + I):continue
              total = 100 *(A + D + G) + 10 * (B + E + H) + (C + F + I)
              if is_square(total) and 100 < total < 1000:
                  ABC, DEF = 100*A + 10*B + C, 100*D + 10*E + F
                  GHI = 100*G + 10*H + I
                  print(f"Hymn Numbers were {ABC}, {DEF}, {GHI}")
      
      

      Like

    • Jim Randell's avatar

      Jim Randell 9:56 am on 2 March 2023 Permalink | Reply

      I thought it would be interesting to ask ChatGPT to write a Python program to solve this puzzle:

      Write a Python program to find a set of three 3-digit numbers, using each of the digits 1-9 exactly once between them. The sum of the digits in each of the three numbers is the same. The sum of the three numbers is a 3-digit perfect square. There is only one set of numbers that satisfies these conditions.

      And it produced this (without further prompting):

      import itertools
      
      # Generate all permutations of the digits 1-9
      perms = itertools.permutations(range(1, 10))
      
      # Loop through each permutation and check if it satisfies the conditions
      for perm in perms:
        # Split the permutation into three 3-digit numbers
        a, b, c = perm[:3], perm[3:6], perm[6:]
      
        # Check that each number contains no repeated digits
        if len(set(a)) == len(a) == len(set(b)) == len(b) == len(set(c)) == len(c):
          # Check that the sum of digits in each number is the same
          if sum(a) == sum(b) == sum(c):
            # Check that the sum of the three numbers is a 3-digit perfect square
            total = int("".join(map(str, a))) + int("".join(map(str, b))) + int("".join(map(str, c)))
            if len(str(total)) == 3 and int(total ** 0.5) ** 2 == total:
              # Print the solution and break out of the loop
              print(a, b, c)
              break
      

      It is hardly an elegant or efficient approach (and the check that each number contains no repeated digits is not necessary), but there is no arguing with the fact that it does correctly find the solution to the puzzle.

      I then tried to get it to improve the program. Firstly by removing the unnecessary check (which it did), and then by performing an exhaustive search in case there are further solutions while avoiding duplicate solutions. It took a bit of discussion to get it to implement this second improvement correctly, but here is the final program:

      import itertools
      
      # Join the digits in the given list of numbers into a single integer
      def join_and_convert(nums):
        return int(''.join(map(str, nums)))
      
      # Generate all permutations of the digits 1-9
      digits = range(1, 10)
      permutations = itertools.permutations(digits)
      
      # Find sets of three numbers with the same digit sum and sum to a perfect square
      solutions = set()
      for p in permutations:
        a, b, c = p[:3], p[3:6], p[6:]
        if sum(a) == sum(b) == sum(c):
          total = join_and_convert(a) + join_and_convert(b) + join_and_convert(c)
          if len(str(total)) == 3 and int(total ** 0.5) ** 2 == total:
            solution = tuple(sorted([a, b, c]))
            if solution not in solutions:
              solutions.add(solution)
              print(solution)
      

      Like

    • GeoffR's avatar

      GeoffR 12:09 pm on 2 March 2023 Permalink | Reply

      @Jim: Very Interesting experiment, both for the original program and the second solution. Would it be possible to ask ChatGPT to write a program in other languages?

      Like

      • Jim Randell's avatar

        Jim Randell 3:10 pm on 2 March 2023 Permalink | Reply

        @GeoffR: I don’t think ChatGPT has been specifically trained in Python (or indeed in programming). But as Python is quite a popular language amount of it was probably present in the data it was trained on.

        What ChatGPT is doing is attempting to provide a likely continuation of the conversation I started with my question. It has been trained on an extremely large data set, so it is able to give a plausible looking answer to a wide variety of questions.

        However it is very much a jack of all trades, master of none. If you have asked a vague question on a subject you are not too familiar the plausible sounding answer feels like it might be right. But if you ask a specific question on something you know a lot about you soon discover that it will confidently state an answer that is fundamentally flawed.

        When it confidently produces a program and claims that it solves the problem you gave it, what it is really saying is: “problems that look similar to the problem you set can be solved by programs that look similar to the program I have given”.

        Jim: Which weighs more, a pound of feathers or 2 pounds of bricks?

        ChatGPT: Both the pound of feathers and the 2 pounds of bricks weigh the same, which is 1 pound.

        The confusion may arise because when we hear “2 pounds of bricks”, our mind may immediately think of the number “2” and assume that it must weigh more than just “1”. However, the key here is to pay attention to the unit of measurement, which is pounds.

        In summary, a pound of feathers and 2 pounds of bricks weigh the same amount, which is 1 pound.

        And you can ask it to justify this incorrect position, and it will produce more paragraphs of plausible sounding rubbish. Sometimes the plausible sounding answer will be a correct response to a question, but it is able to bluff its way though a remarkably wide variety of subjects.

        I have tried asking it to solve a few puzzles before, and this is the first it has actually succeeded on. But you do need to understand the puzzle yourself (and if you have asked it to write a program, the programming language too), to be able to gauge how useful the plausible sounding response it produces is.

        Like

    • GeoffR's avatar

      GeoffR 5:06 pm on 2 March 2023 Permalink | Reply

      @Jim: Thanks for the detailed appraisal.

      I found a link stating that ChatGPT is written in Python and knows 12 programming languages:

      https://seo.ai/blog/how-many-languages-does-chatgpt-support#:~:text=ChatGPT%20is%20mainly%20focused%20on,JavaScript

      Like

c
Compose new post
j
Next post/Next comment
k
Previous post/Previous comment
r
Reply
e
Edit
o
Show/Hide comments
t
Go to top
l
Go to login
h
Show/Hide help
shift + esc
Cancel
Design a site like this with WordPress.com
Get started